Co-authored-by: moilanik <niko.moilanen@tietoevry.com> Reviewed-on: #34
17 KiB
Consumer Pipelines — Reference
Mallipohjat, esimerkit ja konfiguraatiot. Katso säännöt SKILL.md:stä.
Pre-cache-esimerkit (Offline Container)
Alla Dockerfile-esimerkit kielikohtaisista pre-cacheista. Kaikki ajetaan build-vaiheessa — kontti on täysin itseriittoinen eikä lataa mitään pipeline- tai runtime-vaiheessa.
Go
FROM golang:1.24-alpine AS deps
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
FROM deps AS test-build
COPY . .
RUN go test -c -o /tmp/test.bin ./...
FROM alpine:3.21
RUN apk add --no-cache git nodejs
COPY --from=deps /go/pkg/mod /go/pkg/mod
COPY --from=test-build /tmp/test.bin /usr/local/bin/test
Node.js
FROM node:22-alpine AS deps
WORKDIR /build
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
FROM node:22-alpine
RUN apk add --no-cache git
COPY --from=deps /build/node_modules /app/node_modules
COPY . /app
WORKDIR /app
Java / Maven
FROM maven:3.9-eclipse-temurin-21 AS deps
WORKDIR /build
COPY pom.xml ./
RUN mvn dependency:go-offline -B
FROM maven:3.9-eclipse-temurin-21 AS build
COPY --from=deps /root/.m2 /root/.m2
COPY . .
RUN mvn package -B -DskipTests
FROM eclipse-temurin:21-jre
RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/*
COPY --from=build /build/target/*.jar /app/app.jar
WORKDIR /app
Python
FROM python:3.12-alpine AS deps
WORKDIR /build
COPY requirements.txt ./
RUN pip wheel --wheel-dir=/wheels -r requirements.txt
FROM python:3.12-alpine
RUN apk add --no-cache git
COPY --from=deps /build/wheels /wheels
COPY --from=deps /build/requirements.txt /
RUN pip install --no-index --find-links=/wheels -r /requirements.txt && rm -rf /wheels
COPY . /app
WORKDIR /app
Helm + Node.js (korvaa helm-build-push.yml:n runtime-apk)
FROM alpine/helm:3.16.0 AS helm-bin
FROM node:22-alpine
RUN apk add --no-cache git
COPY --from=helm-bin /usr/bin/helm /usr/local/bin/helm
Tämä kontti korvaa helm-build-push.yml:n alpine/helm:3.19.0-image-riippuvuuden
ja poistaa tarpeen asentaa node.js runtime-vaiheessa.
Reititin — täydellinen esimerkki
jobs:
load-config:
uses: <owner>/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
secrets: inherit
<test-1>:
needs: [load-config]
uses: ./.gitea/workflows/<component>.<test-1>.yml
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
<test-2>:
needs: [load-config]
uses: ./.gitea/workflows/<component>.<test-2>.yml
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
report-summary:
needs: [load-config, <test-1>, <test-2>]
if: always()
uses: <owner>/gitea-ci-library/.gitea/workflows/report-summary.yml@v1
with:
env_json: ${{ needs.load-config.outputs.env_json }}
suites: <suite-1> <suite-2>
CI-kontin build — parametroitu workflow
CI-kontin build on workflow_dispatch-triggeröity job, joka näkyy Gitea Actionsissa kuten Jenkinsin
parametroitu job — käyttäjä antaa inputit UI:sta ennen ajoa.
name: CI Container Build <työkalu>
on:
workflow_dispatch:
inputs:
config_path:
required: true
type: string
default: '.gitea/workflows/<komponentti>.gitea-env.conf'
description: 'Polku .gitea-env.conf-tiedostoon'
dockerfile_path:
required: true
type: string
default: '<komponentti>/Dockerfile.ci-<työkalu>'
description: 'Polku Dockerfileen'
image_name:
required: true
type: string
default: 'ci-<työkalu>'
description: 'Kontin nimi ilman registry-polkua'
tag:
required: true
type: string
default: 'latest'
description: 'Image-tägi'
jobs:
load-config:
uses: <owner>/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
secrets: inherit
with:
config_path: ${{ inputs.config_path }}
build-push:
needs: [load-config]
uses: <owner>/gitea-ci-library/.gitea/workflows/ci-container-build-push.yml@v1
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
dockerfile_path: ${{ inputs.dockerfile_path }}
image_name: ${{ inputs.image_name }}
tag: ${{ inputs.tag }}
CI-kontin ajaminen testijobissa
Ainoa sallittu tapa consumer-puolella on container:-direktiivi. docker run komennolla
kontin käynnistäminen stepin sisällä on anti-pattern. container:-direktiivillä kaikki stepit
ajetaan samassa kontissa — tiedostot ovat suoraan filesystemillä eikä erillistä
volyyminhallintaa tarvita.
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/<suite>"
<komento> > "reports/<suite>/results.txt" 2>&1
- name: Post-process reports
if: always()
run: |
<mahdollinen_raporttien_jälkikäsittely>
- name: Report
if: always()
run: bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite> ${{ job.status }}
Usean runnerin cache-ongelma: Jos eri kerroilla käynnistyy eri runnereita,
niillä voi olla eri versio latest-imagen digesteistä. Ratkaisuja:
- Rebuildaa kontti ja aja
docker pull <image>manuaalisesti kaikilla runnereilla - Käytä versioitua tagia (
v2,v3, ...) ja päivitä workflow'n default buildauksen jälkeen
Mallit:
example-cucumber-tests.yml— ei post-processiaexample-bats-tests.yml— post-process coverage + report
Raporttitasot — tarkat YAML-mallit
Taso 1: Ei jälkikäsittelyä
- name: Run tests
shell: bash
run: |
mkdir -p "reports/<suite>"
<testikomento>
- name: Report
if: always()
run: bash .ci/scripts/ci-report.sh "<kuvaus>" <context> <suite> ${{ job.status }}
Taso 2: Jälkikäsittely tarvitaan
- name: Run tests
shell: bash
run: |
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> ${{ job.status }}
Väärin vs oikein — yksi asia per step
# VÄÄRIN — helm template fail → kubeconform jää ajamatta, report jää tekemättä
- name: Run tests
run: |
helm template ... > /tmp/manifests.yaml
kubeconform ... > results.txt 2>&1
# OIKEIN — erilliset stepit
- name: Helm template
run: helm template platform-helm/ -f values.yaml > /tmp/manifests.yaml 2>&1
- name: Kubeconform
if: success()
run: |
mkdir -p reports/kubeconform
kubeconform ... > reports/kubeconform/results.txt 2>&1
- name: Report
if: always()
run: bash .ci/scripts/ci-report.sh "Helm kubeconform" helm-test kubeconform ${{ job.status }}
Väärin vs oikein — post-process
# 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
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 |
Hakemistorakenne
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
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.
Provider vastuulla: ci-report.sh (provider-skripti) hoitaa sekä hakemistorakenteen
skannauksen, index.html-generoinnin että julkaisun git-pagesiin. Consumer tuottaa
vain raakatiedostot reports/<suite>/-hakemistoon — ci-report.sh päättää
julkaisukelpoisuuden ja generoi tarvittavan navigaation.
Debug-ohje: raportti ei näy
1. Aja lokaalisti samalla komennolla kuin CI
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
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.
4. Tarkista tiedostopolut
- Onko lähdetiedosto olemassa ennen kopiointia?
- Onko kohde olemassa kopioinnin jälkeen?
- Onko
index.htmlsubdirissä (vaaditaanci-report.sh:lle)?
5. Poista debug-echot kun ongelma on korjattu
6. Älä kokeile — debuggaa
Kokeilu = arvaus. Debuggaus = lisää echo, aja, lue logi, eristä ongelma. Vasta sitten korjaa.
Konfiguraatiotiedosto (.gitea-env.conf)
Tiedosto on key=value-muotoinen (kuten .env). Kommentit ja tyhjät rivit sallittuja.
Single repo
# .gitea/workflows/gitea-env.conf
GITEA_API_URL=https://gitea.example.com
GIT_PAGES_URL=https://reports.example.com
Docker-artifaktin buildaavat projektit
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ä.
Helm-artifaktin buildaavat projektit
HELM_REGISTRY=gitea.example.com/myorg
GIT_TAG_PREFIX=git-pages/
VERSION_FILE=git-pages/Chart.yaml
| Kenttä | Pakollinen | Kuvaus |
|---|---|---|
HELM_REGISTRY |
kyllä | Registry host + owner, esim. gitea.example.com/myorg. Tyhjä pysäyttää workflow'n. |
GIT_TAG_PREFIX |
ei | Etuliite git-tägille. Pakollinen monorepossa. |
VERSION_FILE |
ei | Polku version lähteeseen. Oletus: juuren Chart.yaml. |
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 |
HELM_USER |
Vain jos pushaat Helm chartin OCI-rekisteriin (oletus github.actor) |
HELM_PASSWORD |
Vain jos pushaat Helm chartin OCI-rekisteriin |
Monorepo
Monorepossa yhdessä repossa asuu useampi julkaistava komponentti. Jokaiselle komponentille
oma conf-tiedosto .gitea/workflows/<komponentti>.gitea-env.conf.
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.
Ongelmat ja ratkaisut
| Ongelma | Ratkaisu |
|---|---|
| Monta komponenttia, yksi repo — mikä triggeröi? | paths:-filtteri: komponentin hakemisto + sen CI-workflow't ja conf-tiedosto |
| Jokaisella komponentilla oma versio | VERSION_FILE=<komponentti>/package.json confissa |
| Git-tägit sekaisin ellei nimiavaruutta | GIT_TAG_PREFIX=<komponentti>/ confissa → tägi <komponentti>/1.2.3 |
| Eri julkaisutahdit | Riippumattomat CI-triggerit, omat versiopolut |
Komponenttikohtainen conf
# .gitea/workflows/<komponentti>.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=<image-nimi>
DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container
GIT_TAG_PREFIX=<komponentti>/
# Jompikumpi — JSON (.version-kenttä) tai plain text:
VERSION_FILE=<komponentti>/package.json
#VERSION_FILE=<komponentti>/VERSION
Monorepo reititin
name: CI <Komponentti> Main
on:
push:
branches:
- main
paths:
- <komponentti>/**
- .gitea/workflows/<komponentti>.*
jobs:
load-config:
uses: <owner>/gitea-ci-library/.gitea/workflows/config-provider.yml@v1
secrets: inherit
with:
config_path: .gitea/workflows/<komponentti>.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 }}
<testit>:
needs: [load-config, check-version]
if: needs.check-version.outputs.artifact_exists != 'true'
uses: ./.gitea/workflows/<komponentti>.<testi>.yml
secrets: inherit
with:
env_json: ${{ needs.load-config.outputs.env_json }}
build-push:
needs: [load-config, check-version, <testit>]
if: needs.check-version.outputs.artifact_exists != 'true'
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:
name: Report Summary
needs: [load-config, 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: '<suite-1> <suite-2>'
Version elinkaari per komponentti
GIT_TAG_PREFIX takaa että eri komponenttien versiohistoria pysyy erillään.
Git-tägi <komponentti>/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
- Älä rajaa
paths:pelkkään komponentin hakemistoon — CI ei triggeröidy workflow- tai conf-muutoksista
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) |
<version>-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 |
helm-build-push.yml |
Paketoi + puskea Helm chartin OCI-rekisteriin, 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 "<kuvaus>" <context> <suite> ${{ job.status }} |
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ä) |