Feature/yksinkertaistetaan raportointi logiikkaa (#22)
unit-tests Bats test report
CI Main / Bats tests (push) Successful in 1m21s
ci-docker-build-push Docker build & push 0.2.9 OK
CI Main / Build & Push Docker (push) Successful in 40s
CI Main / Report Summary (push) Successful in 5s
CI Main / Move provider version tag (push) Successful in 12s
CI Main / Load example-gitea-env.conf to pipeline env (push) Successful in 18s
CI Main / Check existing artifact (push) Successful in 12s
acc-tests Cucumber test report
CI Main / Cucumber tests (push) Successful in 2m3s

Co-authored-by: moilanik <niko.moilanen@tietoevry.com>
Reviewed-on: #22
This commit was merged in pull request #22.
This commit is contained in:
2026-06-18 12:55:02 +03:00
parent 65d385f9b9
commit 737b2fc3f2
11 changed files with 357 additions and 180 deletions
+189 -88
View File
@@ -79,16 +79,14 @@ Ei monoliittista `ci-tests.yml`. Jokainen testityyppi tai operaatio on oma `work
## 3. Exit-koodin käsittely
Jokainen testi kaappaa komentonsa exit-koodin eksplisiittisesti:
`set -e` on oletuksena käytössä Gitea Actions -stepeissä — ensimmäinen feilaava komento pysäyttää stepin
ja exit-koodi välittyy natiivisti. Ylimääräistä `EXIT=$?` + `echo >> GITHUB_ENV` -käärettä ei tarvita.
```yaml
- name: Run tests
shell: bash
run: |
<testikomento> > results.txt 2>&1
EXIT=$?
echo "EXIT=${EXIT}" >> "${GITHUB_ENV}"
exit ${EXIT}
```
**Miksi ei pipeä (`| tee`):**
@@ -101,8 +99,29 @@ Jokainen testi kaappaa komentonsa exit-koodin eksplisiittisesti:
<komento> > results.txt 2>&1
```
Ilman `EXIT=$?` + `exit ${EXIT}` komento voi feilata mutta job menee läpi vihreänä — `container:`-modessa
shellin käyttäytyminen vaihtelee.
`set -e` ei pelasta pipe-tilanteessa — `|` syö exit-koodin kuten ennenkin. Redirectillä exit-koodi
välittyy luonnollisesti.
**Yksi asia per step:** Älä koskaan niputa useaa post-process-komentoa samaan `run:`-blockiin.
`bash -e` pysäyttää koko stepin jos yksi komento epäonnistuu — seuraavat jäävät ajamatta.
Käytä erillisiä steppejä `if: always()`:lla, jotta jokainen vaihe ajetaan itsenäisesti:
```yaml
# VÄÄRIN — jos coverage epäonnistuu, report jää generoimatta
- name: Post-process reports
run: |
bash .ci/.gitea/scripts/bats-coverage.sh reports/bats
bash .ci/.gitea/scripts/bats-report.sh reports/bats
# OIKEIN — erilliset stepit if: always()
- name: Post-process coverage
if: always()
run: bash .ci/.gitea/scripts/bats-coverage.sh reports/bats
- name: Post-process test report
if: always()
run: bash .ci/.gitea/scripts/bats-report.sh reports/bats
```
## 4. Konttipolitiikka
@@ -114,44 +133,33 @@ shellin käyttäytyminen vaihtelee.
`latest` on näille paras käytäntö, ei kompromissi
3. **Ei koskaan `curl`-latauksia CI-ajon sisällä** — työkalujen asennus CI-stepeissä hidastaa,
epäluotettavaa, ja vaikeuttaa toistettavuutta
4. **Konttikuva hallitaan workflow'ssa, ei kutsujassa** — jos workflow vaatii tietyn
konttikuvan, se määritellään oletuksena (`default:`) workflow'n inputissa.
Kutsujan ei tarvitse tietää eikä välittää image-nimeä ellei halua ylikirjoittaa.
CI-kontin build-workflow'n template: [skills/ci-container-build/SKILL.md](../ci-container-build/SKILL.md) — sisältää
valmiin `ci-container-build-<kontti>.yml`-pohjan jossa `workflow_dispatch`-tuki manuaaliajoon.
### 4.1 CI-kontin ajaminen jobissa
CI-kontin voi ajaa joko `container:`-direktiivillä (kaikki stepit kontissa)
tai `docker run --rm`:llä stepin sisällä (checkout natiivisti). Molemmat tavat
toimivat.
Ainoa sallittu tapa on `container:`-direktiivi. `docker run` komennolla kontin
käynnistäminen stepin sisällä on anti-pattern.
**Miksi:** `docker run` erilliskonttina aiheuttaa:
- Tiedostojen jako vaatii erillisen volyyminhallinnan (`docker volume create`)
- Coverage-data jää volyymiin, ei filesystemille → post-process-skriptit eivät löydä sitä
- Ylimääräisiä siirtoja (`tar`, `docker cp`), jotka voivat epäonnistua hiljaa
- Vaikeampi debugata (data on kontissa, ei CWD:ssä)
`container:`-direktiivillä kaikki ajetaan samassa kontissa — tiedostot ovat suoraan
filesystemillä, post-process-skriptit näkevät ne ilman erillistä siirtoa.
```yaml
# Tapa A: container:-direktiivi
jobs:
<työkalu>:
runs-on: ubuntu-latest
container:
image: ${{ inputs.<image-name> }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
repository: <owner>/gitea-ci-library
path: .ci
- name: Run <työkalu>
shell: bash
run: |
mkdir -p "reports/${GITHUB_SHA:0:8}/<suite>"
<komento> > "reports/${GITHUB_SHA:0:8}/<suite>/results.txt" 2>&1
EXIT=$?
echo "EXIT=${EXIT}" >> "${GITHUB_ENV}"
exit ${EXIT}
```
```yaml
# Tapa B: docker run --rm stepin sisällä (kuten example-bats-tests.yml)
jobs:
<työkalu>:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v4
@@ -162,87 +170,179 @@ jobs:
- name: Run <työkalu>
shell: bash
run: |
docker volume create ws-<suite>
tar c . | docker run --rm -i -v ws-<suite>:/data alpine tar x -C /data
mkdir -p "reports/${GITHUB_SHA:0:8}/<suite>"
set +e
docker run --rm \
-v ws-<suite>:/data \
--entrypoint bash ${{ inputs.<image-name> }} \
-c 'cd /data && <komento>' \
> "reports/${GITHUB_SHA:0:8}/<suite>/results.txt" 2>&1
EXIT=$?
docker volume rm ws-<suite> > /dev/null 2>&1
echo "EXIT=${EXIT}" >> "${GITHUB_ENV}"
exit ${EXIT}
mkdir -p "reports/<suite>"
<komento> > "reports/<suite>/results.txt" 2>&1
- name: Publish <suite> reports
if: always()
run: bash .ci/scripts/publish-git-pages.sh <suite>
- name: Report status
- name: Post-process reports
if: always()
run: |
if [ "${EXIT}" = "0" ]; then
bash .ci/scripts/report-status.sh success "<kuvaus>" <context> <suite>
else
bash .ci/scripts/report-status.sh failure "<kuvaus>" <context> <suite>
fi
<mahdollinen_raporttien_jälkikäsittely>
- name: Report
if: always()
run: bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite>
```
**Malli:** `example-bats-tests.yml` (tapa B).
Jos testi tuottaa raportteja suoraan ilman jälkikäsittelyä, Post-process-steppiä ei tarvita.
Jos jälkikäsittely on tarpeen (coverage-siirto, HTML-generointi raa'asta outputista),
se tehdään omassa stepissä `if: always()` — katso tarkemmin [Raporttitasot](#5-raporttitasot).
**Mallit:**
- `example-cucumber-tests.yml` — ei post-processia
- `example-bats-tests.yml` — post-process coverage + report
## 5. Raporttitasot
Testi tuottaa raportin `reports/${GITHUB_SHA:0:8}/<suite>/`-hakemistoon. `publish-git-pages.sh` julkaisee sen,
`report-status.sh` linkittää commit-statusin siihen. Molemmat `if: always()`.
Testi tuottaa raportin `reports/<suite>/`-hakemistoon. Yksi `ci-report.sh`-kutsu hoitaa se
julkaisun että commit-statuksen — erillistä Publish + Report Status -kaksivaiheisuutta ei tarvita.
### Taso 1: Pelkkä teksti
### Taso 1: Ei jälkisittelyä
Kun testi tuottaa vain stdout/stderr — tallennetaan `results.txt`:
Kun testi tuottaa raportit suoraan (kuten `pytest --html` tai `cucumber-js --format html`):
```yaml
- name: Run tests
shell: bash
run: |
mkdir -p "reports/${GITHUB_SHA:0:8}/<suite>"
<testikomento> > "reports/${GITHUB_SHA:0:8}/<suite>/results.txt" 2>&1
EXIT=$?
echo "EXIT=${EXIT}" >> "${GITHUB_ENV}"
exit ${EXIT}
mkdir -p "reports/<suite>"
<testikomento>
- name: Publish reports
- name: Report
if: always()
shell: bash
run: bash .ci/scripts/publish-git-pages.sh <suite>
run: bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite>
```
- name: Report status
if: always()
### Taso 2: Jälkikäsittely tarvitaan
Kun testi tuottaa raakadataa (stdout, coverage-tiedostot) joka pitää muuntaa tai siirtää
`reports/<suite>/`-hakemistoon, käytetään Post-process-steppejä. **Jokainen operaatio
omassa stepissään** — älä koskaan niputa useaa post-process-komentoa samaan `run:`-blockiin:
```yaml
- name: Run tests
shell: bash
run: |
if [ "${EXIT}" = "0" ]; then
bash .ci/scripts/report-status.sh success "<kuvaus>" <context> <suite>
else
bash .ci/scripts/report-status.sh failure "<kuvaus>" <context> <suite>
fi
mkdir -p "reports/<suite>"
<testikomento> > "reports/<suite>/results.txt" 2>&1
- name: Post-process coverage
if: always()
run: <siirrä coverage-data reports/<suite>/coverage/-hakemistoon>
- name: Post-process test report
if: always()
run: <HTML-generointi raa'asta outputista>
- name: Report
if: always()
run: bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite>
```
### Taso 2: HTML-raportti
**Huomio subdir-sisällöstä:** Jos testi tuottaa dataa alihakemistoon (esim.
coverage `./coverage/`-kansioon), se pitää erikseen SIIRTÄÄ
`reports/<suite>/<subdir>/`-hakemistoon ennen `ci-report.sh`:n ajoa.
Ilman siirtoa sisältö ei näy index-sivulla, vaikka työkalu tuottaisi sen oikein.
Subdir vaatii lisäksi `index.html`:n, jotta `ci-report.sh` löytää sen.
Kun testi tuottaa strukturoitua dataa (JUnit XML, coverage, tms.) — generoidaan HTML ja `index.html`:
**Miksi:** Gitea Actions käyttää `bash -e`-oletusta. Jos yksi post-process-komento
epäonnistuu (esim. `set -euo pipefail`-skripti), koko stepi pysähtyy eivätkä seuraavat
komennot käynnisty — raportti jää julkaisematta. Erilliset stepit `if: always()` takaavat
että jokainen post-process-vaihe ajetaan itsenäisesti.
### Monta raportoitavaa tiedostoa
Kun `reports/<suite>/`-hakemistossa on useita tiedostoja tai alihakemistoja,
`ci-report.sh` generoi automaattisesti `reports/<suite>/index.html` jos hakemistossa
on enemmän kuin yksi raportoitava item.
```
reports/<sha8>/<suite>/
├── index.html ← generoitu: linkit alla oleviin
├── results.txt ← testin stdout
── junit.xml ← testin JUnit XML -output
└── junit.html ← generoitu HTML (xsltproc, tms.)
reports/<suite>/
├── results.txt ← testin stdout (skannataan FILES)
├── test-report.html ← generoitu HTML (skannataan FILES)
── <mikä tahansa>/ ← alihakemisto (skannataan SUBDIRS)
└── index.html ← VAIN jos tämä on olemassa
```
`index.html` linkittää kaikkiin raporttitiedostoihin. Selain avaa sen ja navigoi sieltä
yksittäisiin raportteihin.
**Subdir-sääntö:** Alihakemisto näkyy indexissä VAIN jos se sisältää `index.html`:n.
Pelkkä tyhjä subdir ilman `index.html`:ää ei näy — tämä on yleisin syy miksi
jokin raportin osa (kuten coverage) puuttuu indexistä.
## 6. Nimeäminen
## 6. Raportin julkaisukelpoisuus
`ci-report.sh` päättää onko raportti julkaisukelpoinen skannaamalla
`reports/<suite>/`-hakemistoa.
### Mitä skannataan
| Mitä | Sääntö |
|---|---|
| **Tiedostot (FILES)** | Kaikki `reports/<suite>/`-juuressa olevat tiedostot paitsi `index.html` |
| **Alihakemistot (SUBDIRS)** | Vain ne, joissa on `index.html` |
### Julkaisukelpoisuus
| Tila | Seuraus |
|---|---|
| `FILES + SUBDIRS = 0` | **Failure**`ci-report.sh` palauttaa virheen, raporttia ei julkaista |
| `FILES + SUBDIRS = 1` | Suora linkki itemiin — ei generoi index-sivua |
| `FILES + SUBDIRS > 1` | Generoi `reports/<suite>/index.html`-sivun, linkit kaikkiin itemeihin |
### Esimerkki: coverage-näkymä
```
reports/<suite>/coverage/index.html ← on olemassa
```
Coverage-dataa ei siirretä automaattisesti. Testin tai post-process-stepin pitää
siirtää coverage `reports/<suite>/coverage/`-hakemistoon ja varmistaa että
`index.html` on mukana. Sama periaate pätee mihin tahansa subdir-sisältöön.
## 7. Debug-ohje: raportti ei näy
### 1. Aja lokaalisti samalla komennolla kuin CI
Näet mitä tiedostoja syntyy, mihin ne tulevat ja mikä on työkalun exit-koodi.
```bash
# Esimerkki
mkdir -p reports/bats
bashcov -- bats tests/ > reports/bats/results.txt 2>&1
echo "exit: $?"
ls -la reports/bats/
```
### 2. Lisää `echo "DEBUG: ..." >&2` ennen ja jälkeen kriittisen operaation
Debuggaus stderriin (`>&2`) näkyy CI-logissa eikä häiritse skriptin normaalia outputia.
```bash
echo "DEBUG: coverage exists? $([ -d coverage ] && echo YES || echo NO)" >&2
echo "DEBUG: target/index.html exists? $([ -f reports/suite/coverage/index.html ] && echo YES || echo NO)" >&2
```
### 3. Tarkista kutsuparametrit
Yleisin virhe: skripti odottaa `$1` = X, mutta kutsuja antaa `$1` = Y ja `$2` = X.
Skripti lukee väärän parametrin ja etsii dataa väärästä paikasta.
### 4. Tarkista tiedostopolut
1. Onko lähdetiedosto olemassa ennen kopiointia?
2. Onko kohde olemassa kopioinnin jälkeen?
3. Onko `index.html` subdirissä (vaaditaan `ci-report.sh`:lle)?
Jos vastaus johonkin on "ei" — tiedät mikä pitää korjata.
### 5. Poista debug-echot kun ongelma on korjattu
Debug-rivit eivät kuulu tuotantoon.
### 6. Älä kokeile — debuggaa
Kokeilu = arvaus. Debuggaus = lisää echo, aja, lue logi, eristä ongelma.
Vasta sitten korjaa. Tämä on nopeampi tie oikeaan ratkaisuun.
## 8. Nimeäminen
Tiedostonimet `.gitea/workflows/`-kansiossa noudattavat yhtenäistä rakennetta, jotta
tiedostot löytyvät nopeasti ja niiden rooli on selvillä:
@@ -261,7 +361,7 @@ Single repossa `<komponentti>` jätetään pois — tiedostot ovat suoraan `ci-f
Monorepossa prefiksi pitää komponentin tiedostot yhdessä: `ls <komponentti>.*` löytää kaikki
kerralla.
## 7. Artifact-kuri
## 9. Artifact-kuri
Gitea Actionsin `upload-artifact` jättää pysyvän tiedoston. Artifakteja ei käytetä
workflow_call:ien väliseen datan siirtoon ellei se ole teknisesti välttämätöntä.
@@ -457,8 +557,9 @@ Gitean Settings → Branches → Add Rule:
| Skripti | Käyttötarkoitus |
|---|---|
| `report-status.sh` | POSTaa commit-statuksen linkillä |
| `publish-git-pages.sh` | Julkaisee raporttihakemiston git-pagesiin |
| `ci-report.sh` | Yhdistetty raportointi: julkaisee git-pagesiin ja asettaa commit-statuksen. Korvaa erilliset `publish-git-pages.sh` + `report-status.sh` -kutsut. Käyttö: `bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite>` |
| `report-status.sh` | POSTaa commit-statuksen linkillä (kutsutaan `ci-report.sh`:n sisältä) |
| `publish-git-pages.sh` | Julkaisee raporttihakemiston git-pagesiin (kutsutaan `ci-report.sh`:n sisältä) |
| `ci-validate.sh` | Validoi `.conf`-tiedoston (kutsutaan `config-provider.yml`:stä) |
---
@@ -499,7 +600,7 @@ Ei pipeä (`|`) komennon perässä — se syö exit-koodin. Käytä redirectiä
### Commit-status vain raporttilinkille (ADR 0007)
`report-status.sh`-skriptiä käytetään VAIN kun on raportti linkitettäväksi.
`ci-report.sh`-skriptiä käytetään VAIN kun on raportti linkitettäväksi.
Tool-jobit (build, deploy) luottavat Gitean natiiviin job-statukseen.
### Providerin checkout ei kuulu consumerille