Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

arkadiusz.wasniewski

Hook scripts w PowerShell

Dawno, dawno temu (choć może nie aż tak dawno) popełniłem notkę na temat skryptów przechwytujących (hook scripts) dla repozytoriów systemu kontroli wersji Subversion. Chodziło o uniemożliwienie zapisania w repozytorium zmian, jeśli nie został podany do nich żaden komentarz wyjaśniający. Proponowany kod wyglądał mniej więcej tak:

        private static int Main(string[] args)

        {

            string repositoryPath = args[0];

            string transactionName = args[1];

 

            var process = new Process();

            process.StartInfo.FileName = "svnlook.exe";

            process.StartInfo.Arguments = string.Format("log -t {0} {1}",

                transactionName, repositoryPath);

            process.StartInfo.UseShellExecute = false;

            process.StartInfo.RedirectStandardOutput = true;

            process.StartInfo.CreateNoWindow = true;

            process.Start();

            string output = process.StandardOutput.ReadToEnd();

            process.WaitForExit();

 

            var regex = new Regex("[a-zA-Z0-9]");

            if (!regex.IsMatch(output)){

                Console.Error.WriteLine("Brak opisu poczynionych zmian.");

                return 1;

            }

 

            return 0;

        }

Ostatnio pomyślałem czemu by nie wykorzystać do wspomożenia Subversion języka PowerShell. Wykoncypowałem, iż w katalogu, w którym są składowane repozytoria założę folder scripts, który będzie zawierał skrypty PowerShell, a do którego będą sięgały pliki wsadowe (batch files) wywoływane przez SVN w katalogu hooks danego repozytorium.

Pierwsza wersja skryptu sprawdzającego istnienie komentarza do zachowywanych zmian była następująca:

        $repositoryPath = $args[0]
        $transactionName = $args[1]
        
        $message = svnlook.exe log -t $transactionName $repositoryPath
        if($message -notmatch '[a-zA-Z0-9]'){
            Write-Error "Brak opisu poczynionych zmian."
            return 1
        }

Okazało się jednak, iż nie funkcjonuje on w zadowalający sposób. Po pierwsze niepoprawnie był zwracany do systemu status zakończenia skryptu (dos exit code). Rozwiązań znalezionych po poszukiwaniach w sieci było kilka. Ale najbardziej sensowne, w mojej opinii oczywiście, polegało na skorzystaniu ze specjalnej zmiennej PowerShell $host. Drugi problem wiązał się z komunikatem zwrotnym, który chciałem przekazać użytkownikowi próbującemu zatwierdzić zmiany. Aby wiadomość mogła być wyświetlona musi ona zostać wysłana do standardowego strumienia zawierającego błędy. Niestety okazało się, iż skorzystanie z polecenia Write-Error jest niemal tożsame z wyrzuceniem w tym miejscu wyjątku throw. Użytkownik, poza komunikatem zwrotnym, ze skryptu otrzymywał również dodatkowe informacje na temat kategorii błędów, miejsca wystąpienia etc. Niedobrze. Na szczęście PowerShell będąc opartym o platformę .NET pozwala bez problemów korzystać z jej bibliotek. Wiadomość dla użytkownika wysłałem więc tak jak w kodzie C# do standardowego strumienia błędów System.Console.Error. A oto końcowa wersja skryptu:

        $repositoryPath = $args[0]
        $transactionName = $args[1]
        
        $message = svnlook.exe log -t $transactionName $repositoryPath
        if($message -notmatch '[a-zA-Z0-9]'){   
            [System.Console]::Error.WriteLine("Brak opisu poczynionych zmian.")
            $host.SetShouldExit(1)
        }

Jak już delikatnie powyżej zasugerowałem, skrypt PowerShell nie może być niestety wywołany bezpośrednio przez Subversion. Do tego konieczny jest pośredni plik wsadowy (batch file). I tutaj również nie obyło się bez niespodzianek. Pierwsza wersja pliku prezentowała się tak:

        powershell -noprofile ..\..\scripts\pre-commit.ps1 %1 %2

        exit errorlevel

Okazało się jednak, iż ścieżka dostępu do katalogu ze skryptami nie jest tworzona poczynając od miejsca wywołania pliku wsadowego, tylko od ścieżki C:\Windows\system32. W ramach plików wsadowych można skorzystać ze zmiennej %CD%, która zawiera aktualny katalog. W tym wypadku nic to jednak nie dało. Nie pomogło nawet zapisanie wartości %CD% w zmiennej tymczasowej skryptu i późniejsze wykorzystanie jej przy tworzeniu ścieżki. Rozwiązaniem okazało się natomiast skorzystanie z makra %~dp0. Ostatecznie zawartość pliku wsadowego ustabilizowała się na poniższym zapisie:

        powershell -noprofile  %~dp0\..\..\scripts\pre-commit.ps1 %1 %2

        exit errorlevel

