--- 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. ```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-extraktio, 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: - name: Post-process test report if: always() run: - name: Report if: always() run: bash .ci/scripts/ci-report.sh "" ``` **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. **Malli:** `example-bats-tests.yml`. ### 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 ├── junit.xml ← testin JUnit XML -output └── junit.html ← generoitu HTML (xsltproc, tms.) ``` ## 6. 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. ## 7. 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.