From f17ea7936e218ed75ad60d77d4ebf5754926cfc6 Mon Sep 17 00:00:00 2001 From: niko Date: Wed, 17 Jun 2026 07:40:24 +0300 Subject: [PATCH] Feature/monorepo support (#18) Co-authored-by: moilanik Reviewed-on: https://gitea.app.keskikuja.site/niko/gitea-ci-library/pulls/18 --- .gitea/workflows/check-version.yml | 17 +- .gitea/workflows/docker-build-push.yml | 14 +- .gitea/workflows/example-gitea-env.conf | 2 +- README.md | 7 +- docs/ai-context.md | 1 + docs/consumer-guide.md | 446 ------------------------ skills/consumer-pipelines/SKILL.md | 420 ++++++++++++++++++++++ 7 files changed, 453 insertions(+), 454 deletions(-) delete mode 100644 docs/consumer-guide.md create mode 100644 skills/consumer-pipelines/SKILL.md diff --git a/.gitea/workflows/check-version.yml b/.gitea/workflows/check-version.yml index edd0fad..92cf6af 100644 --- a/.gitea/workflows/check-version.yml +++ b/.gitea/workflows/check-version.yml @@ -16,6 +16,8 @@ on: env: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + GIT_TAG_PREFIX: ${{ fromJson(inputs.env_json).GIT_TAG_PREFIX || '' }} + VERSION_FILE: ${{ fromJson(inputs.env_json).VERSION_FILE || '' }} jobs: check: @@ -28,7 +30,13 @@ jobs: - name: Check existing artifact and calculate version run: | - if [ -f VERSION ]; then + if [ -n "${VERSION_FILE}" ]; then + if echo "${VERSION_FILE}" | grep -q '\.json$'; then + RAW_VERSION=$(jq -r '.version' "${VERSION_FILE}") + else + RAW_VERSION=$(cat "${VERSION_FILE}" | tr -d '[:space:]') + fi + elif [ -f VERSION ]; then RAW_VERSION=$(cat VERSION | tr -d '[:space:]') elif [ -f package.json ]; then RAW_VERSION=$(jq -r '.version' package.json) @@ -44,7 +52,10 @@ jobs: TAGS_JSON=$(curl -s -f -H "Authorization: token $GITEA_TOKEN" \ "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/tags") - TAG=$(echo "$TAGS_JSON" | jq -r 'if type == "array" then .[] | select(.commit.sha == "${{ github.sha }}") | .name else empty end' | head -1) + TAG=$(echo "$TAGS_JSON" | jq -r --arg prefix "${GIT_TAG_PREFIX}" ' + if type == "array" then + .[] | select(.commit.sha == "${{ github.sha }}" and (.name | startswith($prefix))) | .name + else empty end' | head -1) mkdir -p /tmp/build-ctx @@ -55,7 +66,7 @@ jobs: else echo "ARTIFACT_EXISTS=false" > /tmp/build-ctx/build.env - HIGHEST_PATCH=$(echo "$TAGS_JSON" | jq -r --arg bv "$BASE_VERSION." ' + HIGHEST_PATCH=$(echo "$TAGS_JSON" | jq -r --arg prefix "${GIT_TAG_PREFIX}" --arg bv "${GIT_TAG_PREFIX}${BASE_VERSION}." ' if type == "array" then .[] | .name | select(startswith($bv)) | sub($bv; "") | tonumber else empty end' | sort -rn | head -1) if [ -z "$HIGHEST_PATCH" ]; then NEXT_PATCH=0; else NEXT_PATCH=$((HIGHEST_PATCH + 1)); fi diff --git a/.gitea/workflows/docker-build-push.yml b/.gitea/workflows/docker-build-push.yml index 17e4deb..ccd5f65 100644 --- a/.gitea/workflows/docker-build-push.yml +++ b/.gitea/workflows/docker-build-push.yml @@ -23,6 +23,7 @@ env: DOCKER_IMAGE_NAME: ${{ fromJson(inputs.env_json).DOCKER_IMAGE_NAME || '' }} DOCKER_UI_URL: ${{ fromJson(inputs.env_json).DOCKER_UI_URL || '' }} DOCKERFILE: ${{ fromJson(inputs.env_json).DOCKERFILE || 'Dockerfile' }} + GIT_TAG_PREFIX: ${{ fromJson(inputs.env_json).GIT_TAG_PREFIX || '' }} VERSION: ${{ inputs.version }} concurrency: @@ -50,7 +51,8 @@ jobs: --label "git.commitBy=${{ github.actor }}" \ --label "build.date=${NOW}" \ -f "${DOCKERFILE}" \ - -t "${DOCKER_IMAGE_NAME}:${VERSION}" . + -t "${DOCKER_IMAGE_NAME}:${VERSION}" \ + -t "${DOCKER_IMAGE_NAME}:latest" . REGISTRY="${DOCKER_REGISTRY:?DOCKER_REGISTRY not set in env.conf}" IMAGE="${DOCKER_IMAGE_NAME:?DOCKER_IMAGE_NAME not set in env.conf}" @@ -62,6 +64,12 @@ jobs: docker tag "${DOCKER_IMAGE_NAME}:${VERSION}" "$FULL_IMAGE" echo "$DOCKER_PASSWORD" | docker login "$REGISTRY_HOST" -u "$DOCKER_USERNAME" --password-stdin docker push "$FULL_IMAGE" + + FULL_LATEST="${REGISTRY}/${IMAGE}:latest" + echo "Pushing ${FULL_LATEST} ..." + docker tag "${DOCKER_IMAGE_NAME}:latest" "$FULL_LATEST" + docker push "$FULL_LATEST" + docker logout "$REGISTRY_HOST" - name: Report status SUCCESS @@ -69,7 +77,7 @@ jobs: run: | CONTAINER_URL="" if [ -n "${DOCKER_UI_URL:-}" ] && [ -n "${VERSION:-}" ]; then - CONTAINER_URL="${DOCKER_UI_URL}/${VERSION}" + CONTAINER_URL="${DOCKER_UI_URL}/${DOCKER_IMAGE_NAME}/${VERSION}" fi bash .ci/scripts/report-status.sh success "Docker build & push ${VERSION} OK" ci-docker-build-push "" "$CONTAINER_URL" @@ -94,7 +102,7 @@ jobs: "$SERVER_URL/api/v1/repos/${{ github.repository }}/tags" \ -H "Authorization: token $GITEA_TOKEN" \ -H "Content-Type: application/json" \ - -d "{\"tag_name\": \"${VERSION}\", \"message\": \"Build #$RUN_NUMBER\", \"target\": \"$SHA\"}") + -d "{\"tag_name\": \"${GIT_TAG_PREFIX}${VERSION}\", \"message\": \"Build #$RUN_NUMBER\", \"target\": \"$SHA\"}") if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "409" ]; then exit 0 diff --git a/.gitea/workflows/example-gitea-env.conf b/.gitea/workflows/example-gitea-env.conf index 51c5644..7d35737 100644 --- a/.gitea/workflows/example-gitea-env.conf +++ b/.gitea/workflows/example-gitea-env.conf @@ -2,5 +2,5 @@ GITEA_API_URL=https://gitea.app.keskikuja.site GIT_PAGES_URL=https://ci-reports.helm-dev.keskikuja.site DOCKER_REGISTRY=gitea.app.keskikuja.site/niko DOCKER_IMAGE_NAME=gitea-ci-library-test-image -DOCKER_UI_URL=https://gitea.app.keskikuja.site/niko/-/packages/container/gitea-ci-library-test-image +DOCKER_UI_URL=https://gitea.app.keskikuja.site/niko/-/packages/container #DOCKERFILE=Dockerfile.platform diff --git a/README.md b/README.md index 517bdf5..e02fb62 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,12 @@ Reusable workflow -kirjasto Gitea Actionsille. Lisätietoja: [docs/](docs/) -**Consumer-käyttöönotto:** [docs/consumer-guide.md](docs/consumer-guide.md) — vaiheittainen ohje uuden projektin liittämiseen +**Consumer-käyttöönotto:** [skills/consumer-pipelines/SKILL.md](skills/consumer-pipelines/SKILL.md) — pipeline-standardit ja säännöt consumer-projekteille + +**Single repo & monorepo:** Kirjasto toimii molemmissa. Monorepo-tuki +polkusuodatuksella, komponenttikohtaisilla versioilla ja git-tägien +etuliitteillä — jokainen komponentti julkaistaan itsenäisesti omassa +tahdissaan. Katso [skills/consumer-pipelines/SKILL.md](skills/consumer-pipelines/SKILL.md). ## Provider-binding — miten consumer löytää providerin diff --git a/docs/ai-context.md b/docs/ai-context.md index c9cde1e..1ddd599 100644 --- a/docs/ai-context.md +++ b/docs/ai-context.md @@ -30,6 +30,7 @@ kuuluu `git-pages/docs/`-alle, ei juuren `docs/`-kansioon. | `scripts/` | Provider-skriptit: `report-status.sh`, `publish-git-pages.sh`, `ci-validate.sh` | | `.gitea/scripts/` | **Consumer-skriptit**: `bats-coverage.sh`, `bats-report.sh` | | `docs/` | Arkkitehtuuri, ADRt (0004–0008) | +| `skills/consumer-pipelines/` | Consumer-pipeline-standardit — AI:n pakottavat säännöt consumer-CI:lle | | `docs/adr/` | Architecture Decision Records | | `git-pages/` | Raporttien hostaus (Helm-chartti) | | `tests/` | Bats-testit skripteille | diff --git a/docs/consumer-guide.md b/docs/consumer-guide.md deleted file mode 100644 index aabff78..0000000 --- a/docs/consumer-guide.md +++ /dev/null @@ -1,446 +0,0 @@ -# Consumer Guide — Kirjaston käyttöönotto - -> Anna tämä dokumentti AI:lle kun haluat ottaa `gitea-ci-library`:n käyttöön -> uudessa projektissa tai muokata olemassa olevia pipelineja. - ---- - -## Rakenneperiaate - -**Pipeline-tiedostot (`ci-feature.yml`, `ci-main.yml`) eivät saa sisältää -varsinaista logiikkaa.** Ne ovat puhtaita reitittimiä: pelkkiä `uses:`-kutsuja -`if`- ja `needs`-ehdoilla. Kaikki testien ajaminen, buildaus ja raportointi -kuuluu omiin `workflow_call`-tiedostoihinsa. - -``` -ci-unit-tests.yml ← testien ajaminen (varsinainen logiikka) -ci-acc-tests.yml ← hyväksymätestit (varsinainen logiikka) -ci-feature.yml ← reititin: load-config → test-workflow't → summary -ci-main.yml ← reititin: load-config → check-version → testit → build → summary -``` - -Provider tarjoaa 3 reusable workflow'ta ja joukon skriptejä. -Consumer omistaa orkestroinnin: mitä palikoita käytetään, missä järjestyksessä, -millä branch-ehdoilla. Consumer ei kopioi providerin koodia — se viittaa -`uses:`-direktiivillä. - ---- - -## Vaihe 1: Konfiguraatiotiedosto - -Luo `.gitea/workflows/gitea-env.conf`: - -```ini -GITEA_API_URL=https://gitea.example.com -GIT_PAGES_URL=https://reports.example.com -``` - -Jos buildaat Docker-kontteja, lisää: - -```ini -DOCKER_REGISTRY=gitea.example.com/myorg -DOCKER_IMAGE_NAME=my-service -DOCKER_UI_URL=https://gitea.example.com/myorg/-/packages/container/my-service -#DOCKERFILE=Dockerfile.platform # valinnainen, oletus Dockerfile -``` - -Salaisuudet määritellään Gitean Settings → Secrets -näkymässä: - -| Secret | Pakollinen | -|---|---| -| `GITEA_TOKEN` | Aina | -| `GIT_PAGES_PUBLISH_TOKEN` | Aina | -| `DOCKER_USERNAME` | Vain jos buildaat kontteja (ei pakollinen kaikissa registryissä) | -| `DOCKER_PASSWORD` | Vain jos buildaat kontteja | - ---- - -## Vaihe 2: Test-workflow't (varsinainen logiikka) - -Jokainen testityyppi omaan `workflow_call`-tiedostoonsa. Tässä esimerkki -Maven-yksikkötesteistä. Luo `.gitea/workflows/ci-unit-tests.yml`: - -```yaml -name: Unit Tests -on: - workflow_call: - inputs: - env_json: - required: true - type: string - secrets: - GITEA_TOKEN: - required: true - GIT_PAGES_PUBLISH_TOKEN: - required: true - -env: - GITEA_API_URL: ${{ fromJson(inputs.env_json).GITEA_API_URL }} - GIT_PAGES_URL: ${{ fromJson(inputs.env_json).GIT_PAGES_URL }} - GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} - GIT_PAGES_PUBLISH_TOKEN: ${{ secrets.GIT_PAGES_PUBLISH_TOKEN }} - -jobs: - test: - runs-on: ubuntu-latest - container: maven:3.9-eclipse-temurin-21 - steps: - - uses: actions/checkout@v4 - - uses: actions/checkout@v4 - with: - repository: org/gitea-ci-library - path: .ci - - - name: Run tests - shell: bash - run: | - mvn test - EXIT=$? - echo "EXIT=${EXIT}" >> "${GITHUB_ENV}" - exit ${EXIT} - - - name: Publish reports - if: always() - run: bash .ci/scripts/publish-git-pages.sh junit - - - name: Report status - if: always() - shell: bash - run: | - if [ "${EXIT}" = "0" ]; then - bash .ci/scripts/report-status.sh success "Link to JUnit reports" unit-tests junit - else - bash .ci/scripts/report-status.sh failure "Link to JUnit reports" unit-tests junit - fi -``` - -Hyväksymätesteille vastaava tiedosto `ci-acc-tests.yml` (Cucumber, Playwright -tms.), jossa oma `container:`, oma testikomento ja oma `suite`-nimi. - -**Tärkeää:** `mvn test` korvataan omalla testikomennolla. `container:` ja -`publish-git-pages.sh`-suite ovat projektikohtaisia. Muu runko pysyy samana. - ---- - -## Vaihe 3: Feature-haaran CI (puhdas reititin) - -Luo `.gitea/workflows/ci-feature.yml`. **Ei sisällä yhtään `run:`-steppiä** -— pelkkiä `uses:`-kutsuja: - -```yaml -name: CI Feature -on: - push: - branches-ignore: - - main - workflow_dispatch: - -jobs: - load-config: - uses: org/gitea-ci-library/.gitea/workflows/config-provider.yml@v1 - secrets: inherit - with: - config_path: .gitea/workflows/gitea-env.conf - - unit-tests: - needs: [load-config] - uses: ./.gitea/workflows/ci-unit-tests.yml@main - secrets: inherit - with: - env_json: ${{ needs.load-config.outputs.env_json }} - - acc-tests: - needs: [load-config] - uses: ./.gitea/workflows/ci-acc-tests.yml@main - secrets: inherit - with: - env_json: ${{ needs.load-config.outputs.env_json }} - - report-summary: - needs: [load-config, unit-tests, acc-tests] - if: always() - uses: org/gitea-ci-library/.gitea/workflows/report-summary.yml@v1 - with: - env_json: ${{ needs.load-config.outputs.env_json }} - suites: junit cucumber -``` - ---- - -## Vaihe 4: Main-haaran CI (puhdas reititin) - -Luo `.gitea/workflows/ci-main.yml`. **Ei sisällä yhtään `run:`-steppiä**: - -```yaml -name: CI Main -on: - push: - branches: - - main - workflow_dispatch: - -jobs: - load-config: - uses: org/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: org/gitea-ci-library/.gitea/workflows/check-version.yml@v1 - secrets: inherit - with: - env_json: ${{ needs.load-config.outputs.env_json }} - - unit-tests: - needs: [load-config, check-version] - if: needs.check-version.outputs.artifact_exists != 'true' - uses: ./.gitea/workflows/ci-unit-tests.yml@main - secrets: inherit - with: - env_json: ${{ needs.load-config.outputs.env_json }} - - acc-tests: - needs: [load-config, check-version] - if: needs.check-version.outputs.artifact_exists != 'true' - uses: ./.gitea/workflows/ci-acc-tests.yml@main - secrets: inherit - with: - env_json: ${{ needs.load-config.outputs.env_json }} - - build-push: - needs: [load-config, check-version, unit-tests, acc-tests] - if: needs.check-version.outputs.artifact_exists != 'true' - uses: org/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, unit-tests, acc-tests] - if: always() - uses: org/gitea-ci-library/.gitea/workflows/report-summary.yml@v1 - with: - env_json: ${{ needs.load-config.outputs.env_json }} - suites: junit cucumber -``` - -Mihin kiinnittää huomiota: -- `check-version` on **idempotentti** — jos commitilla on jo tagi, kaikki - sen jälkeiset jobit skipataan (`if: artifact_exists != 'true'`) -- `needs`-ketju takaa järjestyksen ja virheiden propagointin -- Artifakti voi olla **mitä tahansa** — `docker-build-push.yml` on yksi - esimerkki. Voit korvata sen Maven-deploylla, npm-publishilla, tai millä - tahansa omalla build-workflow'lla. Rajapinta on `version`-input. - ---- - -## Versionhallinta - -`check-version.yml` lukee version automaattisesti prioriteettijärjestyksessä: - -| # | Lähde | Formaatti | Esimerkki | -|---|---|---|---| -| 1 | `VERSION`-tiedosto | Plain text | `0.2` | -| 2 | `package.json` | `.version` | `"version": "0.2.0"` | -| 3 | `pom.xml` | `` | `0.2.0` | - -`major.minor` otetaan tästä. Patch (kolmas numero) lasketaan automaattisesti -git-tageista. Esim. jos `VERSION` on `0.2` ja tagit ovat `0.2.0`, `0.2.1`, -niin seuraava on `0.2.2`. - ---- - -## Testien lisääminen — oma työkalu - -Kopioi `ci-unit-tests.yml`:n rakenne uudelle testityypille ja muuta: -- `container:` — oma testikonttisi -- Testikomento — oma testityökalusi (`npm test`, `pytest`, `go test`, ...) -- `publish-git-pages.sh ` — oma suite-nimi -- `report-status.sh ... ` — oma uniikki konteksti - -Lisää uusi jobi reititintiedostoihin (`ci-feature.yml`, `ci-main.yml`) -samalla `uses:`-kaavalla. - -Testijobit ajetaan rinnakkain — ne kaikki `needs: [load-config]` ilman -keskinäisiä riippuvuuksia. - -### Tärkeimmät säännöt - -1. **Exit-koodi aina ylös:** - ```bash - run-tests - EXIT=$? - echo "EXIT=${EXIT}" >> "${GITHUB_ENV}" - exit ${EXIT} - ``` - -2. **Ei pipeä testikomennon perään.** `command | tee file` syö exit-koodin. - Käytä `command > file 2>&1` jos haluat logit talteen. - -3. **Status vain jos on raportti.** Testijobit käyttävät commit-status API:a - raporttilinkin takia. Tool-jobit luottavat Gitean natiiviin job-statukseen. - -4. **`if: always()`** publish- ja status-stepeissä — raportti julkaistaan - ja status asetetaan vaikka testit feilaisivat. - -### Raporttien generointi - -`publish-git-pages.sh ` odottaa hakemiston `reports/${SHA8}//` -olevan olemassa. Sen sisältö sellaisenaan julkaistaan git-pagesiin. -`report-status.sh` linkittää statuksen suoraan tähän hakemistoon — selain -avaa sieltä `index.html`:n. - -Test-workflow'n vastuulla on tuottaa raportit oikeaan polkuun. Kaksi -tyypillistä patternia: - -**Pattern 1: Yksi raporttitiedosto (Cucumber)** - -Testityökalu tuottaa suoraan HTML-raportin. Yksinkertaisin tapaus: - -```bash -mkdir -p "reports/${GITHUB_SHA:0:8}/cucumber" -npx cucumber-js \ - --format html:"reports/${GITHUB_SHA:0:8}/cucumber/index.html" -``` - -**Pattern 2: Monta raporttitiedostoa (Bats + coverage)** - -Eri työkalut tuottavat eri tiedostoja. Generoi `index.html` joka linkittää -ne yhteen: - -``` -reports/${SHA8}/bats/ - ├── index.html ← generoitu: linkit alla oleviin - ├── results.txt ← bats-testien stdout - ├── coverage/ ← bashcov-coverage HTML - │ └── index.html - └── ... -``` - -```bash -mkdir -p "reports/${GITHUB_SHA:0:8}/bats" - -# Aja testit → results.txt -bats tests/ > "reports/${GITHUB_SHA:0:8}/bats/results.txt" 2>&1 - -# Generoi coverage → coverage-hakemisto -bashcov -- bats tests/ - -# Generoi index.html joka linkittää kaikkiin raportteihin -cat > "reports/${GITHUB_SHA:0:8}/bats/index.html" <<'EOF' - - -

Bats Test Reports

- - -EOF -``` - -Yhteistä molemmille: `publish-git-pages.sh `-kutsun jälkeen raportit -ovat julkisesti selailtavissa. `report-status.sh`-kutsu `suite`-parametrilla -linkittää commit-statuksen suoraan `index.html`:ään. - -Jos testit feilasivat, raportti generoidaan silti — se kertoo MITKÄ testit -feilasivat. Siksi publish- ja status-stepit käyttävät `if: always()`. - ---- - -## Branch protection (PR-gate) - -Gitean Settings → Branches → Add Rule: - -- **Branch:** `main` -- **Enable Require Status Checks:** päälle -- **Status checks:** valitse `unit-tests`, `acc-tests` - ---- - -## Raporttien koonti (Gitea 1.27+) - -Kun Gitea päivittyy versioon 1.27, `GITHUB_STEP_SUMMARY`-tuki mahdollistaa -raporttilinkkien koontinäkymän suoraan Gitea UI:ssa. `report-summary`-jobi -on mukana molemmissa reititinesimerkeissä yllä — forward-compatibeli, ei -hajota vanhemmilla versioilla. - ---- - -## 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 | -| `report-summary.yml` | `GITHUB_STEP_SUMMARY`-taulukko raporttilinkeillä | - -### 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 - -Nämä säännöt on formalisoitu [docs/adr/](docs/adr/)-hakemistossa. Tässä tiivistelmä -consumer-näkökulmasta: - -### Reititin ei sisällä suorittavaa koodia (ADR 0010) - -`ci-feature.yml` ja `ci-main.yml` ovat **puhtaita reitittimiä**: -- **Vain** `uses:`, `needs:` ja `if:` sallittu -- **Ei** `run:`-komentoja, ei inline-skriptejä, ei `actions/checkout` - -Kaikki suorittava koodi on omissa `workflow_call`-tiedostoissaan: -- `ci-unit-tests.yml` — testikomento, publish, status -- `ci-acc-tests.yml` — testikomento, publish, status -- Provider-workflowt (`config-provider.yml`, `check-version.yml`, …) - -### Yksi steppi = yksi workflow_call-tiedosto - -Jokainen pipeline-steppi (testityyppi, build, deploy) on oma tiedostonsa. -Ei kahta eri komentoa samassa workflow'ssa. Tämä pitää reitittimet ohuina -ja steppitiedostot itsenäisinä — testattavissa erikseen. - -### Provider-versio on `@v1` (ADR 0009) - -Kaikki `org/gitea-ci-library/…`-viittaukset käyttävät `@v1`-tagia: -```yaml -uses: org/gitea-ci-library/.gitea/workflows/config-provider.yml@v1 -``` -`@main` on vain providerin oman repon sisäiseen dogfood-käyttöön. - -Breaking changet on kielletty — `v1`-rajapinta on pysyvä. - -### Exit-koodi on ainoa onnistumisen mittari (ADR 0008) - -Älä käytä pipeä (`|`) komennon perässä — se syö exit-koodin. -Käytä redirectiä (`> file 2>&1`) jos haluat logit talteen. - -### 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ä. Consumer käyttää -providerin määrittelemää polkua (`.ci/scripts/`). Consumer ei kopioi eikä -muokkaa providerin tiedostoja. - -### Testattavuus - -Jokainen `workflow_call`-tiedosto on testattavissa itsenäisesti — consumer -voi ajaa `ci-unit-tests.yml`:n paikallisesti act:lla tai Gitean -`workflow_dispatch`:llä ilman koko pipelineä. diff --git a/skills/consumer-pipelines/SKILL.md b/skills/consumer-pipelines/SKILL.md new file mode 100644 index 0000000..a166a3e --- /dev/null +++ b/skills/consumer-pipelines/SKILL.md @@ -0,0 +1,420 @@ +--- +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.