--- 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. Katso tarkat mallipohjat ja esimerkit `REFERENCE.md`:stä. ## 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. Katso täydellinen esimerkki `REFERENCE.md`:stä. ## 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`):** `|` syö exit-koodin. Käytä redirectiä `>`. **Yksi asia per step:** Älä koskaan niputa useaa komentoa samaan `run:`-blockiin. `bash -e` pysäyttää koko stepin ensimmäisellä failaavalla komennolla, ja loput jäävät ajamatta. Sama pätee post-process-steppeihin. ## 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ä. 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`. ### 4.1 Offline Container -vaatimus (DoD) CI-kontin (ja kaikkien pipeline-konttien) on oltava täysin itseriittoisia: > Kontti ei lataa mitään pipeline-vaiheessa (`workflow run` -stepit) eikä > kontin runtime-prosessissa (`container:` / `docker run`). Kaikki > riippuvuudet pre-cachataan `docker build` -vaiheessa. > Ainoa sallittu lataushetki on `docker build`. **Esimerkkejä rikkomuksista:** - `apk add`, `apt-get install`, `npm install`, `go mod download`, `pip install` pipeline-stepissä - `curl | tar xz` runtime-vaiheessa - Node.js-konttikuva ilman nodea (joudutaan asentamaan lennossa) ### 4.2 Kielikohtainen pre-cache Kun kontissa testataan kielikohtaista koodia, kaikki riippuvuudet on pre-cachattava Dockerfilessä, ei pipeline-stepissä: | Kieli | Pre-cache Dockerfilessä | |---|---| | Go | `COPY go.mod go.sum ./` → `RUN go mod download` | | Java/Maven | `COPY pom.xml ./` → `RUN mvn dependency:go-offline` | | Node | `COPY package.json package-lock.json ./` → `RUN npm ci --omit=dev` | | Python | `COPY requirements.txt ./` → `RUN pip install -r requirements.txt` | Katso tarkat Dockerfile-esimerkit `REFERENCE.md`:stä. ### 4.3 CI-kontin ajaminen jobissa Ainoa sallittu tapa on `container:`-direktiivi. `docker run` komennolla kontin käynnistäminen stepin sisällä on anti-pattern. Katso CI-kontin template `REFERENCE.md`:stä. **Huomio `actions/checkout@v4`:stä:** `container:`-direktiivillä kaikki stepit ajetaan kontin *sisällä* — myös `actions/checkout@v4`. Se on JavaScript-action joka vaatii sekä `nodejs` että `git`. Varmista että CI-kontin Dockerfilessä on molemmat — muuten checkout ei toimi ja pipeline failaa. ### 4.4 Build-konteksti, `.dockerignore` ja `COPY` **Build-konteksti** on aina tiedoston (Dockerfile, Chart.yaml) oman hakemiston juuri (`dirname "${DOCKERFILE}"` / `dirname "${CHART_FILE}"`). Kaikki suhteelliset polut — ignore-tiedosto, `COPY`, `ADD` — ovat suhteessa tähän kontekstiin. | Tiedosto | Konteksti | Ignore-tiedosto | Käyttö | |---|---|---|---| | `Dockerfile` | `.` | `./.dockerignore` | `docker build` / `COPY src/ src/` | | `api/Dockerfile` | `api/` | `api/.dockerignore` | `docker build` / `COPY src/ src/` | | `Chart.yaml` (`VERSION_FILE`) | `.` | `./.helmignore` | `helm package` | | `api/Chart.yaml` (`VERSION_FILE`) | `api/` | `api/.helmignore` | `helm package` | Helm chartin polku luetaan confin `VERSION_FILE`-kentästä — sama rivi jota `check-version.yml` käyttää version lähteenä. Yksi conf-rivi ohjaa molempia: sekä versionlaskentaa että chartin sijaintia. **Mitä ignore-tiedosto sisältää:** Kaikki mikä EI ole konttiin tai chart-pakettiin tarkoitettua koodia tai resurssia, ON oltava ignore-tiedostossa: - Git- ja CI-historia (`.git/`, `.gitea/`, `.github/`) - Testikoodi, testidata, testiraportit (`tests/`, `reports/`, `coverage/`) - Dokumentaatio (`docs/`, `guides/`, `*.md`, `CHANGELOG`, `README`) - Editori- ja työkalukonfiguraatio (`.vscode/`, `.cursor/`, `.idea/`, `.DS_Store`) - Riippuvuudet jotka asennetaan Dockerfilessä (`node_modules/`) - Väliaikaistiedostot (`tmp/`, `*.log`) - Projektikohtaiset konfiguraatiot (`.env`, `*.conf`, `CURRENT_PROVIDER_VERSION`) **Miksi:** Build-kontekstin koko vaikuttaa suoraan `docker build` -nopeuteen. Raskas konteksti (etenkin `.git/` ja `node_modules/`) hidastaa buildia ja kuluttaa runnerin resursseja turhaan. Ylimääräiset tiedostot kontissa ovat **tietoturvariski** — tokenit, `.env` ja sensitiivinen data voivat päätyä kontin layeriin jos `.dockerignore` ei ole kattava. ### 4.5 `COPY`-kuri — kopioi vain tarvittava `COPY . .` on kielletty. Jokainen `COPY` kopioi vain tarvittavat tiedostot tai hakemistot: ```dockerfile # VÄÄRIN COPY . . # OIKEIN COPY package.json package-lock.json ./ COPY src/ src/ COPY public/ public/ ``` **Miksi:** - Layer-cache: `COPY . .` rikkoo välimuistin — mikä tahansa muutos tiedostossa tyhjentää koko layerin - Tietoturva: konttiin voi päätyä ylimääräisiä tiedostoja vaikka `.dockerignore` olisi kattava (unohtunut ignore-rivi, uusi työkalu joka luo tiedostoja build-kontekstiin) - Luettavuus: `COPY . .` ei kerro mitä kontti todella sisältää - Kontin koko: eksplisiittinen `COPY` pitää image-koon kurissa ### 4.6 `.helmignore` — pidä chart-paketti siistinä `helm package` käyttää `.helmignore`-tiedostoa samalla periaatteella kuin `docker build` käyttää `.dockerignore`a: - Chart-hakemisto luetaan confin `VERSION_FILE`-kentästä (`dirname "${VERSION_FILE}"`) - ignore-tiedosto luetaan chart-hakemiston juuresta (sama konteksti kuin `Chart.yaml`, ks. 4.4) - Kaikki turha (testit, docs, git, CI-konffit, kuvat) on poissuljettava - Jos `.helmignore` puuttuu, `helm package` paketoi mukaan kaikki chart-hakemiston tiedostot — turhaa bulkkia registryyn **`.helmignore` on pakollinen** jokaiselle chartille. Minimisisältö: ``` .git/ .gitignore tests/ docs/ *.md .DS_Store ``` ## 5. Raporttitasot Testi tuottaa raportin `reports//`-hakemistoon. Yksi `ci-report.sh`-kutsu hoitaa sekä julkaisun että commit-statuksen. ### Taso 1: Ei jälkikäsittelyä Kun testi tuottaa raportit suoraan (kuten `pytest --html` tai `cucumber-js --format html`): - testi kirjoittaa `reports//`-hakemistoon - `ci-report.sh` julkaisee ja asettaa commit-statuksen ### Taso 2: Jälkikäsittely tarvitaan Kun testi tuottaa raakadataa (stdout, coverage-tiedostot) joka pitää muuntaa tai siirtää `reports//`-hakemistoon. **Jokainen operaatio omassa stepissään** `if: always()`. Tarkat YAML-mallit molemmista tasoista: `REFERENCE.md`. **Subdir-sääntö:** Alihakemisto näkyy indexissä VAIN jos se sisältää `index.html`:n. ## 6. Nimeäminen Tiedostonimet `.gitea/workflows/`-kansiossa noudattavat yhtenäistä rakennetta: ``` .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. Monorepossa prefiksi pitää komponentin tiedostot yhdessä. ### 6.1 Commit status -nimeäminen `ci-report.sh`-kutsun `description` (2. argumentti) ja `context` (3. argumentti) noudattavat seuraavaa kaavaa: **Single repo:** ``` context: (esim. unit-tests, acc-tests) description: test report (esim. Unit test report) ``` **Monorepo:** ``` context: . (esim. library.unit-tests) description: : test report (esim. Library: Unit test report) ``` > Gitea YAML: `run:` laita lainausmerkeillä `run: |`-blockiin — Gitea ei tue lainausmerkkejä yhden rivin `run:`-komennoissa. > > ```yaml > - name: Report > if: always() > run: | > bash .ci/scripts/ci-report.sh ": test report" . ${{ job.status }} > ``` Build/push-status (Docker, Helm) on providerin hallussa — consumer ei vaikuta niiden nimeämiseen. ## 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. ## 8. Report-Summary — pakollinen jokaisen pipelinen lopuksi Jokaisen reitittimen (oli se `ci-main.yml`, `ci-feature.yml` tai mikä tahansa) viimeinen job on `report-summary`. **Säännöt:** - `needs:` sisältää **kaikki edeltävät jobit** — summary odottaa että kaikki on valmis (onnistui tai ei) - `if: always()` — ajetaan aina, vaikka pipeline olisi keskeytetty tai joku jobi failannut - `suites:` on välilyönnein eroteltu lista suiten nimistä (esim. `bats cucumber`). Tyhjä merkkijono sallittu jos testisuiteja ei ole. - Provider (`report-summary.yml`) hoitaa summaryn logiikan — reititin vain kutsuu **Miksi aina:** - Gitea 1.27+ näyttää `GITHUB_STEP_SUMMARY`:n Actions UI:ssa. Ilman summarya pipeline näyttää epätäydelliseltä. - Summaryyn voidaan myöhemmin lisätä muutakin kuin testilinkkejä (build-artefaktit, deploy-tiedot). - Yhtenäinen rakenne jokaisessa pipeline-parissa vähentää kysymyksiä. YAML-malli: `REFERENCE.md`. ## 9. 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@`. 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. ### Exit-koodi on ainoa onnistumisen mittari (ADR 0008) Ei pipeä (`|`) komennon perässä — se syö exit-koodin. Käytä redirectiä (`> file 2>&1`). ### Providerin checkout ei kuulu consumerille Providerin scriptit haetaan `actions/checkout`-stepillä `.ci/`-polkuun. Consumer ei kopioi eikä muokkaa providerin tiedostoja. ## 10. Build & Push -providerit ### `docker-build-push.yml` — Docker image build & push Buildaa ja pushee Docker-imagen OCI-registryyn. Ajaa suoraan runnerilla (ei `container:`-direktiiviä), joten `actions/checkout` toimii natiivisti. **`env_json`-avaimet (pakolliset):** ```yaml DOCKER_REGISTRY: gitea.app.keskikuja.site/niko DOCKER_IMAGE_NAME: my-app ``` **Käyttö reitittimessä:** ```yaml docker-build-push: uses: OWNER/gitea-ci-library/.gitea/workflows/docker-build-push.yml@v1 needs: [check-version] if: needs.check-version.outputs.artifact_exists == 'false' secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} version: ${{ needs.check-version.outputs.version }} ``` Tarkka input/secret-lista: `docs/workflows.md`. ### `helm-build-push.yml` — Helm chart build & push Pakkaa ja pushee Helm-chartin OCI-registryyn. Käyttää `alpine/helm`-konttia. **`env_json`-avaimet (pakolliset):** ```yaml HELM_REGISTRY: gitea.app.keskikuja.site/niko VERSION_FILE: platform-helm/Chart.yaml # versionlähde, chart_path määrää chart-hakemiston ``` **Inputit:** | Parametri | Pakollinen | Kuvaus | |-----------|------------|--------| | `env_json` | Kyllä | Konffi `gitea-env.conf`:stä | | `version` | Kyllä | Version string (check-version output) | | `chart_path` | Kyllä | Polku Chart.yaml-hakemistoon (esim. `platform-helm`) | | `extra_dependency_paths` | Ei | Pilkulla erotellut polut subcharttien dependeinceille | **Käyttö reitittimessä:** ```yaml helm-build-push: uses: OWNER/gitea-ci-library/.gitea/workflows/helm-build-push.yml@v1 needs: [check-version] if: needs.check-version.outputs.artifact_exists == 'false' secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} version: ${{ needs.check-version.outputs.version }} chart_path: platform-helm # extra_dependency_paths: subchart-a,subchart-b # tarvittaessa ``` `chart_path` on eksplisiittinen polku chart-hakemistoon (esim. `platform-helm`). `VERSION_FILE` määrää version lähteen (`Chart.yaml:n` `version`-kenttä) — nämä voivat olla eri polkuja, mutta tyypillisesti molemmat osoittavat samaan chart-hakemistoon. **`extra_dependency_paths`:** Jos chartilla on alikarttoja (subchartteja) jotka vaativat `helm dependency update` -ajon ennen päächartin buildia, anna niiden polut pilkulla eroteltuna. Provider ajaa `helm dependency update` jokaiselle polulle ennen päächartin buildia. **Yksittäisten Helm-UI-linkkien raportointi:** `HELM_UI_URL` on tarkoitettu yleiselle registry UI:lle — provider muodostaa linkin `${HELM_UI_URL}/${CHART_NAME}/${VERSION}` automaattisesti. Tarkka input/secret-lista: `docs/workflows.md`. ## 11. Multi-artifact monorepo -komponentti Yksi monorepo-komponentti voi tuottaa useita artefakteja (esim. Docker image + Helm chart). Kukin artefakti on **omassa reitittimessään** — ei yhtä monoliittista pipelinea. Tämä on tietoinen arkkitehtuurivalinta: - Reitittimet ovat itsenäisiä: eri `paths:`-triggerit, eri tagit, eri confit - Yksi commit voi triggeröidä molemmat rinnakkain - Yhden artefaktin build tai testi ei estä toista ### Esimerkki: `platform-helm` joka tuottaa Docker-imagen ja Helm chartin ``` .gitea/workflows/ ├── platform-helm.ci-main.yml # Docker build & push ├── platform-helm.gitea-env.conf # Docker-konffi ├── platform-helm.helm-ci-main.yml # Helm build & push ├── platform-helm.helm-gitea-env.conf # Helm-konffi ├── platform-helm.helm-chart-lint.yml # Chart-testi └── platform-helm.ci-container-build-helm.yml # CI-kontin build ``` ### `platform-helm.gitea-env.conf` (Docker) ```ini DOCKER_REGISTRY=gitea.app.keskikuja.site/niko DOCKER_IMAGE_NAME=platform-helm GIT_TAG_PREFIX=platform-helm/ ``` ### `platform-helm.helm-gitea-env.conf` (Helm) ```ini HELM_REGISTRY=gitea.app.keskikuja.site/niko VERSION_FILE=platform-helm/Chart.yaml GIT_TAG_PREFIX=chart/ ``` ### Reitittimet **`platform-helm.ci-main.yml`** — Docker-buildi, testit, oma tagi: ```yaml name: platform-helm CI Main on: push: branches: [main] paths: - platform-helm/** - .gitea/workflows/platform-helm.* jobs: load-config: uses: OWNER/gitea-ci-library/.gitea/workflows/config-provider.yml@v1 secrets: inherit with: config_path: .gitea/workflows/platform-helm.gitea-env.conf check-version: needs: [load-config] uses: OWNER/gitea-ci-library/.gitea/workflows/check-version.yml@v1 secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} test: needs: [load-config, check-version] uses: ./.gitea/workflows/platform-helm.sbom-lint.yml secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} build-push: needs: [load-config, check-version, test] if: needs.check-version.outputs.artifact_exists == 'false' uses: OWNER/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 }} report-summary: needs: [load-config, test, build-push] if: always() uses: OWNER/gitea-ci-library/.gitea/workflows/report-summary.yml@v1 with: env_json: ${{ needs.load-config.outputs.env_json }} suites: '' ``` **`platform-helm.helm-ci-main.yml`** — Helm-buildi, chart-testi, oma tagi: ```yaml name: platform-helm Helm CI Main on: push: branches: [main] paths: - platform-helm/** - .gitea/workflows/platform-helm.helm* jobs: load-config: uses: OWNER/gitea-ci-library/.gitea/workflows/config-provider.yml@v1 secrets: inherit with: config_path: .gitea/workflows/platform-helm.helm-gitea-env.conf check-version: needs: [load-config] uses: OWNER/gitea-ci-library/.gitea/workflows/check-version.yml@v1 secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} chart-lint: needs: [load-config, check-version] uses: ./.gitea/workflows/platform-helm.helm-chart-lint.yml secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} helm-build-push: needs: [load-config, check-version, chart-lint] if: needs.check-version.outputs.artifact_exists == 'false' uses: OWNER/gitea-ci-library/.gitea/workflows/helm-build-push.yml@v1 secrets: inherit with: env_json: ${{ needs.load-config.outputs.env_json }} version: ${{ needs.check-version.outputs.version }} report-summary: needs: [load-config, chart-lint, helm-build-push] if: always() uses: OWNER/gitea-ci-library/.gitea/workflows/report-summary.yml@v1 with: env_json: ${{ needs.load-config.outputs.env_json }} suites: '' ``` ### Säännöt - Jokaisella artefaktilla on oma reititin, oma conf, omat testit - Conf-tiedoston nimi erottaa artefaktit: `.gitea-env.conf` vs `.helm-gitea-env.conf` - `.helm-`-prefiksi erottaa Helm-artefaktin tiedostot - `GIT_TAG_PREFIX` pitää tagit erillään: `platform-helm/1.2.3` vs `chart/1.2.3` - Molemmat reitittimet voivat triggeröityä samasta commitista