Cake.Deploy.ScriptRunner

Ostatnio pisałam o podstawach Cake. Dzisiaj czas na nowy projekt wykorzystujący Cake do uruchamiania skryptów np. az-cli. Stworzyłam na GitHubie specjalne repozytorium, z którego możecie korzystać.

Jeszcze zanim zaczniemy, upewnijcie się, że macie zainstalowane Azure CLI oraz npm. Dodatkowo polecam też wtyczkę markdownlint do VSCode – przydaje się podczas pisania dokumentacji z użyciem składni markdown.

Struktura

Na początek opiszę strukturę repozytorium.

Starałam się odpowiednio pogrupować pliki dotyczące command-runners i commands (dla naszego przykładu commands-az.cake do uruchamiania skryptów az-cli).

Podstawowy plik, który będziemy uruchamiać, to deploy.ps1. Jest to powershellowy plik uruchamiający Cake. W jego logice jest zawarte pobieranie Cake i innych narzędzi z Nuget i uruchomienie skryptu deploy.cake z przekazaniem odpowiednich parametrów. Taki plik można wziąć z oficjalnego repozytorium Cake – należy tylko uważać na wersje. Aktualna wersja do uruchamiania Cake używa Invoke-Expression (końcówka pliku):

# Start Cake

Write-Host „Running build script…”
Invoke-Expression „& $CAKE_EXE_INVOCATION $($cakeArguments -join ” „)”
exit $LASTEXITCODE

Niestety ta wersja nie działała podczas przekazywania w parametrach zmiennych zawierających znaki specjalne. Żeby to naprawić można używać wcześniejszej wersji skryptu:

# Start Cake

Write-Host „Running build script…”
&$CAKE_EXE $cakeArguments
exit $LASTEXITCODE

Główny plik, w którym będziemy pisać logikę, to deploy.cake. To w nim tworzymy na starcie (w metodzie Setup()) nowe instancje CommandRunner dla az-cli oraz PowershellCommanRunner dla powershella. W tym pliku definiujemy też główne zadania (Task()), które będziemy uruchamiać. Domyślnie będzie zawsze brany Task(„Default-Az-Show-Version”), ale używając odpowiednich parametrów, można zmienić wywoływany task.

Popatrzmy na powershell-command-runner.cake, po którym dziedziczą wszystkie inne pliku runner.

public class PowershellCommandRunner
{
    protected ICakeContext context;
    protected bool globalExceptionOnError;

    public PowershellCommandRunner(ICakeContext context,
                                   bool globalExceptionOnError = true)
    {
        this.context = context;
        this.globalExceptionOnError = globalExceptionOnError;
    }
    //...
}

W konstruktorze przekazujemy context, a także ustawiamy globalExceptionOnError (domyślnie na true). W skrócie – jest potrzebna taka „furtka”, żeby pozwolić skryptowi działać dalej pomimo warningu. Niestety az-cli czasami wyświetla warning pomimo sukcesu operacji. W domyślnym ustawieniu nasz skrypt się przerwie z powodu błędu. Dlatego dla konkretnych operacji można ustawić powyższą flagę na false, wtedy „połykamy błąd” i skrypt leci dalej. Oczywiście są również mankamenty takiego rozwiązania – gdy operacja się naprawdę wysypie, nasz skrypt będzie dalej wykonywał kolejne kroki.

Widzimy, że mamy 3 metody:

  • Run<T>() pozwala na wywołanie skryptu i zdeserializowanie outputu jako konkretnego obiektu o typie T.
  • Run() pozwala wywołać skrypt, bez zwracania outputu.
  • RunCommand() zawiera już bezpośrednio logikę związaną z uruchomieniem skryptu Powershell z naszą komendą. W powyższym przykładzie mogłaby być prywatna.

Przypatrzmy się Command-Runner.cake.

public class CommandRunner : PowershellCommandRunner 
{
    private string toolName;

    public CommandRunner(ICakeContext context,
                         string toolName,
                         bool globalExceptionOnError = true)
        : base(context, globalExceptionOnError)
    {
        this.toolName = toolName;
    }
    //...
}

