--- 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@main secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} : needs: [load-config] uses: ./.gitea/workflows/..yml@main 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 Jokainen testi kaappaa komentonsa exit-koodin eksplisiittisesti: ```yaml - name: Run tests shell: bash run: | > results.txt 2>&1 EXIT=$? echo "EXIT=${EXIT}" >> "${GITHUB_ENV}" exit ${EXIT} ``` **Miksi ei pipeä (`| tee`):** ```bash # VÄÄRIN — pipe syö exit-koodin | tee results.txt # OIKEIN — redirect tiedostoon > 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. ## 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-push.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 Pre-buildattu kontti rakennetaan minimaalisella Dockerfilella, joka kopioi tarvitut binäärit perusimagesta ja asentaa vain välttämättömät paketit. ## 5. Raporttitasot Testi tuottaa raportin `reports/${GITHUB_SHA:0:8}//`-hakemistoon. `publish-git-pages.sh` julkaisee sen, `report-status.sh` linkittää commit-statusin siihen. Molemmat `if: always()`. ### Taso 1: Pelkkä teksti Kun testi tuottaa vain stdout/stderr — tallennetaan `results.txt`: ```yaml - name: Run tests shell: bash run: | mkdir -p "reports/${GITHUB_SHA:0:8}/" > "reports/${GITHUB_SHA:0:8}//results.txt" 2>&1 EXIT=$? echo "EXIT=${EXIT}" >> "${GITHUB_ENV}" exit ${EXIT} - name: Publish reports if: always() shell: bash run: bash .ci/scripts/publish-git-pages.sh - name: Report status if: always() shell: bash run: | if [ "${EXIT}" = "0" ]; then bash .ci/scripts/report-status.sh success "" else bash .ci/scripts/report-status.sh failure "" fi ``` ### Taso 2: HTML-raportti Kun testi tuottaa strukturoitua dataa (JUnit XML, coverage, tms.) — generoidaan HTML ja `index.html`: ``` reports/// ├── index.html ← generoitu: linkit alla oleviin ├── results.txt ← testin stdout ├── junit.xml ← testin JUnit XML -output └── junit.html ← generoitu HTML (xsltproc, tms.) ``` `index.html` linkittää kaikkiin raporttitiedostoihin. Selain avaa sen ja navigoi sieltä yksittäisiin raportteihin. ## 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 .gitea-env.conf ← komponenttikohtainen konfiguraatio ``` Single repossa `` jätetään pois — tiedostot ovat suoraan `ci-feature.yml`, `ci-main.yml`, `.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@main 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 | |---|---| | `report-status.sh` | POSTaa commit-statuksen linkillä | | `publish-git-pages.sh` | Julkaisee raporttihakemiston git-pagesiin | | `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ä. ### 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) `report-status.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.