Inżynieria

Check changelogu dla GitHub Actions

4 min czytania

Każdy zespół, który ręcznie prowadzi changelog, przeszedł tę samą rozmowę po tym samym incydencie: wydanie wyszło bez wpisu, ktoś pyta dlaczego, a szczera odpowiedź brzmi, że osoba, która by go napisała, działała szybko, a krok changelogu żył tylko w pamięci. Automatyzacja changelogu opisuje, co pipeline może bezpiecznie zautomatyzować, a co wciąż potrzebuje człowieka; check changelogu w CI to druga połowa tego problemu, bo automatyzacja pisania nie pomaga, jeśli nikt nie jest zobowiązany, by ją w ogóle uruchomić. GitHub Actions to miejsce, gdzie większość zespołów już uruchamia swoje checki pull requestów, więc to tam żyje też ten.

Dlaczego “prosimy ludzi o dodanie wpisu” zawodzi według przewidywalnego wzorca?

Bo konkuruje o uwagę ze wszystkim innym w pull requeście, i jest jedyną częścią bez natychmiastowej konsekwencji za pominięcie. Testy zawodzą głośno i blokują merge. Brakujący wpis w changelogu nie blokuje niczego, więc przegrywa, gdy tylko ktoś się spieszy, co w praktyce jest większością czasu. Polityka wymuszana pamięcią degraduje się dokładnie w tempie, jakiego można się spodziewać: dobrze przez pierwsze kilka tygodni po ustaleniu, potem cicho porzucona, gdy osoba, której na tym zależało, idzie na urlop albo zmienia zespół.

Co naprawdę weryfikuje check CI dla wpisu w changelogu?

Nie jakość tekstu, tylko to, że wpis istnieje i ma poprawną formę, co jest właściwym zakresem dla checku changelogu uruchamianego w CI, a nie w czyjejś głowie. Powszechna forma: check patrzy na diff PR i wymaga albo nowego pliku w katalogu changesetów (wzorzec, którego używają Changesets i podobne narzędzia) albo zmienionej linii w pliku changelogu, i psuje build, jeśli żadne z nich nie istnieje. Przegląd tego, co wpis naprawdę mówi, wciąż odbywa się tam, gdzie zawsze się odbywał, w code review, bo ten osąd nie należy do skryptu.

Co weryfikuje check CICzego nie weryfikuje
Istnieje changeset lub linia changelogu w diffieCzy sformułowanie jest jasne
Wpis odnosi się do właściwego pakietu, w monorepoCzy zmiana w ogóle zasługuje na wpis
Plik jest poprawny składniowo (front matter, forma JSON)Czy wpis jest szczery co do wpływu

Czy każdy PR tego potrzebuje, czy niektóre zmiany są zwolnione?

Niektóre są zwolnione, i lista zwolnień jest miejscem, gdzie takie systemy naprawdę się buduje albo porzuca. Podniesienie zależności bez widocznego efektu, zmiana wyłącznie testów, wewnętrzny refaktor bez zmiany zachowania: żadna z tych rzeczy nie powinna zmuszać współtwórczyni do wymyślania wpisu w changelogu dla czegoś, czym nikt czytający changelog się nie przejmuje. Działający wzorzec to etykieta lub flaga, którą współtwórczyni może zastosować (no-changelog-needed), spełniająca check CI bez pliku, sprawdzana przez tego, kto zatwierdza PR, tak że samo zwolnienie przechodzi tę samą kontrolę, co przeszedłby wpis.

Co dzieje się z uzasadnionymi wyjątkami, jak pilny hotfix?

Bramka należy do mergu, nie do deployu: hotfix pod prawdziwą presją czasu może mergować się z wpisem-zastępczym albo biletem uzupełniającym, o ile check CI jest zaspokajany intencją zamiast tylko ukończonym akapitem; niektóre zespoły akceptują jednolinijkowy stub, który maintainerka dopracowuje przed kolejnym cięciem wydania. Czego bramka nigdy nie powinna pozwolić, to cichego pominięcia kroku, bo zapomniany stub jest mniejszą porażką niż wpis, który nigdy nie istniał, a stub przynajmniej zostawia ślad, który ktoś może znaleźć później.

# .github/workflows/changelog-check.yml
on:
  pull_request:
    types: [opened, synchronize, reopened, labeled, unlabeled]
