ProHoster > Blog > administracja > Kręgi piekła z akcjami GitHub (budowa potoku CI/CD dla projektu Java)
Kręgi piekła z akcjami GitHub (budowa potoku CI/CD dla projektu Java)
Często muszę budować potok do tworzenia projektów w Javie. Czasem jest to oprogramowanie typu open source, czasem nie. Niedawno zdecydowałem się spróbować przenieść niektóre moje repozytoria z Travis-CI i TeamCity do GitHub Actions i oto co z tego wyszło.
Co zautomatyzujemy?
Na początek potrzebujemy projektu, który zautomatyzujemy, zróbmy małą aplikację w Spring boot / Java 11 / Maven. Na potrzeby tego artykułu nie będziemy w ogóle interesować się logiką aplikacji, ważna jest dla nas infrastruktura wokół aplikacji, dlatego wystarczy nam prosty kontroler REST API.
Źródła możesz zobaczyć tutaj: github.com/antkorwin/github-actions Wszystkie etapy tworzenia potoku są odzwierciedlone w żądaniach ściągnięcia dla tego projektu.
JIRA i planowanie
Warto dodać, że zazwyczaj używamy JIRA jako narzędzia do śledzenia problemów, dlatego utwórzmy osobną tablicę dla tego projektu i tam dodajmy pierwsze zgłoszenia:
Nieco później powrócimy do tego, jakie ciekawe rzeczy mogą zaoferować JIRA i GitHub w połączeniu.
Automatyzujemy montaż projektu
Nasz projekt testowy jest tworzony za pomocą mavena, więc jego zbudowanie jest dość proste, potrzebujemy tylko pakietu mvn clean.
Aby to zrobić za pomocą Github Actions, będziemy musieli utworzyć w repozytorium plik opisujący nasz przepływ pracy, można to zrobić za pomocą zwykłego pliku yml, nie mogę powiedzieć, że lubię „programowanie yml”, ale co możemy zrobić - robimy to w katalogu .github/ flow/ plik build.yml w którym opiszemy działania podczas budowania gałęzi master:
on — to opis wydarzenia, na którym zostanie uruchomiony nasz skrypt.
na: pull_request/push — wskazuje, że ten przepływ pracy musi być uruchamiany za każdym razem, gdy wysyłane jest polecenie push do urządzenia głównego i tworzone są żądania ściągnięcia.
Poniżej znajduje się opis zadań (Oferty pracy) i kroki wykonania (kroki) dla każdego zadania.
działa - tutaj możemy wybrać docelowy system operacyjny, o dziwo można nawet wybrać Mac OS, ale na prywatnych repozytoriach jest to dość drogie (w porównaniu z Linuksem).
zastosowania pozwala na ponowne wykorzystanie innych akcji, np. za pomocą akcji action/setup-java instalujemy środowisko dla Javy 11.
Za pomocą w możemy określić parametry z jakimi uruchamiamy akcję, zasadniczo są to argumenty, które zostaną przekazane do akcji.
Pozostaje tylko uruchomić kompilację projektu w Maven: run: mvn -B clean package flaga -B mówi, że potrzebujemy trybu nieinteraktywnego, aby maven nagle nie chciał nas o coś pytać
Świetnie! Teraz za każdym razem, gdy zatwierdzisz projekt główny, rozpocznie się kompilacja projektu.
Automatyzacja uruchamiania testów
Montaż jest dobry, ale w rzeczywistości projekt można złożyć bezpiecznie, ale nie działać. Dlatego kolejnym krokiem jest automatyzacja przebiegów testowych. Ponadto całkiem wygodnie jest spojrzeć na wyniki przejścia testów podczas przeglądu PR - wiesz na pewno, że testy wyszły pomyślnie i nikt nie zapomniał uruchomić swojego oddziału przed dokonaniem fuzji.
Podczas tworzenia pull requesta przeprowadzimy testy i połączymy się z masterem, a jednocześnie dodamy utworzenie raportu o pokryciu kodu.
Do testów używam codecov w połączeniu z wtyczką jacoco. codecov ma własną akcję, ale potrzebuje tokena, aby działać z naszym żądaniem ściągnięcia:
${{ secrets.CODECOV_TOKEN }} — tę konstrukcję zobaczymy jeszcze nie raz, secrets to mechanizm przechowywania sekretów w GitHubie, możemy tam zapisywać hasła/tokeny/hosty/adresy URL i inne dane, które nie powinny znajdować się w bazie kodu repozytorium.
Możesz dodać zmienną do sekretów w ustawieniach repozytorium na GitHubie:
Token możesz zdobyć pod adresem codecov.io Aby dodać projekt publiczny, po autoryzacji za pośrednictwem GitHuba, wystarczy kliknąć poniższy link: Nazwa użytkownika GitHuba/[nazwa repozytorium]. Można także dodać prywatne repozytorium, w tym celu należy nadać aplikacji uprawnienia codecov w Githubie.
Teraz bot codecov wejdzie w każde nasze żądanie ściągnięcia i doda wykres zmiany zasięgu:
Dodajmy analizator statyczny
W większości moich projektów open source wykorzystuję chmurę sonarową do analizy kodu statycznego, dość łatwo jest połączyć się z travis-ci. Logicznym krokiem jest więc migracja do GitHub Actions, aby zrobić to samo. Rynek akcji to fajna sprawa, ale tym razem trochę mnie zawiodła, bo z przyzwyczajenia znalazłem akcję, której potrzebowałem i dodałem ją do przepływu pracy. Okazało się jednak, że sonar nie obsługuje pracy poprzez akcję analizy projektów na Maven lub Gradle. Oczywiście jest to napisane w dokumentacji, ale kto to czyta?!
Nie jest to możliwe poprzez akcję, więc zrobimy to poprzez wtyczkę mvn:
SONAR_TOKEN - można uzyskać pod adresem sonarcloud.io i musisz to zarejestrować w tajemnicy. GITHUB_TOKEN - jest to wbudowany token, który generuje GitHub, za pomocą którego sonarcloud[bot] będzie mógł logować się do Gita, aby zostawiać nam wiadomości w pull requestach.
Klucz projektu Dsonar — nazwa projektu w sonarze, możesz ją zobaczyć w ustawieniach projektu.
Organizacja Dsonar — nazwa organizacji z GitHuba.
Wykonujemy żądanie ściągnięcia i czekamy, aż sonarcloud[bot] pojawi się w komentarzach:
Zarządzanie wersjami
Kompilacja została skonfigurowana, testy zostały przeprowadzone i możemy opublikować wersję. Przyjrzyjmy się, jak GitHub Actions może znacznie ułatwić zarządzanie wydaniami.
W pracy mam projekty, których baza kodu jest w bitbuckecie (wszystko jest jak w tej historii „w ciągu dnia piszę do bitbucketa, w nocy wchodzę do GitHuba”). Niestety, Bitbucket nie ma wbudowanych narzędzi do zarządzania wersjami. Jest to problem, ponieważ dla każdego wydania trzeba ręcznie utworzyć stronę w konfluencji i wrzucić tam wszystkie funkcje zawarte w wydaniu, przeszukać pałace umysłu, zadania w Jirze, zatwierdzenia w repozytorium. Istnieje wiele szans na popełnienie błędu, możesz o czymś zapomnieć lub wprowadzić coś, co zostało już wydane ostatnim razem, czasami po prostu nie jest jasne, jak sklasyfikować żądanie ściągnięcia - czy jest to funkcja, czy poprawka błędu, czy testy edycyjne, czy coś infrastrukturalnego.
W jaki sposób działania GitHuba mogą nam pomóc? Jest tam świetna akcja - release drafter, pozwala ustawić szablon pliku uwag do wydania, aby skonfigurować kategorie żądań ściągnięcia i automatycznie grupować je w pliku uwag do wydania:
Wszystkie żądania ściągnięcia od tej chwili będą automatycznie gromadzone w informacjach o wydaniu – magia!
Tutaj może pojawić się pytanie: co się stanie, jeśli programiści zapomną umieścić tagi w PR? Wtedy nie jest jasne, do której kategorii go umieścić i znowu będziesz musiał sobie z tym poradzić ręcznie, z każdym PR osobno. Aby rozwiązać ten problem, możemy zastosować inną akcję - weryfikator etykiet - sprawdza on obecność tagów w żądaniu ściągnięcia. Jeśli nie ma wymaganych tagów, sprawdzenie zakończy się niepowodzeniem i w naszym żądaniu ściągnięcia pojawi się komunikat.
Teraz każde żądanie ściągnięcia musi być oznaczone jednym z tagów: type:fix, type:features, type:documentation, type:tests, type:config.
Automatyczna adnotacja żądań ściągnięcia
Skoro poruszyliśmy już taki temat jak efektywna praca z pull requestami, warto wspomnieć o takiej akcji jak labeler, umieszczający w PR tagi na podstawie tego, które pliki zostały zmienione. Na przykład możemy oznaczyć jako [build] dowolne żądanie ściągnięcia zawierające zmiany w katalogu .github/workflow.
Nie udało mi się połączyć akcji automatycznie umieszczającej etykiety w żądaniach ściągnięcia z akcją sprawdzającą obecność wymaganych etykiet; match-label nie chce widzieć etykiet dodanych przez bota. Łatwiej wydaje się napisanie własnego działania, które łączy oba etapy. Ale nawet w tej formie jest to dość wygodne w użyciu, podczas tworzenia żądania ściągnięcia musisz wybrać etykietę z listy.
Czas wdrożyć
Wypróbowałem kilka opcji wdrażania za pośrednictwem GitHub Actions (przez ssh, przez scp i używając docker-hub) i mogę powiedzieć, że najprawdopodobniej znajdziesz sposób na przesłanie pliku binarnego na serwer, niezależnie od tego, jak krzywy jest twój potok Jest.
Podobała mi się opcja utrzymania całej infrastruktury w jednym miejscu, więc przyjrzyjmy się, jak wdrożyć pakiety na GitHubie (jest to repozytorium treści binarnych, npm, jar, docker).
Skrypt do budowania obrazu dokera i publikowania go w pakietach GitHub:
Na początek musimy zbudować plik JAR naszej aplikacji, po czym wyliczamy ścieżkę do rejestru dockerów GitHub oraz nazwę naszego obrazu. Jest tu kilka trików, na które jeszcze nie natrafiliśmy:
konstrukcja typu: echo „::set-output name=NAZWA::WARTOŚĆ” pozwala ustawić wartość zmiennej w bieżącym kroku, tak aby można ją było następnie odczytać we wszystkich pozostałych krokach.
wartość zmiennej ustawioną w poprzednim kroku możesz uzyskać poprzez identyfikator tego kroku: ${{steps.global_env.outputs.DOCKERHUB_IMAGE_NAME }}
Standardowa zmienna GITHUB_REPOSITORY przechowuje nazwę repozytorium i jego właściciela („właściciel/nazwa repozytorium”). Aby wyciąć wszystko z tej linii oprócz nazwy repozytorium, użyjemy składni basha: ${GITHUB_REPOSITORY#*/}
Do wskazania wersji obrazka używamy pierwszych cyfr z hasha SHA zatwierdzenia - GITHUB_SHA tutaj też są niuanse, jeśli robisz takie kompilacje nie tylko przy łączeniu do mastera, ale także zgodnie z utworzeniem pull requestu zdarzenie, wówczas SHA może nie pasować do skrótu, który widzimy w historii git, ponieważ akcja/akcja kasowania tworzy swój własny, unikalny skrót, aby uniknąć zakleszczenia działań w PR.
Jeśli wszystko poszło dobrze, to otwierając sekcję pakietów (https://github.com/antkorwin/github-actions/packages) w repozytorium, zobaczysz nowy obraz okna dokowanego:
Można tam również zobaczyć listę wersji obrazu okna dokowanego.
Pozostaje tylko skonfigurować nasz serwer do pracy z tym rejestrem i zrestartować usługę. Prawdopodobnie porozmawiam o tym, jak to zrobić poprzez systemd innym razem.
Monitorowanie
Przyjrzyjmy się prostej opcji sprawdzania stanu naszej aplikacji za pomocą akcji GitHub. Nasza aplikacja startowa posiada aktuator, więc nie musimy nawet pisać API, żeby sprawdzić jej status, dla leniwych zrobiliśmy już wszystko. Wystarczy wyciągnąć hosta: SERVER-URL:PORT/actuator/health
jobs:
ping:
runs-on: ubuntu-18.04
steps:
- name: curl actuator
id: ping
run: |
echo "::set-output name=status::$(curl ${{secrets.SERVER_HOST}}/api/actuator/health)"
- name: health check
run: |
if [[ ${{ steps.ping.outputs.status }} != *"UP"* ]]; then
echo "health check is failed"
exit 1
fi
echo "It's OK"
Najpierw zapisujemy do zmiennej to, co serwer odpowiedział na żądanie, w kolejnym kroku sprawdzamy, czy status jest UP i jeśli tak nie jest, to wychodzimy z błędem. Jeśli chcesz „przytłoczyć” akcję rękami, to tak wyjście 1 - odpowiednia broń.
- name: send alert in telegram
if: ${{ failure() }}
uses: appleboy/telegram-action@master
with:
to: ${{ secrets.TELEGRAM_TO }}
token: ${{ secrets.TELEGRAM_TOKEN }}
message: |
Health check of the:
${{secrets.SERVER_HOST}}/api/actuator/health
failed with the result:
${{ steps.ping.outputs.status }}
Wysyłamy na telegram tylko wtedy, gdy akcja w poprzednim kroku nie powiodła się. Do wysłania wiadomości używamy appleboy/telegram-action; o tym, jak zdobyć token bota i identyfikator czatu, możesz przeczytać w dokumentacji: github.com/appleboy/telegram-action
Nie zapomnij wpisać sekretów na Githubie: adres URL serwera i tokeny dla bota telegramu.
Bonus track - JIRA dla leniwych
Obiecałem, że wrócimy do JIRA i wróciliśmy. Setki razy zaobserwowałem na stand-upach sytuację, gdy programiści stworzyli funkcję, połączyli gałąź, ale zapomnieli przeciągnąć problem do JIRA. Oczywiście, gdyby to wszystko było zrobione w jednym miejscu, byłoby łatwiej, ale tak naprawdę piszemy kod w IDE, scalamy gałęzie do bitbucketa lub GitHuba, a następnie przeciągamy zadania do Jiry, w tym celu musimy otworzyć nowe okna , czasami zaloguj się ponownie itp. Kiedy doskonale pamiętasz, co musisz dalej zrobić, nie ma sensu ponownie otwierać planszy. W rezultacie rano na stand-upie musisz poświęcić czas na aktualizację tablicy zadań.
GitHub również pomoże nam w tym rutynowym zadaniu; na początek możemy automatycznie przeciągnąć problemy do kolumny code_review, gdy przesyłamy żądanie ściągnięcia. Wszystko, co musisz zrobić, to postępować zgodnie z konwencją nazewnictwa oddziałów:
[имя проекта]-[номер таска]-название
na przykład, jeśli klucz projektu „Akcje GitHub” to GA, to GA-8-jira-bot mogłaby stanowić gałąź realizacji zadania GA-8.
Integracja z JIRA działa poprzez działania z Atlassian, nie są one idealne, muszę przyznać, że niektóre z nich w ogóle u mnie nie zadziałały. Ale omówimy tylko te, które na pewno działają i są aktywnie wykorzystywane.
Najpierw należy zalogować się do JIRA za pomocą akcji: atlassian/gajira-login
Wyodrębniamy identyfikator zadania z nazwy oddziału:
- name: Find Issue
id: find_issue
shell: bash
run: |
echo "::set-output name=ISSUE_ID::$(echo ${GITHUB_HEAD_REF} | egrep -o 'GA-[0-9]{1,4}')"
echo brach name: $GITHUB_HEAD_REF
echo extracted issue: ${GITHUB_HEAD_REF} | egrep -o 'GA-[0-9]{1,4}'
- name: Check Issue
shell: bash
run: |
if [[ "${{steps.find_issue.outputs.ISSUE_ID}}" == "" ]]; then
echo "Please name your branch according to the JIRA issue: [project_key]-[task_number]-branch_name"
exit 1
fi
echo succcessfully found JIRA issue: ${{steps.find_issue.outputs.ISSUE_ID}}
Jeśli poszukasz na rynku GitHub, możesz znaleźć akcję dla tego zadania, ale musiałem napisać to samo używając grep, używając nazwy oddziału, ponieważ ta akcja z Atlassian w żaden sposób nie chciała działać na moim projekcie , aby dowiedzieć się, co jest nie tak - dłużej niż robić to samo rękami.
Pozostaje tylko przenieść zadanie do kolumny „Przegląd kodu” podczas tworzenia żądania ściągnięcia:
Jest do tego specjalna akcja na GitHubie, wystarczy identyfikator problemu uzyskany w poprzednim kroku i autoryzacja w JIRA, którą zrobiliśmy powyżej.
W ten sam sposób możesz przeciągać zadania podczas łączenia z masterem i inne zdarzenia z przepływu pracy GitHub. Generalnie wszystko zależy od Twojej wyobraźni i chęci automatyzacji rutynowych procesów.
odkrycia
Jeśli spojrzysz na klasyczny diagram DEVOPS, omówiliśmy wszystkie etapy, może z wyjątkiem obsługi, myślę, że jeśli spróbujesz, możesz znaleźć na rynku pewne działania w zakresie integracji z systemem help-desk, więc założymy, że rurociąg się odwrócił być dokładne i na podstawie jego stosowania można wyciągnąć wnioski.
Plusy:
Marketplace z gotowymi akcjami na każdą okazję, to jest bardzo fajne. W większości z nich można także zajrzeć do kodu źródłowego, aby dowiedzieć się, jak rozwiązać podobny problem, lub wysłać prośbę o dodanie funkcji do autora bezpośrednio w repozytorium GitHub.
Wybór platformy docelowej do montażu: Linux, mac os, Windows to dość interesująca funkcja.
Github Packages to świetna sprawa, wygodnie jest trzymać całą infrastrukturę w jednym miejscu, nie trzeba surfować po różnych oknach, wszystko jest w promieniu jednego, dwóch kliknięć myszką i jest doskonale zintegrowane z GitHub Actions. Obsługa rejestru Dockera w wersji darmowej jest również dobrą zaletą.
GitHub ukrywa sekrety w dziennikach kompilacji, więc używanie go do przechowywania haseł i tokenów nie jest takie straszne. Podczas wszystkich moich eksperymentów nigdy nie udało mi się zobaczyć sekretu w czystej postaci w konsoli.
Bezpłatnie dla projektów Open Source
Wady:
YML, cóż, nie lubię go. Podczas pracy z takim przepływem najczęstszym komunikatem o zatwierdzeniu jest „napraw format yml”, wtedy zapominasz gdzieś umieścić tabulator lub piszesz go w niewłaściwej linii. Generalnie siedzenie przed ekranem z kątomierzem i linijką nie należy do najprzyjemniejszych.
DEBUGOWANIE, debugowanie przepływu za pomocą zatwierdzeń, uruchamianie przebudowy i wysyłanie danych wyjściowych do konsoli nie zawsze jest wygodne, ale raczej należy do kategorii „przesadziłeś”; jesteś przyzwyczajony do pracy z wygodnym IDEA, kiedy możesz debugować wszystko .
Możesz napisać swoją akcję na czymkolwiek, jeśli zawiniesz ją w Docker, ale natywnie obsługiwany jest tylko JavaScript, oczywiście jest to kwestia gustu, ale wolałbym coś innego zamiast js.
W przyszłym tygodniu będę występować z raport na konferencji Heisenbug 2020 Piter. Powiem Ci nie tylko, jak uniknąć błędów podczas przygotowywania danych testowych, ale także podzielę się moimi tajnikami pracy ze zbiorami danych w aplikacjach Java!