Co ciekawe zupełnie nie sprawdziło się również polecane również sprawdzanie zmiennej $LASTEXITCODE zamiast ERRORLEVEL.

Wiemy już, iż przedstawiane rozwiązanie posiada jedną niedogodność – konieczne jest posiłkowanie się plikami wsadowymi aby osiągnąć zamierzony efekt. Czy można to pominąć? Ano można. Należy skorzystać z parametru –Command w czasie wywołania powłoki PowerShell w pliku wsadowym i zapisać wewnątrz ciągu “& “ cały kod skryptu pamiętając, aby wszystko znalazło się w jednej linii oraz, by kolejne bloki kodu oddzielone były znakiem średnika.

        powershell.exe -noprofile -command "& {$message = svnlook.exe log -t $args[1] $args[0];if($message -notmatch '[a-zA-Z0-9]'){[System.Console]::Error.WriteLine('Brak opisu poczynionych zmian.');$host.SetShouldExit(1)}}" %1 %2

        exit errorlevel

Dla skrócenia zapisu parametry skryptu przekazuję bezpośrednio do aplikacji svnlook.exe bez tworzenia zmiennych pomocniczych $repositoryPath i $transactionName.

Podsumowanie? Cóż. Skrypty przechwytujące (hook scripts) w PowerShell to chyba jednak w tym przypadku sztuka dla sztuki. Szybciej i sprawniej będzie skorzystanie z kodu C#. Choć zawsze można się czegoś pożytecznego przy okazji nauczyć.

Opublikowane 22 maja 2009 21:32 przez arkadiusz.wasniewski
Filed under: , ,

Komentarze:

 

dotnetomaniak.pl said:

Dziękujemy za publikację - Trackback z dotnetomaniak.pl

maja 22, 2009 22:50
 

Maciek said:

Ciekawe podejście, aczkolwiek zgadzam się, że chyba lepiej skorzystać z "kodu C#" lub z gotowych bibliotek, które stanowią nakładkę na svn w C#. Ja aktualnie korzystam z Captain Hook (http://sourceforge.net/projects/captainhook/), dzięki temu (są tam gotowe pluginy) można zrobić zabezpieczenia przed brakiem komentarza, wysyłanie maila po commit, czy integracje z systemem śledzenia problemów z oprogramowaniem. Jest też dostępna jeszcze jedna biblioteka SharpSvn (http://sharpsvn.open.collab.net/) na pierwszy rzut oka wygląda interesująco, ale jeszcze jej nie próbowałem.

Pozdrawiam.

maja 23, 2009 09:30
 

arkadiusz.wasniewski said:

Dzięki za linki.

Programem Captain Hook interesowałem się baaardzo dawno temu. Zaraz na samym początku tego projektu chyba. Może warto zobaczyć co się zmieniło. A Ty Maćku co wykorzystujesz w ramach skryptów przechwytujących?

Arek

maja 24, 2009 23:49
 

Maciej Zbrzezny said:

Witam ponownie,

Przepraszam za chwilę zwłoki. Ja korzystam właśnie z Captain Hook, w ramach tego wykorzystuję gotowce (wchodzące w skład tego projektu), sprawdzające czy komentarz został wpisany i wysyłające email do wybrancyh osób po commit'cie, dodatkowo udało mi się dopisać własny plugin, który zintegrował mi SVN'a z BugNet (http://www.bugnetproject.com/ - dość przyjaznym narzędziem do śledzenia problemów. W wyniku integracji komentarz wpisany podczas Commit, wraz z listą zmienionych ścieżek automatycznie trafia do BugNET. Więc w zasadzie polecam. Działa u mnie już od ponad dwóch lat i w zasadzie nie trzeba się do tego dotykać.

Co do tego drugiego narzędzia, to na razie czytałem o tym i chyba ma trochę większe możliwości, ale nie znalazłem czasu i chęci by wziąć się za to poważniej.

A możesz mi powiedzieć do czego Ty wykorzystujesz hook scripts? może znasz jakieś inne jeszcze ciekawe wykorzystanie, poza wymienionymi przeze mnie?

Pozdrawiam,

Maciek

maja 29, 2009 21:56
 

arkadiusz.wasniewski said:

Elo elo

Hook scripts wykorzystuję obecnie tylko do sprawdzania czy został wpisany komentarz  do zmian.

Arek

czerwca 1, 2009 11:53
Komentarze anonimowe wyłączone
W oparciu o Community Server (Personal Edition), Telligent Systems