Podejmij akcję i naucz się GitHub Actions!
Programiści uwielbiają automatyzować rzeczy. Ale automatyzacja jest korzystna tylko wtedy, gdy zabiera mniej czasu niż zwraca. Dzięki GitHub Actions może osiągniemy ten zysk.
Zaktualizowano: 14 czerwca 2023“Nigdy nie poświęcaj 6 minut na zrobienie czegoś ręcznie jeżeli możesz poświęcić 6 godzin na nieudane próby zautomatyzowania tego” - brzmi stary dowcip. Pomimo, że jako programiści mamy tendencję do nadmiernej komplikacji różnych zadań, istnieją oczywiste zalety automatyzacji.
Pomyśl o tym - to nie jest tak, że cały dzień poświęcasz na programowanie. Inne żmudne lub organizacyjne zadania również potrzebują naszej atencji: etykietowanie issue, aktualizowanie paczek czy wdrażanie kodu itd. Aaa i nie zapomnijmy o ulubieńcu wszystkich - spotkaniach.
W ciągu ostatniej dekady, programiści stworzyli wiele narzędzi i platform, takich jak CircleCI, Jenkins lub Travis (nie Scott), aby rozwiązać wymienione problemy. My jednak skupimy się na relatywnie nowym graczu w tej grze - GitHub Actions.
Nie kolejne narzędzie CI/CD?
Zanim nauczymy się czym są GitHub Actions, musimy dowiedzieć się czym nie są. “Akcje GitHub to narzędzie CI/CD.” Gdy robiłem research, wiele razy natrafiałem na takie nieporozumienia. I nie zrozum mnie źle - jest nim, ale nie musi być. Przypadek użycia jest szerszy. GitHub Actions to jest platforma do automatyzacji zadań używając zdefiniowanego workflow. CI lub CD jest jednym z wielu przykładów takiego przepływu pracy.
Dlaczego używać Github Actions?
Ok, ale dlaczego użyć ich zamiast innych, zaprawionych w boju rozwiązań? Istnieje kilka powodów.
- Nie wymagają zewnętrznych integracji. Spoglądając na statystyki, najprawdopodobniej i tak już używasz GitHuba.
- Konfiguracja jest prosta. Nie musisz być specjalistą DevOps, żeby ich używać (nie złośćcie się na mnie chłopaki - jesteście niezastąpieni przy bardziej złożonych rzeczach).
- Abstrahują niskopoziomowe komendy i inną logikę. Przypomnij sobie ile małych kroków musisz wykonać, aby uruchomić aplikację node od podstaw. Przypomniałeś sobie? Dobrze, teraz możesz zapomnieć je znowu, bo będą wyabstrahowane.
- Istnieje wiele integracji z rozmaitymi narzędziami i stosami technologicznymi. Java, .NET, Node.js? Wejdź na GitHub Marketplace i znajdź środowisko, którego potrzebujesz.
- Nie musisz zaczynać od zera. Wspomniany marketplace oferuje mnóstwo gotowych do użycia aplikacji i akcji. Możesz także tworzyć swoje niestandardowe akcje na bazie szablonów.
Czym jest GitHub workflow?
Na początek zapoznajmy się z podstawową terminologią. Będziemy się uczyć z góry na dół, zaczynając od ogólnych konceptów i przechodząc do detali.
Workflow to zautomatyzowany proces, który uruchamia jedną lub więcej prac (job). Możesz go skonfigurować dodając plik konfiguracyjny YAML w folderze .github/workflows
. Składni przyjrzymy się w następnych sekcjach.
Job to zestaw korków definiujący przepływ pracy. Kroki mogą uruchamiać komendy, konfigurować zadania lub wywoływać akcje GitHub. Domyślnie, prace działają równolegle, ale możesz skonfigurować je, aby uruchamiały się sekwencyjnie.
Action to pojedynczy krok wewnątrz pracy. Jest to aplikacja dla platformy GitHub Actions, która wykonuje często powtarzające się zadania. Możesz stworzyć swoje niestandardowe akcje lub znaleźć wiele gotowych do użycia na GitHub Marketplace.
Runner to serwer uruchamiający workflow w odpowiednim momencie. Każda praca w workflow jest uruchamiana w nowym wirtualnym środowisku. GitHub zarządza tymi serwerami i oferuje trzy główne systemy operacyjne: Ubuntu, Windows i macOS.
Jak działają GitHub Actions?
GitHub emituje zdarzenia (events), gdy coś się dzieje “w” lub “w stosunku do” twojego repozytorium - jak np. otworzenie PR lub issue. W odpowiedzi na te zdarzenia, możesz uruchamiać akcje GitHub. Te akcje to są również repozytoria. Ogólnie, przypomina to bazującą na zdarzeniach naturę JavaScriptu. Ogólna idea jest zatem prosta:
- Nasłuchuj na event
- Uruchom odpowiedni workflow
Składnia pliku konfigurującego workflow
Tak jak wspomniałem, potrzebujesz pliku YAML, żeby skonfigurować workflow. Przyjrzyjmy się strukturze takiego pliku.
name
- nazwa workflow. Strona z akcjami w repozytorium ją wyświetli.on
- nazwa zdarzenia, które wywołuje workflow, np.push
lubpull_request
. Istnieje cała lista zdarzeń na stronie GitHub events.jobs
- sekcja, gdzie umieszczasz pojedyncze prace.job-name
- nazwa zadania, którą możesz edytować. Pod tym kluczem, umieszczasz listę kroków. Każdy krok może:- Mieć nazwę -
name
. - Używać zdefiniowanej akcji -
uses
. - Uruchomić komendę w terminalu -
run
. - Być uruchomiony z dodatkowymi parametrami -
with
.
- Mieć nazwę -
CI pipeline w GitHub Actions
Ludzie najlepiej uczą się z przykładami (a przynajmniej ja tak mam), więc sprawdźmy przykładową konfigurację ciągłej integracji.
Konfiguracja GitHub Actions CIYAML
1name: CI2on:3 pull_request:4 branches:5 - main6 paths:7 - '**.js'8 - '**.jsx'9 - '**.json'10 - '**.yml'1112concurrency:13 group: ${{ github.ref }}14 cancel-in-progress: true1516jobs:17 install-cache:18 runs-on: ubuntu-latest19 strategy:20 matrix:21 node: [16]22 steps:23 - name: Checkout commit24 uses: actions/checkout@v325 - name: Use Node ${{ matrix.node }}26 uses: actions/setup-node@v327 with:28 node-version: ${{ matrix.node }}29 cache: npm30 - name: Install dependencies31 run: npm ci --legacy-peer-deps32 - name: Cache Cypress binary33 uses: actions/cache@v334 id: cache-cypress35 with:36 path: ~/.cache/Cypress37 key: cypress-binary-${{ hashFiles('**/package-lock.json') }}38 restore-keys: |39 cypress-binary-40 unit-test:41 runs-on: ubuntu-latest42 strategy:43 matrix:44 node: [16]45 needs: install-cache46 steps:47 - name: Checkout commit48 uses: actions/checkout@v349 - name: Use Node ${{ matrix.node }}50 uses: actions/setup-node@v351 with:52 node-version: ${{ matrix.node }}53 cache: npm54 - name: Install dependencies55 run: npm ci --legacy-peer-deps56 - name: Run unit tests57 run: npm test58 build:59 runs-on: ubuntu-latest60 strategy:61 matrix:62 node: [16]63 needs: unit-test64 steps:65 - name: Checkout commit66 uses: actions/checkout@v367 - name: Use Node ${{ matrix.node }}68 uses: actions/setup-node@v369 with:70 node-version: ${{ matrix.node }}71 cache: npm72 - name: Install dependencies73 run: npm ci --legacy-peer-deps74 - name: Run build75 run: npm run build76 - name: Upload build artifacts77 uses: actions/upload-artifact@v378 with:79 name: build-output80 path: |81 .cache82 public83 retention-days: 184 e2e-test-chrome:85 runs-on: ubuntu-latest86 strategy:87 matrix:88 node: [16]89 needs: build90 steps:91 - name: Checkout commit92 uses: actions/checkout@v393 - name: Use Node ${{ matrix.node }}94 uses: actions/setup-node@v395 with:96 node-version: ${{ matrix.node }}97 cache: npm98 - name: Install dependencies99 run: npm ci --legacy-peer-deps100 - name: Restore Cypress binary101 uses: actions/cache@v3102 id: cache-cypress103 with:104 path: ~/.cache/Cypress105 key: cypress-binary-${{ hashFiles('**/package-lock.json') }}106 restore-keys: |107 cypress-binary-108 - name: Download build artifacts109 uses: actions/download-artifact@v3110 with:111 name: build-output112 - name: Run Cypress113 uses: cypress-io/github-action@v4114 with:115 start: npm run serve116 wait-on: 'http://localhost:8000'117 browser: chrome118 install: false119 e2e-tests-firefox:120 runs-on: ubuntu-latest121 strategy:122 matrix:123 node: [16]124 needs: build125 steps:126 - name: Checkout commit127 uses: actions/checkout@v3128 - name: Use Node ${{ matrix.node }}129 uses: actions/setup-node@v3130 with:131 node-version: ${{ matrix.node }}132 cache: npm133 - name: Install dependencies134 run: npm ci --legacy-peer-deps135 - name: Restore Cypress binary136 uses: actions/cache@v3137 id: cache-cypress138 with:139 path: ~/.cache/Cypress140 key: cypress-binary-${{ hashFiles('**/package-lock.json') }}141 restore-keys: |142 cypress-binary-143 - name: Download build artifacts144 uses: actions/download-artifact@v3145 with:146 name: build-output147 - name: Run Cypress148 uses: cypress-io/github-action@v4149 with:150 start: npm run serve151 wait-on: 'http://localhost:8000'152 browser: firefox153 headed: true154 install: false
O panie, jaki długi plik! Ale nie bądź przytłoczony - spróbujemy go rozszyfrować. Nasz workflow nazywa się “CI”. Wystartuje, gdy ktoś zrobi PR do gałęzi main
. Dodatkowo, zmiany muszą obejmować pliki z wymienionymi rozszerzeniami. Dlaczego? Bo zmiany w plikach takich jak Markdown lub MDX najprawdopodobniej niczego nie zepsują, wiec nie musimy uruchamiać naszego worfklow.
Sekcja concurrency
pozwala na anulowanie zadań w trakcie pracy, jeżeli należą do tej samej grupy. Np. kontynuowanie procesu budowania projektu nie ma sensu, jeżeli testy nie przeszły.
Praca install-cache
rozpoczyna się typowo - od uzyskania dostępu do repozytorium. Do tego celu istnieje zdefiniowana funkcja, nazwana checkout
. Później musimy skonfigurować środowisko node. W naszym przykładzie użyjemy node 16, pobierając liczbę z sekcji matrix. Mając node możemy zainstalować paczki. Komenda npm ci
działa podobnie do npm install
, ale jest przeznaczona do zautomatyzowanych środowisk. W końcu sprawdzamy czy istnieją jakieś pliki binarne narzędzia Cypress. Pózniej się dowiemy po co.
Praca unit-test
, bez niespodzianek, uruchamia testy jednostkowe. Zaczyna się podobnie do poprzedniej pracy - od sprawdzenia plików w cache. Nie musimy instalować zależności z każdym uruchomieniem workflow. Ostatni krok rzeczywiście uruchamia testy.
Kolejna praca buduje projekt. Jest to przykład pracy sekwencyjnej. Ma ona zależność w postaci poprzedniego kroku. Po zbudowaniu projektu, akcja upload-artifact
zapisuje w pamięci wyjście tego procesu na jeden dzień.
Dwie ostatnie prace są bardzo podobne - obie uruchamiają testy end-to-end. Jedna z nich w Chrome, druga w Firefox. Te prace muszą zostać uruchomione po fazie budowania. Obie próbują znaleźć zapisane pliki narzędzia Cypress, dla zwiększenia wydajności. Po pomyślnym wykonaniu wszystkich prac, cały workflow jest zakończony.
Mam nadzieję, że ten post był niezłym wprowadzeniem do GitHub Actions. Po jego przeczytaniu powinieneś wiedzieć dlaczego i jak ich używać. Być może będziesz nawet w stanie zautomatyzować jakieś żmudne zadania w mniej niż 6 godzin. Być może będziemy kontynuowali naszą zautomatyzowaną przygodę i napiszemy jakąś niestandardową akcję GitHub. Ale, na razie, the best I can do to zaoferować przydatne linki.