jobs:
  changelog:
    if: >-
      !contains(github.event.pull_request.labels.*.name,
      'no-changelog-needed')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # diff potrzebuje gałęzi bazowej
      - name: Require changelog entry
        run: |
          base="origin/${{ github.base_ref }}"
          if ! git diff --name-only "$base"...HEAD \
              | grep -q '^\.changeset/'; then
            echo "No changeset. Add one, or have a maintainer"
            echo "apply the no-changelog-needed label."
            exit 1
          fi

Skąd wiadomo, że sam check jest poprawny, zanim zacznie blokować prawdziwe PR-y?

Otwórzcie najpierw testowy pull request na jednorazową gałąź: jeden z changesetem, jeden bez, i jeden z etykietą zwolnienia, i potwierdźcie, że wszystkie trzy dają oczekiwany wynik, zanim check zacznie dotyczyć cudzej pracy. Check changelogu, który zawodzi w trybie otwartym, przepuszczając każdy PR, bo warunek został napisany na odwrót, jest gorszy niż brak checku, bo wygląda jak pokrycie, którego nie ma. workflow_dispatch na tym samym pliku, uruchomiony ręcznie na kilku niedawno zmergowanych PR-ach, wyłapuje większość takich błędów bez potrzeby żywego pull requesta.

Czy ten sam pomysł działa poza GitHub Actions?

Kształt się przenosi, zmienia się tylko składnia. GitLab CI wyraża tę samą regułę jako blok rules zadania sprawdzający $CI_MERGE_REQUEST_LABELS zamiast if z GitHub Actions, a wymagana akceptacja merge requesta może zastąpić krok przeglądu zwolnienia. Check opisany w tym artykule to GitHub Actions, bo to platforma, na której jest już większość czytających go zespołów, ale leżący u podstaw wymóg, sprawdzana maszynowo bramka zamiast umownej konwencji, jest taki sam wszędzie tam, gdzie CI działa przed mergem.

Czy to działa tak samo w monorepo?

Potrzebuje jednego elementu więcej: dla którego pakietu jest wpis. Changelogi monorepo opisuje, dlaczego jeden plik dla całego repo przestaje działać, gdy pakiety są wydawane niezależnie; check CI dziedziczy ten sam wymóg; changeset, który nie nazywa pakietu, nie jest użytecznym dowodem, że właściwy changelog się zaktualizuje, tylko że jakiś plik zmienił się gdzieś w diffie. Narzędzia zbudowane do tego (Changesets to powszechne w ekosystemie JavaScript) proszą współtwórczynię o wybór dotkniętego pakietu i podbicie semver w tym samym momencie, w którym changeset jest tworzony, więc check CI dostaje obie części za darmo zamiast wnioskować je później.

FAQ

Czy check CI powinien blokować merge, czy tylko ostrzegać? Blokować. Ostrzeżenie jest funkcjonalnie identyczne z uprzejmym proszeniem, co już zawiodło. Etykieta zwolnienia istnieje właśnie po to, żeby prawdziwy przypadek tylko-ostrzeżenia miał mimo to legalną ścieżkę przez tę samą surową bramkę.

Kto sprawdza, czy etykieta zwolnienia została zastosowana poprawnie? Ten, kto zatwierdza pull request, w ramach przeglądu, który i tak już wykonuje. Etykieta nigdy nie powinna być zastosowana samodzielnie i bez przeglądu, bo staje się tym samym cichym obejściem, które bramka miała zamknąć.

Czy wymuszanie tego w CI zastępuje potrzebę pipeline’u automatyzacji changelogu? Nie, karmi go. Automatyzacja changelogu opisuje zamianę ustrukturyzowanych wpisów w stronę, strumień i e-mail; check CI to to, co gwarantuje, że te ustrukturyzowane wpisy w ogóle istnieją, by je zautomatyzować.

Jaka jest najmniejsza wersja tego, którą warto zbudować najpierw? Pojedynczy check, który zawodzi, jeśli żaden plik nie zmienił się w wyznaczonym katalogu changelogu, z jedną etykietą zwolnienia. Routing według pakietu i wnioskowanie semver dla monorepo mogą przyjść później; podstawowy nawyk, wpis istnieje albo ktoś wyraźnie powiedział, że nie jest potrzebny, jest tym, co warto mieć od pierwszego dnia.


Twierdzenia techniczne w tym artykule nie zostały niezależnie zweryfikowane. Jeśli coś się nie zgadza, daj nam znać, a poprawimy to.

Powiązane w changeloop: Porównanie narzędzi do changeloga, Generator changeloga

changeloop
Zespół, który tworzy changelog zamykający pętlę. Użytkownicy o coś proszą, Twój zespół to dostarcza, proszący się dowiaduje.