--- name: consumer-pipelines description: | Creating or modifying consumer CI pipelines, .gitea/workflows/ files, reusable test workflows, monorepo CI configuration, or CI routing files (ci-feature.yml, ci-main.yml, ci-*.yml). Activates when the user asks to build, fix, or change consumer-side Gitea Actions pipelines that use gitea-ci-library providers. activation-gate: | User mentions consumer pipelines, ci-feature.yml, ci-main.yml, test workflows, .gitea/workflows/ files, monorepo CI, routing files, or asks to create/modify CI pipelines on top of gitea-ci-library. category: ci impact: high --- # Consumer Pipelines — Pipeline Standards Säännöt joilla consumer-projektit rakentavat CI-pipelinejä `gitea-ci-library`-kirjaston päälle. Nämä eivät ole provider-kirjaston sääntöjä — ne kuvaavat miten consumerin kuuluu käyttää kirjastoa oikein. ## 1. Reitittimen puhtaus Reitittimet (`ci-feature.yml`, `ci-main.yml`) eivät sisällä `run:`-steppejä. Ne koostuvat vain: ```yaml uses: needs: if: secrets: inherit with: env_json: : ``` Jokainen job vastaa yhtä loogista testiä tai operaatiota. Reititin on orkestraattori — kaikki suorittava logiikka on omassa `workflow_call`-tiedostossaan. **Esimerkki:** ```yaml jobs: load-config: uses: /gitea-ci-library/.gitea/workflows/config-provider.yml@v1 secrets: inherit : needs: [load-config] uses: ./.gitea/workflows/..yml secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} : needs: [load-config] uses: ./.gitea/workflows/..yml secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} report-summary: needs: [load-config, , ] if: always() uses: /gitea-ci-library/.gitea/workflows/report-summary.yml@v1 with: env_json: ${{ needs.load-config.outputs.env_json }} suites: ``` ## 2. Yksi asia per tiedosto Ei monoliittista `ci-tests.yml`. Jokainen testityyppi tai operaatio on oma `workflow_call`-tiedostonsa. **Miksi:** - Testit ajetaan rinnakkain (ei keinotekoisia riippuvuuksia) - Yhden testin fail ei estä muita - Testattavissa itsenäisesti `workflow_dispatch`:llä - Diff näyttää heti mitä testiä muutettiin ## 3. Exit-koodin käsittely `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: | > results.txt 2>&1 ``` **Miksi ei pipeä (`| tee`):** ```bash # VÄÄRIN — pipe syö exit-koodin | tee results.txt # OIKEIN — redirect tiedostoon > results.txt 2>&1 ``` `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 1. **Julkiset registry-kontit kiinteällä versiolla** — `alpine/helm:3.19.0`, `node:22`, `maven:3.9-eclipse-temurin-21`. Toistettavuus ja turvallisuus eivät saa riippua ulkoisesta `latest`:sta 2. **Projektin omat CI-kontit `latest`-tägillä** — buildattu `ci-container-build-.yml`:llä. Kontin build-pipeline päivittää `latest`:n automaattisesti. Rebuild = käyttöönotto kaikissa pipelineissa ilman versioviittauksien päivittelyä. `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 CI-kontin build-workflow'n template: [skills/ci-container-build/SKILL.md](../ci-container-build/SKILL.md) — sisältää valmiin `ci-container-build-.yml`-pohjan jossa `workflow_dispatch`-tuki manuaaliajoon. ### 4.1 CI-kontin ajaminen jobissa 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 jobs: : runs-on: ubuntu-latest container: image: ${{ inputs. }} steps: - uses: actions/checkout@v4 - uses: actions/checkout@v4 with: repository: /gitea-ci-library path: .ci - name: Run shell: bash run: | mkdir -p "reports/" > "reports//results.txt" 2>&1 - name: Post-process reports if: always() run: | - name: Report if: always() run: bash .ci/scripts/ci-report.sh "" ``` 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//`-hakemistoon. Yksi `ci-report.sh`-kutsu hoitaa sekä julkaisun että commit-statuksen — erillistä Publish + Report Status -kaksivaiheisuutta ei tarvita. ### Taso 1: Ei jälkikäsittelyä Kun testi tuottaa raportit suoraan (kuten `pytest --html` tai `cucumber-js --format html`): ```yaml - name: Run tests shell: bash run: | mkdir -p "reports/" - name: Report if: always() run: bash .ci/scripts/ci-report.sh "" ``` ### Taso 2: Jälkikäsittely tarvitaan Kun testi tuottaa raakadataa (stdout, coverage-tiedostot) joka pitää muuntaa tai siirtää `reports//`-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: | mkdir -p "reports/" > "reports//results.txt" 2>&1 - name: Post-process coverage if: always() run: /coverage/-hakemistoon> - name: Post-process test report if: always() run: - name: Report if: always() run: bash .ci/scripts/ci-report.sh "" ``` **Huomio subdir-sisällöstä:** Jos testi tuottaa dataa alihakemistoon (esim. coverage `./coverage/`-kansioon), se pitää erikseen SIIRTÄÄ `reports///`-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. **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//`-hakemistossa on useita tiedostoja tai alihakemistoja, `ci-report.sh` generoi automaattisesti `reports//index.html` jos hakemistossa on enemmän kuin yksi raportoitava item. ``` reports// ├── results.txt ← testin stdout (skannataan FILES) ├── test-report.html ← generoitu HTML (skannataan FILES) └── / ← alihakemisto (skannataan SUBDIRS) └── index.html ← VAIN jos tämä on olemassa ``` **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. Raportin julkaisukelpoisuus `ci-report.sh` päättää onko raportti julkaisukelpoinen skannaamalla `reports//`-hakemistoa. ### Mitä skannataan | Mitä | Sääntö | |---|---| | **Tiedostot (FILES)** | Kaikki `reports//`-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//index.html`-sivun, linkit kaikkiin itemeihin | ### Esimerkki: coverage-näkymä ``` reports//coverage/index.html ← on olemassa ``` Coverage-dataa ei siirretä automaattisesti. Testin tai post-process-stepin pitää siirtää coverage `reports//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ä: ``` .ci-feature.yml ← feature-haaran reititin .ci-main.yml ← main-haaran reititin ..yml ← yksittäinen testi tai operaatio .ci-container-build-.yml ← CI-kontin build-workflow .gitea-env.conf ← komponenttikohtainen konfiguraatio ``` Single repossa `` jätetään pois — tiedostot ovat suoraan `ci-feature.yml`, `ci-main.yml`, `.yml`, `ci-container-build-.yml`. Monorepossa prefiksi pitää komponentin tiedostot yhdessä: `ls .*` löytää kaikki kerralla. ## 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ä. **Ensisijainen ratkaisu:** jokainen testi tuottaa tarvitsemansa datan itse. Ei `upload-artifact` + `download-artifact` -riippuvuuksia. ```yaml # OIKEIN — molemmat testit tuottavat oman datansa - name: Prepare data run: > /tmp/data - name: Validate data run: /tmp/data ``` **Miksi:** - Testit pysyvät itsenäisinä — yhden testin fail ei estä muita - Ei "artifact expired" -virheitä myöhemmin - Ei pysyviä artifakteja siivoamatta --- ## Konfiguraatiotiedosto (.gitea-env.conf) Tiedosto on `key=value`-muotoinen (kuten `.env`). Kommentit ja tyhjät rivit sallittuja. ### Single repo ```ini # .gitea/workflows/gitea-env.conf GITEA_API_URL=https://gitea.example.com GIT_PAGES_URL=https://reports.example.com ``` ### Docker-artifaktin buildaavat projektit ```ini DOCKER_REGISTRY=gitea.example.com/myorg DOCKER_IMAGE_NAME=my-service DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container #DOCKERFILE=Dockerfile.platform # valinnainen, oletus Dockerfile ``` `DOCKER_UI_URL` ei sisällä image-nimeä — se on puhdas container-registryn osoite. Image-nimi lisätään automaattisesti URL:iin `docker-build-push.yml`:ssä. ### Salaisuudet (Gitea Settings → Secrets) | Secret | Pakollinen | |---|---| | `GITEA_TOKEN` | Aina (Gitean sisäinen, automaattisesti saatavilla) | | `GIT_PAGES_PUBLISH_TOKEN` | Aina | | `DOCKER_USERNAME` | Vain jos buildaat kontteja | | `DOCKER_PASSWORD` | Vain jos buildaat kontteja | --- ## Monorepo Monorepossa yhdessä repossa asuu useampi julkaistava komponentti. Jokaiselle komponentille oma conf-tiedosto `.gitea/workflows/.gitea-env.conf`, jossa on kaikki komponenttikohtainen tieto. ### Suositus: komponentit omiin juurihakemistoihin On suositeltavaa sijoittaa jokaisen komponentin koko lähdekoodi omaan juuritason hakemistoonsa (`api/`, `frontend/`, `shared/`). Tämä helpottaa `paths:`-filtteröintiä, pitää komponentit selkeästi erillään, ja tekee repossa navigoinnista suoraviivaista. Tämä on kuitenkin vain suositus — ei pakottava sääntö. ### Ongelmat ja ratkaisut | Ongelma | Ratkaisu | |---|---| | Monta komponenttia, yksi repo — mikä triggeröi? | `paths:`-filtteri: `push: { paths: ['/**'] }` | | Jokaisella komponentilla oma versio | `VERSION_FILE=/package.json` confissa | | Git-tägit sekaisin ellei nimiavaruutta | `GIT_TAG_PREFIX=/` confissa → tägi `/1.2.3` | | Eri julkaisutahdit | Riippumattomat CI-triggerit, omat versiopolut | ### Komponenttikohtainen conf ```ini # .gitea/workflows/.gitea-env.conf GITEA_API_URL=https://gitea.example.com GIT_PAGES_URL=https://reports.example.com DOCKER_REGISTRY=gitea.example.com/myorg DOCKER_IMAGE_NAME= DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container GIT_TAG_PREFIX=/ # Jompikumpi — JSON (.version-kenttä) tai plain text: VERSION_FILE=/package.json #VERSION_FILE=/VERSION ``` ### Monorepo reititin ```yaml name: CI Main on: push: branches: - main paths: - '/**' jobs: load-config: uses: /gitea-ci-library/.gitea/workflows/config-provider.yml@v1 secrets: inherit with: config_path: .gitea/workflows/.gitea-env.conf check-version: needs: [load-config] uses: /gitea-ci-library/.gitea/workflows/check-version.yml@v1 secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} : needs: [load-config, check-version] if: needs.check-version.outputs.artifact_exists != 'true' uses: ./.gitea/workflows/..yml secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} build-push: needs: [load-config, check-version, ] if: needs.check-version.outputs.artifact_exists != 'true' uses: /gitea-ci-library/.gitea/workflows/docker-build-push.yml@v1 secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} version: ${{ needs.check-version.outputs.version }} ``` ### Version elinkaari per komponentti `GIT_TAG_PREFIX` takaa että eri komponenttien versiohistoria pysyy erillään. Git-tägi `/0.2.3` ei sekoitu toisen komponentin tägeihin. `check-version.yml` suodattaa ja laskee seuraavan patchin vain kyseisen komponentin etuliitteellä. Idempotenttius toimii komponenttikohtaisesti: jos commitilla on jo tägi, pipeline skipataan `if: artifact_exists != 'true'`. ### Mitä EI kannata tehdä monorepossa - Älä aja kaikkia komponentteja samasta triggeristä — `paths:` pitää CI:t erillisinä - Älä käytä samaa versionhallintatiedostoa usealle komponentille - Älä anna monorepo-parametreja pipeline-overrideina — kaikki kuuluu conf-tiedostoon --- ## Versionhallinta `check-version.yml` lukee version automaattisesti prioriteettijärjestyksessä: | # | Lähde | Formaatti | |---|---|---| | 1 | `VERSION_FILE` confissa | Määritelty polku | | 2 | `VERSION`-tiedosto (root) | Plain text | | 3 | `package.json` (root) | `.version`-kenttä | | 4 | `pom.xml` (root) | ``-elementti | `major.minor` otetaan tästä. Patch lasketaan automaattisesti git-tageista. Esim. `VERSION` = `0.2`, tagit = `0.2.0`, `0.2.1` → seuraava `0.2.2`. --- ## Branch protection (PR-gate) Gitean Settings → Branches → Add Rule: - **Branch:** `main` - **Enable Require Status Checks:** päälle - **Status checks:** valitse testijobien nimet --- ## Provider-rajapinnat — referenssi ### Workflowt | Workflow | Käyttötarkoitus | |---|---| | `config-provider.yml` | Lataa + validoi `.conf`, tuottaa `env_json` | | `check-version.yml` | Tarkistaa onko commit buildattu, laskee version | | `docker-build-push.yml` | Buildaa + puskea Docker-imagen, tagittaa commitin | | `report-summary.yml` | `GITHUB_STEP_SUMMARY`-taulukko raporttilinkeillä (Gitea 1.27+) | ### Skriptit (kutsutaan `.ci/scripts/`-polun kautta) | Skripti | Käyttötarkoitus | |---|---| | `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 "" ` | | `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ä) | --- ## ADR-yhteenveto — consumerin kannalta oleelliset säännöt ### Reititin ei sisällä suorittavaa koodia (ADR 0010) `ci-feature.yml` ja `ci-main.yml` koostuvat **vain** `uses:`, `needs:` ja `if:`-avainsanoista. Ei `run:`-komentoja, ei inline-skriptejä, ei `actions/checkout`. ### Yksi steppi = yksi workflow_call-tiedosto Jokainen job reitittimessä on oma `workflow_call`-tiedostonsa. Ei kahta eri komentoa samassa workflow'ssa. ### Provider-versio on `@v1` (ADR 0009) Kaikki provider-viittaukset käyttävät `@v1`-tagia. `@main` on vain providerin oman repon sisäiseen dogfood-käyttöön. Breaking changet kielletty — `v1`-rajapinta on pysyvä. ### Paikalliset `uses:` eivät käytä refiä Gitea act runner v1.0.8 muodostaa paikallisista `uses: ./.gitea/workflows/*.yml@main`-viittauksista epävalidin git-refin `main@`, joka aiheuttaa virheen `Revision invalid : reference must be defined once at the beginning`. Paikallisista `uses:`-direktiiveistä EI koskaan käytetä `@main`- tai muuta ref-päätettä: - `uses: ./.gitea/workflows/chart.helm-lint.yml` ← oikein - `uses: ./.gitea/workflows/chart.helm-lint.yml@main` ← väärin Ilman refiä runner käyttää workflow'ta triggeröivästä commitista. Ulkoisten repojen viittauksissa (`niko/...@v1`) pääte pysyy. Nämä resolvoidaan eri reittiä ja toimivat oikein. ### Exit-koodi on ainoa onnistumisen mittari (ADR 0008) Ei pipeä (`|`) komennon perässä — se syö exit-koodin. Käytä redirectiä (`> file 2>&1`). ### Commit-status vain raporttilinkille (ADR 0007) `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 Providerin scriptit haetaan `actions/checkout`-stepillä `.ci/`-polkuun. Consumer ei kopioi eikä muokkaa providerin tiedostoja.