Dziedziczy on po PowershellCommandRunner. W konstruktorze przyjmuje 3 parametry: context, toolName i globalExceptionOnError ustawiony na true. Najważniejszy parametr to toolName – jest to odpowiednik nazwy narzędzia, którą chcemy wywołać. Dla az-cli jest to po prostu „az” (bo używamy go jako „az login”, gdzie „login” jest już konkretną komendą).

Dodatkowo utworzyłam również npm-command-runner.cake. Pozwala on na uruchamianie skryptów zainstalowanych przez npm.

public class NpmCommandRunner: CommandRunner
{
    public NpmCommandRunner(ICakeContext context,
                            string commandName,
                            bool globalExceptionOnError = false)
        : base(context,
               context.MakeAbsolute(new FilePath($"./node_modules/.bin/{commandName}.cmd")).FullPath,
               globalExceptionOnError)
    {
    }
}

Potrzebujemy przekazać mu w kontruktorze commandName, który będzie szukał odpowiedniego pliku {commandName}.cmd w folderze node_modules/.bin/.

W folderze commands możemy dodawać pliki command dla różnych narzędzi. W naszym przykładzie mamy tylko jeden plik commands-az.cake. Mamy w nim 3 taski:

Task("Az-Show-Version")
    .Does(() => {
        az.Run(a => {
                a.Append("version");
            });
    });

Task("Login")
    .Does(() => {
        //exceptionOnError: false because of default warning
        //"WARNING: You have logged in. Now let us find all
        //the subscriptions to which you have access..."
        az.Run(a => {
                a.Append("login");
            }, exceptionOnError: false);
    });

Task("Set-Subscription")
    .Does(() => {
        az.Run(a => {
                a.Append("account");
                a.Append("set");
                a.AppendSwitchQuoted("--subscription",
                                     ReleaseVariable("subscription"));
            });
    });
  • „Az-Show-Version” – do wyświetlania wersji az
  • „Login” – do logowania się
  • „Set-Subscription” – do ustawiania subskrypcji (po zalogowaniu)

Jak widać w ostatnim tasku, potrzebujemy pobrać zmienną subscription korzystając z ReleaseVariable.

ReleaseVariable

W celu definiowania zmiennych korzystam z paczki Cake.Deploy.Variables. Dzięki niej definiuję odrębne pliki ze zmiennymi dla różnych środowisk. W projekcie mamy domyślny plik variables-default.cake:

// example of basic variable file
ReleaseEnvironment("default")
    .AddVariable("subscription",
                 x => Argument<string>("subscription"));

oraz dodatkowy plik dla środowiska dev variables-dev.cake:

// example of new variable file for other environment
ReleaseEnvironment("dev")
    .IsBasedOn("default")
    .AddVariable("env", "dev");

Uruchamianie

Jak uruchomić skrypty? Wystarczy uruchomiść skrypt powershell

./deploy.ps1

Wywoła on domyślny task Az-Show-Version. Jeśli chcemy wybrać inny task, wystarczy go podać w parametrze:

./deploy.ps1 -Target Az-Login

A jeśli chcemy wywołać task z przekazaniem dodatkowych zmiennych, możemy to zapisać tak:

./deploy.ps1 --env="dev" --subscription= '*****' -Target Az-Login-With-Set-Subscription

PS

Niedługo po napisaniu tego posta dowiedziałam się, że powstało coś takiego jak Cake.AzureCLI (z tego posta). Rozszerzenie do Cake, pozwalające bezpośrednio używać azure-cli w Cake. Ma swoje plusy i minusy, ale jeśli chcecie korzystać tylko z azure-cli i nie przeszkadza Wam uzależnienie od konkretnej jego wersji, to może warto rozważyć 🙂


Post powstał pod patronatem firmy, w której aktualnie pracuję: Objectivity.

Grafikę tytułową zaprojektował niezastąpiony zespół designu!


Podoba Ci się to, co tworzę? Chcesz dostawać informacje o:
– wydarzeniach, które organizuję lub wspieram (np. konferencje, meetupy, webinary)
– inicjatywach, które organizuję lub wspieram (np. GeekWeekWro, DevAdventCalendar)
– moich prelekcjach, kursach i szkoleniach
– wyróżnionych artykułach z mojego bloga

0% SPAMu, 100% informacji! Krótko i na temat.

2 uwagi do wpisu “Cake.Deploy.ScriptRunner

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s