diff --git a/.gitea/workflows/ci-validate.yml b/.gitea/workflows/ci-validate.yml index 807a63b..7995e5b 100644 --- a/.gitea/workflows/ci-validate.yml +++ b/.gitea/workflows/ci-validate.yml @@ -8,10 +8,13 @@ on: secrets: GITEA_TOKEN: required: true + GIT_PAGES_PUBLISH_TOKEN: + required: true env: GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }} GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }} jobs: validate: @@ -30,12 +33,10 @@ jobs: id: validate run: bash .ci/scripts/ci-validate.sh - - name: Report status - if: always() - run: | - if [ "${{ steps.validate.outcome }}" = "success" ]; then - bash .ci/scripts/report-status.sh success "CI config valid" ci-validate - else - bash .ci/scripts/report-status.sh failure "CI validation FAILED" ci-validate - exit 1 - fi + - name: Report status SUCCESS + if: success() + run: bash .ci/scripts/report-status.sh success "CI config valid" ci-validate + + - name: Report status FAILURE + if: failure() + run: bash .ci/scripts/report-status.sh failure "CI validation FAILED" ci-validate diff --git a/.gitea/workflows/eat-own-dogfood-cucumber-tests.yml b/.gitea/workflows/eat-own-dogfood-cucumber-tests.yml index 651152f..612a14d 100644 --- a/.gitea/workflows/eat-own-dogfood-cucumber-tests.yml +++ b/.gitea/workflows/eat-own-dogfood-cucumber-tests.yml @@ -56,6 +56,7 @@ jobs: - name: Report status if: always() + shell: bash run: | if [ "${CUCUMBER_EXIT}" = "0" ]; then if [ -f "reports/${GITHUB_SHA:0:8}/cucumber/index.html" ]; then diff --git a/.gitea/workflows/report-index.yml b/.gitea/workflows/report-index.yml index 83de088..94d15b0 100644 --- a/.gitea/workflows/report-index.yml +++ b/.gitea/workflows/report-index.yml @@ -30,12 +30,10 @@ jobs: id: report-index run: bash .ci/.gitea/scripts/generate-report-index.sh - - name: Report status - if: always() - run: | - if [ "${{ steps.report-index.outcome }}" = "success" ]; then - bash .ci/scripts/report-status.sh success "Build complete" ci-build - else - bash .ci/scripts/report-status.sh failure "Build FAILED" ci-build - exit 1 - fi + - name: Report status SUCCESS + if: success() + run: bash .ci/scripts/report-status.sh success "Build complete" ci-build + + - name: Report status FAILURE + if: failure() + run: bash .ci/scripts/report-status.sh failure "Build FAILED" ci-build diff --git a/docs/adr/0007-status-reporting-pattern.md b/docs/adr/0007-status-reporting-pattern.md new file mode 100644 index 0000000..eb210d6 --- /dev/null +++ b/docs/adr/0007-status-reporting-pattern.md @@ -0,0 +1,97 @@ +# 7. Statusraportoinnin pattern + +## Päätös + +Jokaisen jobin on raportoitava lopputulos commit-statukseen. Käytössä on kaksi +patternia, jotka eroavat toisistaan vain raporttien julkaisun osalta. + +### Tool-jobit (validate, build, push, tag, check-version, report-index) + +``` +PENDING → työvaihe → SUCCESS (if: success) / FAILURE (if: failure) +``` + +```yaml +- name: Set Gitea status to PENDING + run: bash .ci/scripts/report-status.sh pending "Validating..." ci-validate + +- name: Do work + run: some-command + +- name: Report status SUCCESS + if: success() + run: bash .ci/scripts/report-status.sh success "OK" ci-validate + +- name: Report status FAILURE + if: failure() + run: bash .ci/scripts/report-status.sh failure "FAILED" ci-validate +``` + +### Test-jobit (bats, cucumber) + +``` +PENDING → testit → publish (always) → status (always, exit-koodin mukaan) +``` + +```yaml +- name: Set Gitea status to PENDING + run: bash .ci/scripts/report-status.sh pending "Running tests..." ci-tests + +- name: Run tests + shell: bash + run: | + run-tests + EXIT=$? + echo "EXIT=${EXIT}" >> "${GITHUB_ENV}" + exit ${EXIT} + +- name: Publish reports + if: always() + run: bash .ci/scripts/publish-git-pages.sh suite + +- name: Report status + if: always() + shell: bash + run: | + if [ "${EXIT}" = "0" ]; then + bash .ci/scripts/report-status.sh success "Tests OK" ci-tests suite + else + bash .ci/scripts/report-status.sh failure "Tests FAILED" ci-tests suite + fi +``` + +## Periaatteet + +1. Ennen varsinaista työvaihetta asetetaan status `pending` — näin commit-näkymässä + näkyy heti, että jobi on käynnissä. +2. `run`-komennon on nostettava virhe ylös — oli kyse tool-callista (docker, + curl, bash) tai testien ajamisessa tapahtuneesta testivirheestä. +3. Tool-jobit käyttävät `if: success()` ja `if: failure()` — jobin sisäinen + tila ratkaisee suoraan kumman steppi ajetaan. +4. Test-jobit käyttävät `if: always()` publish- ja status-stepeissä — raportti + julkaistaan ja status asetetaan aina, riippumatta testin lopputuloksesta. +5. Testiraportit julkaistaan myös virhetilanteessa, mikäli ne ovat syntyneet. +6. Status-linkki ohjaa testiraporttiin (suite-parametri) jos raportti on + olemassa, muuten Gitea Actions -logiin. + +## Tausta + +Aiemmassa `quality-gate.yml`:ssä käytettiin `if: always()` + sisäistä if/elseä +myös tool-jobeissa (`steps..outcome`-tarkistus). Tämä on tarpeetonta +kompleksisuutta työvaiheille, jotka eivät tuota erillistä raporttia — jobin oma +tila (`success`/`failure`) on riittävä ehto. + +Test-jobit tarvitsevat `if: always()`:n koska raportti on julkaistava ja status +asetettava silloinkin kun testit epäonnistuvat. Status-viestin URL osoittaa +suoraan raporttiin git-pagesissa, mikä on kriittinen feature virheenjäljityksessä. + +Pipeline-status jokaiselle vaiheelle on välttämätön, koska Gitean branch +protection -säännöt käyttävät commit-statusta merge-estona. Jokainen +`ci-*`-konteksti voi toimia vaadittuna statuksena PR:n sulkemiselle — jos +yksikin status on `failure`, merge estyy. Tämä korvaa Jenkinsin +`disableConcurrentBuilds()`- ja build gate -logiikan Gitea-natiivilla +mekanismilla. + +Status-näkymä toimii myös master-haaran terveyden monitorina: Git UI:sta +näkee suoraan yhdellä silmäyksellä onko uusin commit vihreä. Ei tarvitse +avata CI-järjestelmän dashboardia. diff --git a/docs/adr/0008-exit-code.md b/docs/adr/0008-exit-code.md new file mode 100644 index 0000000..cb6e3c2 --- /dev/null +++ b/docs/adr/0008-exit-code.md @@ -0,0 +1,73 @@ +# 8. Exit code — ainoa onnistumisen mittari + +## Päätös + +Jokaisen `run`-stepin on nostettava virheellinen exit-koodi ylös sellaisenaan. +Exit-koodia ei saa "syödä" missään tilanteessa. Onnistumisen ja epäonnistumisen +päättely tapahtuu **ainoastaan** exit-koodin perusteella — ei tiedostojen +olemassaolon, stdout-tulosteen tai minkään muun heuristiikan perusteella. + +## Periaatteet + +1. Exit-koodi on ainoa totuus. `0` = onnistui, kaikki muut = epäonnistui. +2. Exit-koodia ei saa syödä. Pipe (`|`) viimeisenä komentona `tee`:hen syö + exit-koodin — `docker run … | tee file` palauttaa aina 0. +3. Data transfer -pipet ovat sallittuja (`tar c . | docker run … tar x`), + koska niiden exit-koodilla ei ole semanttista merkitystä. +4. Testien tai työkalujen ajaminen ei saa päättyä pipeen. +5. `set -o pipefail` ei ole riittävä suojaus — PIPESTATUS resetoituu herkästi. + +## Sallitut patternit + +```yaml +# Oikein: suora ajo, exit koodi $?:iin +- name: Do work + run: | + some-command + EXIT=$? + echo "EXIT=${EXIT}" >> "${GITHUB_ENV}" + exit ${EXIT} + +# Oikein: stdout talteen ilman pipeä +- name: Do work + run: | + some-command > results.txt 2>&1 + EXIT=$? + echo "EXIT=${EXIT}" >> "${GITHUB_ENV}" + exit ${EXIT} + +# Oikein: docker run ilman pipeä +- name: Run in container + run: | + docker run --rm image command > output.txt 2>&1 + EXIT=$? + exit ${EXIT} +``` + +## Kielletyt patternit + +```yaml +# Väärin: pipe syö exit-koodin +- run: docker run … | tee results.txt + +# Väärin: pipe syö exit-koodin +- run: tar … | docker … | tee file + +# Väärin: onnistumisen päättely tiedoston olemassaolosta +- run: | + some-command || true + [ -f success.txt ] && exit 0 || exit 1 +``` + +## Tausta + +Gitea Actionsissa `run`-stepin tila määräytyy viimeisen komennon exit-koodista. +Pipe (`|`) asettaa `$?`:ksi viimeisen komennon tuloksen — jos viimeinen komento +on `tee`, tulos on aina 0 riippumatta siitä mitä aiemmat komennot palauttivat. + +Tämä on aiheuttanut tuotannossa tilanteita, joissa testit feilasivat mutta jobi +näytti vihreää, koska `tee` söi exit-koodin. Virhe havaittiin vasta kun raportteja +alettiin lukea manuaalisesti — commit-status valehteli. + +Ratkaisu on yksiselitteinen: exit-koodi talteen `$?`-muuttujaan ennen kuin mikään +muu komento ehtii muuttaa sitä, ja stepin viimeinen komento on aina `exit ${EXIT}`.