diff --git a/.gitea/workflows/example-main.yml b/.gitea/workflows/example-main.yml index a734123..95eacac 100644 --- a/.gitea/workflows/example-main.yml +++ b/.gitea/workflows/example-main.yml @@ -39,7 +39,7 @@ jobs: with: env_json: ${{ needs.load-config.outputs.env_json }} - build-push: + docker-build-push: name: Build & Push Docker needs: [load-config, check-version, bats, cucumber] if: needs.check-version.outputs.artifact_exists != 'true' @@ -49,18 +49,118 @@ jobs: env_json: ${{ needs.load-config.outputs.env_json }} version: ${{ needs.check-version.outputs.version }} + helm-build-push: + name: Build & Push Helm + needs: [load-config, check-version, bats, cucumber] + if: needs.check-version.outputs.artifact_exists != 'true' + uses: niko/gitea-ci-library/.gitea/workflows/helm-build-push.yml@main + secrets: inherit + with: + env_json: ${{ needs.load-config.outputs.env_json }} + version: ${{ needs.check-version.outputs.version }} + + gitops-chart: + name: GitOps — helm version + needs: [helm-build-push] + if: success() + runs-on: ubuntu-latest + outputs: + chart_commit: ${{ steps.update.outputs.chart_commit }} + steps: + - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + with: + repository: niko/gitea-ci-library + path: .ci + - name: Update Chart.yaml version + id: update + run: | + INPUTS=$(jq -nc \ + --arg file "dev/Chart.yaml" \ + --arg yq_tpl '(.dependencies[] | select(.name == "git-pages") | .version) = "{{VERSION}}"' \ + --arg version "${{ needs.check-version.outputs.version }}" \ + --arg source_repo "${{ github.repository }}" \ + --arg source_commit "${{ github.sha }}" \ + --arg git_tag_prefix "helm" \ + '{file: $file, yq_tpl: $yq_tpl, version: $version, source_repo: $source_repo, source_commit: $source_commit, git_tag_prefix: $git_tag_prefix}') + OUTPUT=$(bash .ci/scripts/dispatch-workflow.sh \ + "niko/gitea-ci-gitops-tests" "gitops-service.yaml" "main" \ + "$INPUTS" "${{ fromJson(needs.load-config.outputs.env_json).GITEA_API_URL }}" \ + "${{ secrets.GITOPS_DISPATCH_TOKEN }}" "30") + echo "$OUTPUT" + CHART_REPO=$(echo "$OUTPUT" | grep '^GITOPS_COMMIT=' | cut -d= -f2) + echo "chart_commit=$CHART_REPO" >> "$GITHUB_OUTPUT" + + gitops-values: + name: GitOps — docker tag + needs: [docker-build-push] + if: success() + runs-on: ubuntu-latest + outputs: + values_commit: ${{ steps.update.outputs.values_commit }} + steps: + - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + with: + repository: niko/gitea-ci-library + path: .ci + - name: Update values.yaml tag + id: update + run: | + INPUTS=$(jq -nc \ + --arg file "dev/values.yaml" \ + --arg yq_tpl '.service.tag = "{{VERSION}}"' \ + --arg version "${{ needs.check-version.outputs.version }}" \ + --arg source_repo "${{ github.repository }}" \ + --arg source_commit "${{ github.sha }}" \ + --arg git_tag_prefix "docker" \ + '{file: $file, yq_tpl: $yq_tpl, version: $version, source_repo: $source_repo, source_commit: $source_commit, git_tag_prefix: $git_tag_prefix}') + OUTPUT=$(bash .ci/scripts/dispatch-workflow.sh \ + "niko/gitea-ci-gitops-tests" "gitops-service.yaml" "main" \ + "$INPUTS" "${{ fromJson(needs.load-config.outputs.env_json).GITEA_API_URL }}" \ + "${{ secrets.GITOPS_DISPATCH_TOKEN }}" "30") + echo "$OUTPUT" + VALUES_REPO=$(echo "$OUTPUT" | grep '^GITOPS_COMMIT=' | cut -d= -f2) + echo "values_commit=$VALUES_REPO" >> "$GITHUB_OUTPUT" + report-summary: name: Report Summary - needs: [load-config, build-push] + needs: [load-config, docker-build-push, helm-build-push] if: always() uses: niko/gitea-ci-library/.gitea/workflows/report-summary.yml@main with: env_json: ${{ needs.load-config.outputs.env_json }} suites: bats cucumber + gitops-summary: + name: GitOps Summary + needs: [load-config, check-version, gitops-chart, gitops-values] + if: always() + runs-on: ubuntu-latest + steps: + - name: Write GitOps summary + run: | + GITEA_URL="${{ fromJson(needs.load-config.outputs.env_json).GITEA_API_URL }}" + CHART_COMMIT="${{ needs.gitops-chart.outputs.chart_commit }}" + VALUES_COMMIT="${{ needs.gitops-values.outputs.values_commit }}" + CHART_LINK="${GITEA_URL}/niko/gitea-ci-gitops-tests/commit/${CHART_COMMIT}" + VALUES_LINK="${GITEA_URL}/niko/gitea-ci-gitops-tests/commit/${VALUES_COMMIT}" + + cat >> "$GITHUB_STEP_SUMMARY" << 'GITOPS' + + ## GitOps updates + + | Component | Version | Status | GitOps commit | + |-----------|---------|--------|--------------| + GITOPS + { + echo "| helm | ${{ needs.check-version.outputs.version }} | ${{ needs.gitops-chart.result }} | [link](${CHART_LINK}) |" + echo "| docker | ${{ needs.check-version.outputs.version }} | ${{ needs.gitops-values.result }} | [link](${VALUES_LINK}) |" + } >> "$GITHUB_STEP_SUMMARY" + tag-maintenance: name: Move provider version tag - needs: [build-push] + needs: [docker-build-push, helm-build-push] if: success() uses: niko/gitea-ci-library/.gitea/workflows/tag-maintenance.yml@main secrets: inherit diff --git a/README.md b/README.md index a94da3a..586b11b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Reusable workflow -kirjasto Gitea Actionsille. Lisätietoja: [docs/](docs/) **Consumer-käyttöönotto:** [skills/consumer-pipelines/SKILL.md](skills/consumer-pipelines/SKILL.md) — pipeline-standardit ja säännöt consumer-projekteille +**GitOps-päivitys:** [skills/gitops-update/SKILL.md](skills/gitops-update/SKILL.md) — GitOps-repon job-template, dispatch ja token-ohjeet + **Single repo & monorepo:** Kirjasto toimii molemmissa. Monorepo-tuki polkusuodatuksella, komponenttikohtaisilla versioilla ja git-tägien etuliitteillä — jokainen komponentti julkaistaan itsenäisesti omassa @@ -220,7 +222,9 @@ Consumer-repossa on oltava seuraavat asetukset: |--------|--------| | `GIT_PAGES_PUBLISH_TOKEN` | Git-pages-palvelimen BasicAuth-token. Nimi on lukittu — tämä tarkka nimi vaaditaan. | -`GITEA_TOKEN` on Gitean sisäinen secret (`secrets.GITEA_TOKEN`), joka on automauttisesti saatavilla — sitä ei tarvitse erikseen luoda. +`GITEA_TOKEN` on Gitean automaattisesti jokaiselle workflow-runille generoima token (`secrets.GITEA_TOKEN`). Se on scopeutettu **siihen repoon**, jossa workflow ajaa — ei toimi toiseen repoon dispatchaukseen eikä toisen repon commit-statusin asettamiseen. Ei tarvitse erikseen luoda. + +Jos workflow tarvitsee oikeuksia **toiseen** repoon (esim. dispatch GitOps-repoon), tarvitaan manuaalinen token. Katso [skills/gitops-update/SKILL.md](skills/gitops-update/SKILL.md). ### Config-tiedosto (`.gitea/workflows/gitea-env.conf`) @@ -256,6 +260,17 @@ Jokaisen jobin alussa `ci-validate.sh` tarkistaa: Jos validointi epäonnistuu, job keskeytyy exit-koodilla 1 ja Gitean commit-status näyttää epäonnistumisen linkkinä lokiin. +### GitOps-päivitys + +Artifact buildin jälkeen voidaan dispatchata GitOps-repoon, joka päivittää +konfiguraatiotiedoston (esim. Chart.yaml version) ja pushaa muutoksen. + +Kaksi skriptiä: +- `scripts/dispatch-workflow.sh` — lähettää workflow_dispatch-pyynnön ja pollaa valmistumista +- `scripts/gitops-update.sh` — kloonaa, päivittää yq:llä, committaa ja pushaa + +Tarkka asennus: [skills/gitops-update/SKILL.md](skills/gitops-update/SKILL.md) + ### Muuta | Muuttuja | Kuvaus | diff --git a/docs/config-model.md b/docs/config-model.md index 8288206..75dcaba 100644 --- a/docs/config-model.md +++ b/docs/config-model.md @@ -60,9 +60,15 @@ Salaisuudet eivät ole `.conf`-tiedostossa. Ne määritellään Gitean organization/repository secrets -mekanismissa ja välitetään workflowlle `secrets: inherit` -direktiivillä. +**`secrets.GITEA_TOKEN` on Gitean automaattisesti generoima token, +scopeutuu siihen repoon jossa workflow ajaa.** Se ei oikeuta +dispatchaamaan toiseen repoon eikä kirjoittamaan toisen repon +commit-statusta. Cross-repo-operaatioihin tarvitaan manuaalinen +org-tason token. + | Secret | Pakollinen | Käyttäjä | |---|---|---| -| `GITEA_TOKEN` | Kyllä | `report-status.sh`, `check-version.yml`, `docker-build-push.yml`, `gitops-update.sh` | +| `GITEA_TOKEN` | Kyllä | `report-status.sh`, `check-version.yml`, `docker-build-push.yml`, `gitops-update.sh` (GitOps-repossa) | | `GIT_PAGES_PUBLISH_TOKEN` | Kyllä | `publish-git-pages.sh`, `config-provider.yml` (validointi) | | `DOCKER_USERNAME` | Ei | `docker-build-push.yml` (oletus: `github.actor`, ei pakollinen kaikissa registryissä) | | `DOCKER_PASSWORD` | Kyllä | `docker-build-push.yml` | diff --git a/docs/shared-scripts.md b/docs/shared-scripts.md index f173037..3940913 100644 --- a/docs/shared-scripts.md +++ b/docs/shared-scripts.md @@ -113,17 +113,38 @@ Lukee tiedoston polun `CI_CONF_FILE`-env-muuttujasta (oletus: `.gitea/workflows/ Dispatchaa workflow'n toisessa repossa ja pollaa sen valmistumista synkronisesti. Käytetään GitOps-deploymentissa ja klusteritestien ketjutuksessa (tuleva). +Generoi automaattisesti `dispatch_id`-tunnisteen, lisää sen dispatch- +inputteihin ja tunnistaa workflow-runin kohdereposta `display_title`- +kentän perusteella. Toimii luotettavasti vaikka samassa repossa olisi +useita samanaikaisia ajoja. + +**Kohde-workflow'ssa on oltava `dispatch_id`-input ja `run-name`-kenttä +`display_title`-matchausta varten.** Katso `skills/gitops-update/SKILL.md`. + ### Rajapinta ```bash -dispatch-workflow.sh [timeout_minutes] +dispatch-workflow.sh [timeout_minutes] ``` +| Parametri | Pakollinen | Kuvaus | +|-----------|------------|--------| +| `target_repo` | Kyllä | `owner/repo` | +| `workflow_file` | Kyllä | Workflow-tiedosto (esim. `ci-main.yml`) | +| `ref` | Kyllä | Branch | +| `inputs_json` | Kyllä | JSON-objekti dispatch-inputteina | +| `gitea_api_url` | Kyllä | Gitean API-URL | +| `gitea_token` | Kyllä | Gitea API -token (write kohderepoon) | +| `timeout_minutes` | Ei | Aikakatkaisu (oletus 360) | + ### Toiminta -1. **Dispatch:** `POST /api/v1/repos/{target_repo}/actions/workflows/{workflow_file}/dispatches` -2. **Poll:** `GET /api/v1/repos/{target_repo}/actions/runs` → odota valmistumista -3. **Palauta:** `conclusion` (`success`/`failure`/`timeout`) +1. **Generoi `dispatch_id`** — 8-hex uniikki tunniste +2. **Injektoi** `dispatch_id` inputteihin +3. **Dispatch:** `POST /api/v1/repos/{target_repo}/actions/workflows/{workflow_file}/dispatches` +4. **Etsi run:** pollaa rinnakkaisia `workflow_dispatch`-runeja, matchaa `display_title` sisältää `dispatch_id`:n +5. **Poll:** `GET /api/v1/repos/{target_repo}/actions/runs/{run_id}` — odota valmistumista +6. **Palauta:** exit 0 (success), exit 1 (failure), exit 124 (timeout) --- diff --git a/docs/workflows.md b/docs/workflows.md index a9de492..a466298 100644 --- a/docs/workflows.md +++ b/docs/workflows.md @@ -153,9 +153,17 @@ load-config → bats + cucumber → report-summary (always) ``` load-config → check-version → [artifact exists] → done - [no artifact] → bats + cucumber → report-summary (always) → docker-build-push + [no artifact] → bats + cucumber + ├─ docker-build-push → gitops-values ─┐ + └─ helm-build-push → gitops-chart ─┤ + ├─ gitops-summary + tag-maintenance ←─────────────────────┘ ``` +GitOps-jobit (`gitops-chart`, `gitops-values`) dispatchaavat GitOps-repon +workflown ja asettavat commit-statusin code-repoon + GitOps-repoon +(kaksisuuntainen track). Katso [skills/gitops-update/SKILL.md](../skills/gitops-update/SKILL.md). + ### `example-bats-tests.yml` — Bats unit-testit **Trigger:** `workflow_call` @@ -189,9 +197,15 @@ Forward-compatibeli — ei haittaa vanhemmilla Gitea-versioilla. **Riippuvuudet:** `yq`, `scripts/report-status.sh`, `git` Päivittää GitOps-repon konfiguraatiotiedoston versionumeron `yq`:lla, -committaa muutoksen ja asettaa commit-statuksen molempiin repoihin. +committaa muutoksen ja asettaa commit-statuksen molempiin repoihin +(kaksisuuntainen track): -**Input-ympäristömuuttujat:** +| Status | Mihin repo | Context | Linkki | +|---|---|---|---| +| ✅ | **GitOps-repo** | `source/{repo}` | Code-repon committiin | +| ✅ | **Code-repo** (dispatchin jälkeen) | `gitops/{repo} {RUN_ID}` | GitOps-repon committiin | + +**Input-ympäristömuuttujat (ajetaan GitOps-repon workflow'ssa):** | Muuttuja | Pakollinen | Kuvaus | |---|---|---| @@ -200,33 +214,53 @@ committaa muutoksen ja asettaa commit-statuksen molempiin repoihin. | `VERSION` | Kyllä | Uusi versio (esim. `0.2.3`) | | `SOURCE_REPO` | Kyllä | Lähdekoodirepo (esim. `org/app`) | | `SOURCE_COMMIT` | Kyllä | Lähdekoodin commit-SHA | -| `GITOPS_REPO` | Kyllä | GitOps-konfiguraatiorepo (esim. `org/app-gitops`) | +| `GITOPS_REPO` | Kyllä | GitOps-repo slug | | `GITEA_API_URL` | Kyllä | Gitean API-URL | -| `GITEA_TOKEN` | Kyllä | Gitea API-token | +| `GITEA_TOKEN` | Kyllä | Gitea API-token (write GitOps-repoon) | | `GITOPS_BRANCH` | Ei | GitOps-repon branch (oletus `main`) | +| `GIT_TAG_PREFIX` | Ei | Komponentin tag-prefix status-nimeämiseen | + +**Commit-status (GitOps-repoon):** +| Kenttä | Formaatti | Esimerkki | +|--------|-----------|-----------| +| Context | `source/{repo}` | `source/gitea-ci-library` | +| Description | `Install to {env} {version}` | `Install to dev 0.2.0` | +| Target URL | Linkki code-repon committiin | `/org/repo/commit/sha` | + +`{env}` parsitaan `INPUT_FILE`:stä (`dev/Chart.yaml` → `dev`). **Steppikuvaus:** 1. Korvaa `YQ_TPL`:n `{{VERSION}}` versiolla 2. Muodostaa `CLONE_URL` tokenilla ja hostilla 3. Kloonaa GitOps-repon 4. Ajaa `yq eval -i` päivittääkseen tiedoston -5. Commit + push `[skip ci]` -6. Asettaa commit-statuksen: code-repoon (gitops-konteksti) ja GitOps-repoon (source-konteksti) +5. Jos muutoksia: commit + push `[skip ci]`, muuten status `— no change` +6. Asettaa commit-statuksen GitOps-repoon (source-konteksti, linkki code-repoon) -**Esimerkki dispatchistä:** -```yaml -- name: Update GitOps - run: | - export INPUT_FILE=dev/Chart.yaml - export YQ_TPL='(.version) = "{{VERSION}}"' - export VERSION=0.2.3 - export SOURCE_REPO=org/app - export SOURCE_COMMIT=${{ github.sha }} - export GITOPS_REPO=org/app-gitops - bash scripts/gitops-update.sh - env: - GITEA_API_URL: ${{ vars.GITEA_API_URL }} - GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} -``` +**Scriptiä ei ajeta code reposta.** Se ajaa GitOps-repon workflow'ssa. + +### Code-repon commit-status (dispatchin jälkeen) + +GitOps-päivityksen valmistuttua `dispatch-workflow.sh` tulostaa +`GITOPS_COMMIT=` (GitOps-repon commitin SHA). Code repo asettaa +oman commit-statusinsa linkillä GitOps-committiin: + +| Kenttä | Formaatti | Esimerkki | +|--------|-----------|-----------| +| Context | `gitops/{repo} {RUN_ID}` | `gitops/gitea-ci-library 473` | +| Description | `Install to {env} {version}` | `Install to dev 0.2.0` | +| Target URL | Linkki GitOps-repon committiin | `/niko/gitea-ci-gitops-tests/commit/def456` | + +### Loppuraportti (GITHUB_STEP_SUMMARY) + +`gitops-summary`-job (tai `report-summary`-job) lisää rivin GitOps-päivityksestä +GITHUB_STEP_SUMMARYyn: + +| Component | Version | Status | GitOps commit | +|---|---|---|---| +| helm | 0.2.0 | success | [link](...) | + +Kokonainen esimerkki molemmista puolista: [skills/gitops-update/SKILL.md](../skills/gitops-update/SKILL.md) +ja [.gitea/workflows/example-main.yml](../.gitea/workflows/example-main.yml). --- diff --git a/skills/gitops-update/SKILL.md b/skills/gitops-update/SKILL.md new file mode 100644 index 0000000..cc6b450 --- /dev/null +++ b/skills/gitops-update/SKILL.md @@ -0,0 +1,410 @@ +--- +name: gitops-update +description: | + Setting up GitOps version updates: GitOps-repo workflow template, code + repo dispatch, secret requirements, and two-repo commit-status pattern. + Activates when the user needs to wire up artifact builds to GitOps + configuration updates. +activation-gate: | + User mentions GitOps update, gitops-update, dispatch to another repo, + two-repo version bump, cross-repo deployment, or wiring build output to + config repo. +category: ci +impact: high +--- + +# GitOps Update — Provider-palvelu + +`scripts/gitops-update.sh` ja `scripts/dispatch-workflow.sh` muodostavat +GitOps-päivityspalvelun. Artifact buildataan code repossa, minkä jälkeen +code repo dispatchaa GitOps-repoon, joka päivittää konfiguraatiotiedoston +ja pushaa muutoksen. + +## Arkkitehtuuri + +Kaksi erillistä repoa, eristetyt oikeudet: + +``` +Code repo GitOps repo +(build & push artifact) (konfiguraatiot) + +build & push onnistuu (v0.2.3) + │ + │ dispatch ci-main.yml + │ {file, yq_tpl, version, source_repo, source_commit} + │ + └────────────────────────────────────→┐ + │ + dispatch-workflow.sh pollaa ←─────────┘ + │ + code repo asettaa │ git clone, yq update, + oman commit-statusnsa │ git commit + push + dispatchin exit-koodilla │ status GitOps-repoon +``` + +**Token-periaate:** Vain GitOps-repoon kirjoitetaan. Code repo asettaa +oman commit-statusnsa dispatch-kutsun exit-koodin perusteella omalla +auto-tokenillaan. GitOps-repon auto-token ei tarvitse oikeuksia code +repoon. + +## GitOps-repon workflow (ci-main.yml) + +GitOps-repoon luodaan `.gitea/workflows/ci-main.yml`: + +```yaml +name: GitOps Update +run-name: "GitOps Service (${{ inputs.dispatch_id || 'manual' }})" +on: + workflow_dispatch: + inputs: + file: + required: true + type: string + yq_tpl: + required: true + type: string + version: + required: true + type: string + source_repo: + required: true + type: string + source_commit: + required: true + type: string + dispatch_id: + required: false + type: string + git_tag_prefix: + required: false + type: string + +env: + INPUT_FILE: ${{ inputs.file }} + YQ_TPL: ${{ inputs.yq_tpl }} + VERSION: ${{ inputs.version }} + SOURCE_REPO: ${{ inputs.source_repo }} + SOURCE_COMMIT: ${{ inputs.source_commit }} + GITOPS_REPO: ${{ github.repository }} + GITOPS_BRANCH: ${{ github.ref_name }} + GITEA_API_URL: ${{ gitea.server_url }} + GIT_TAG_PREFIX: ${{ inputs.git_tag_prefix || '' }} + +jobs: + update: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/checkout@v4 + with: + repository: niko/gitea-ci-library + path: .ci + + - name: Install yq + run: | + wget -qO /usr/local/bin/yq \ + https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 + chmod +x /usr/local/bin/yq + + - name: Run GitOps update + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + bash .ci/scripts/gitops-update.sh +``` + +**Huomiot:** +- `GITEA_TOKEN` on Gitean auto-token — scopeutuu GitOps-repoon, riittää + cloneen, committiin, pushiin ja commit-statusiin GitOps-repossa +- `run-name` ja `dispatch_id` mahdollistavat dispatchaavan skriptin tunnistaa + tämän workflow-runin yksiselitteisesti `display_title`-kentästä, vaikka + samassa repossa olisi samanaikaisia ajoja +- yq ladataan lennossa (kompromissi, ks. "Tuleva CI-kontti") + +### Tulossa: custom CI-kontti + +Nykyinen job lataa yq:n lennossa. Myöhemmin rakennetaan oma kontti +(`ci-gitops`), jossa on nodejs + git + yq valmiina. Sama patterni kuin +`ci-bats` ja `ci-cucumber`. Ks. `skills/ci-container-build/SKILL.md`. + +## Code-repon dispatch-step + +Code repo dispatchaa GitOps-repon workflown artifact buildin onnistuttua: + +```yaml +gitops-update: + needs: [helm-build-push] + if: success() + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/checkout@v4 + with: + repository: niko/gitea-ci-library + path: .ci + + - name: Dispatch GitOps update + id: dispatch + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + run: | + INPUTS=$(jq -nc \ + --arg file "dev/Chart.yaml" \ + --arg yq_tpl '(.dependencies[] | select(.name == "agent-platform-helm") | .version) = "{{VERSION}}"' \ + --arg version "${{ needs.check-version.outputs.version }}" \ + --arg source_repo "${{ github.repository }}" \ + --arg source_commit "${{ github.sha }}" \ + '{file: $file, yq_tpl: $yq_tpl, version: $version, source_repo: $source_repo, source_commit: $source_commit}') + OUTPUT=$(bash .ci/scripts/dispatch-workflow.sh \ + "niko/agent-platform-gitops" \ + "ci-main.yml" \ + "main" \ + "$INPUTS" \ + "${{ fromJson(needs.load-config.outputs.env_json).GITEA_API_URL }}" \ + "${{ secrets.GITEA_TOKEN }}" \ + "30") + echo "$OUTPUT" + GITOPS_COMMIT=$(echo "$OUTPUT" | grep '^GITOPS_COMMIT=' | cut -d= -f2) + echo "gitops_commit=$GITOPS_COMMIT" >> "$GITHUB_OUTPUT" +``` + +### Multi-artifact pipeline (kontti + helm) + +Yksi main-haaran build tuottaa usein sekä Docker-imagen että Helm-chartin. +Kumpikin artefakti dispatchaa oman GitOps-päivityksensä rinnakkain: + +```yaml +gitops-helm: + needs: [helm-build-push] + if: success() + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + with: + repository: niko/gitea-ci-library + path: .ci + - name: Update helm version + id: helm + run: | + INPUTS=$(jq -nc \ + --arg file "dev/Chart.yaml" \ + --arg yq_tpl '(.dependencies[] | select(.name == "git-pages") | .version) = "{{VERSION}}"' \ + --arg version "${{ needs.check-version.outputs.version }}" \ + --arg source_repo "${{ github.repository }}" \ + --arg source_commit "${{ github.sha }}" \ + --arg git_tag_prefix "helm" \ + '{dispatch_id: "", file: $file, yq_tpl: $yq_tpl, version: $version, source_repo: $source_repo, source_commit: $source_commit, git_tag_prefix: $git_tag_prefix}') + OUTPUT=$(bash .ci/scripts/dispatch-workflow.sh \ + "niko/gitea-ci-gitops-tests" "gitops-service.yaml" "main" \ + "$INPUTS" "${{ fromJson(needs.load-config.outputs.env_json).GITEA_API_URL }}" \ + "${{ secrets.GITOPS_DISPATCH_TOKEN }}" "30") + echo "$OUTPUT" + echo "helm_commit=$(echo "$OUTPUT" | grep '^GITOPS_COMMIT=' | cut -d= -f2)" >> "$GITHUB_OUTPUT" + +gitops-docker: + needs: [docker-build-push] + if: success() + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + with: + repository: niko/gitea-ci-library + path: .ci + - name: Update docker tag + id: docker + run: | + INPUTS=$(jq -nc \ + --arg file "dev/values.yaml" \ + --arg yq_tpl '.service.tag = "{{VERSION}}"' \ + --arg version "${{ needs.check-version.outputs.version }}" \ + --arg source_repo "${{ github.repository }}" \ + --arg source_commit "${{ github.sha }}" \ + --arg git_tag_prefix "docker" \ + '{dispatch_id: "", file: $file, yq_tpl: $yq_tpl, version: $version, source_repo: $source_repo, source_commit: $source_commit, git_tag_prefix: $git_tag_prefix}') + OUTPUT=$(bash .ci/scripts/dispatch-workflow.sh \ + "niko/gitea-ci-gitops-tests" "gitops-service.yaml" "main" \ + "$INPUTS" "${{ fromJson(needs.load-config.outputs.env_json).GITEA_API_URL }}" \ + "${{ secrets.GITOPS_DISPATCH_TOKEN }}" "30") + echo "$OUTPUT" + echo "docker_commit=$(echo "$OUTPUT" | grep '^GITOPS_COMMIT=' | cut -d= -f2)" >> "$GITHUB_OUTPUT" +``` + +Kaksi dispatchia, kaksi eri tiedostoa, kaksi eri `GIT_TAG_PREFIX`-arvoa. +Kummallakin on oma commit-status-linja ja oma summary-rivi. +`dispatch-workflow.sh` hoitaa rinnakkaisuuden `display_title`-matchauksella. + +**GITEA_TOKEN dispatch-vaiheessa:** Tarvitaan manuaalinen token, +jolla on **write-oikeus GitOps-repoon** (esim. org-tason token). +Code-repon auto-token ei oikeuta dispatchaamaan toiseen repoon. +Token luodaan Giteassa: `Settings → Applications → Generate Token` +ja asetetaan code-repoon Actions Secretiksi. + +### Commit-status dispatchin perusteella + +`dispatch-workflow.sh` tulostaa `GITOPS_COMMIT=` stdoutiin onnistuneen +GitOps-päivityksen jälkeen. Code repo parsii sen ja asettaa commit-statusin +linkillä GitOps-committiin: + +```yaml + - name: Set commit-status with GitOps link + if: always() + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + GITEA_API_URL: ${{ fromJson(needs.load-config.outputs.env_json).GITEA_API_URL }} + GITOPS_COMMIT: ${{ steps.dispatch.outputs.gitops_commit }} + VERSION: ${{ needs.check-version.outputs.version }} + run: | + GITOPS_URL="${GITEA_API_URL}/niko/agent-platform-gitops/commit/${GITOPS_COMMIT}" + CTX="gitops/$(basename ${{ github.repository }})" + DESC="Deploy to dev ${VERSION}" + if [ -n "$GITOPS_COMMIT" ]; then + bash .ci/scripts/report-status.sh success "$DESC" "$CTX" "" "$GITOPS_URL" + else + bash .ci/scripts/report-status.sh success "$DESC" "$CTX" + fi +``` + +`dispatch-workflow.sh` palauttaa: +- exit 0 = GitOps-päivitys onnistui (+ `GITOPS_COMMIT=`) +- exit 1 = GitOps-päivitys failasi +- exit 124 = aikakatkaisu (360 min oletus) + +### Loppuraportti (report-summary) + +Code-repon viimeinen job (`report-summary`) lisää GitOps-päivityksestä +rivin GITHUB_STEP_SUMMARYyn: + +```yaml + - name: GitOps summary + if: always() + env: + GITEA_API_URL: ${{ fromJson(needs.load-config.outputs.env_json).GITEA_API_URL }} + GITOPS_COMMIT: ${{ steps.dispatch.outputs.gitops_commit }} + VERSION: ${{ needs.check-version.outputs.version }} + run: | + if [ -n "$GITOPS_COMMIT" ]; then + LINK="${GITEA_API_URL}/niko/agent-platform-gitops/commit/${GITOPS_COMMIT}" + else + LINK="#" + fi + cat >> "$GITHUB_STEP_SUMMARY" << 'GITOPS' + + ## GitOps updates + + | Component | Version | Status | Commit | + |-----------|---------|--------|--------| + | agent-platform-helm | __VERSION__ | __STATUS__ | [link](__LINK__) | + GITOPS + sed -i "s|__VERSION__|${VERSION}|; s|__STATUS__|${{ job.status }}|; s|__LINK__|${LINK}|" \ + "$GITHUB_STEP_SUMMARY" +``` + +## Secretit ja tokenit + +| Secret | Missä | Scope | Kuvaus | +|--------|-------|-------|--------| +| `GITEA_TOKEN` (auto) | Code repo | Vain code repo | Asettaa commit-statusin dispatchin jälkeen | +| `GITEA_TOKEN` (auto) | GitOps repo | Vain GitOps repo | Klooni, push, commit-status GitOps-repossa | +| `GITOPS_DISPATCH_TOKEN` (manuaalinen) | Code repo | Write GitOps-repoon | Dispatchaa GitOps-repon workflow | + +**Tokenin luonti:** +1. Gitea → `Settings` → `Applications` → `Generate Token` +2. Valitse repo-oikeudet: valitse GitOps-repo, anna write-oikeudet +3. Token asetetaan code-repoon: `{repo} → Settings → Actions Secrets` +4. Salaisuuden nimi: esim. `GITOPS_DISPATCH_TOKEN` + +## Provider-skriptit + +### `scripts/gitops-update.sh` + +Ajaan GitOps-repon workflow'ssa. Päivittää konfiguraatiotiedoston yq:llä, +committaa ja pushaa. Asettaa commit-statuksen vain GitOps-repoon. + +**Input-ympäristömuuttujat:** + +| Muuttuja | Pakollinen | Kuvaus | +|---|---|---| +| `INPUT_FILE` | Kyllä | Tiedosto GitOps-repossa (esim. `dev/Chart.yaml`) | +| `YQ_TPL` | Kyllä | yq-lauseke `{{VERSION}}`-placeholderilla | +| `VERSION` | Kyllä | Uusi versio (esim. `0.2.3`) | +| `SOURCE_REPO` | Kyllä | Code-repo slug (esim. `org/app`) | +| `SOURCE_COMMIT` | Kyllä | Code-repon commit SHA | +| `GITOPS_REPO` | Kyllä | GitOps-repo slug | +| `GITEA_API_URL` | Kyllä | Gitean API-URL | +| `GITEA_TOKEN` | Kyllä | Gitea API-token (write GitOps-repoon) | +| `GITOPS_BRANCH` | Ei | Branch (oletus `main`) | +| `GIT_TAG_PREFIX` | Ei | Komponentin tag-prefix status-nimeämiseen (esim. `agent-platform-helm`) | +| `GITOPS_CLONE_URL` | Ei | Yliajaa clone-URL (esim. eri protokolla) | +| `GITOPS_TARGET_DIR` | Ei | Yliajaa clone-kohdehakemisto | + +**Commit-status muoto:** + +GitOps-repoon asetetaan commit-status: + +| Kenttä | Formaatti | Esimerkki | +|--------|-----------|-----------| +| Context | `{repo}/{GIT_TAG_PREFIX} {RUN_ID}` tai `{repo} {RUN_ID}` | `gitea-ci-library/agent-platform-helm 473` | +| Description | `Install to {env} {version}` | `Install to dev 0.2.0` | +| Target URL | Linkki code-repon committiin | `/niko/gitea-ci-library/commit/abc123` | + +Jos tiedosto on jo halutussa versiossa (ei muutoksia), status saa descriptionin `Install to {env} {version} — no change`. Commit-pushia ei tehdä, GitOps-repo pysyy muuttumattomana. + +- `{env}` parsitaan `INPUT_FILE`:stä (`dev/Chart.yaml` → `dev`) +- `{repo}` parsitaan `SOURCE_REPO`:sta (`niko/gitea-ci-library` → `gitea-ci-library`) +- `{GIT_TAG_PREFIX}` tulee env-varista (sama kuin `gitea-env.conf`:ssa) + +### `scripts/dispatch-workflow.sh` + +Dispatchaa workflow_dispatchin kohderepoon ja pollaa valmistumista. +Generoi automaattisesti `dispatch_id`-tunnisteen, lisää sen dispatch- +inputteihin ja tunnistaa workflow-runin kohdereposta `display_title`- +kentän perusteella. Toimii luotettavasti vaikka samassa repossa olisi +useita samanaikaisia dispatch-attribuutioita. + +**Argumentit:** + +| # | Pakollinen | Kuvaus | +|---|------------|--------| +| 1 | Kyllä | Kohderepo (esim. `niko/agent-platform-gitops`) | +| 2 | Kyllä | Workflow-tiedosto (esim. `ci-main.yml`) | +| 3 | Kyllä | Branch/ref | +| 4 | Kyllä | Inputs JSON | +| 5 | Kyllä | Gitea API URL | +| 6 | Kyllä | Gitea token | +| 7 | Ei | Aikakatkaisu minuutteina (oletus 360) | + +Kutsujan ei tarvitse välittää `dispatch_id`:tä — skripti generoi sen +itse ja lisää inputteihin ennen dispatchia. + +## [skip ci] + +Commit-viestissä on `[skip ci]`, joka estää GitActions-runneria +triggeröimästä uutta CI-ajoa GitOps-repoon pushista. Näin vältetään +ääretön trigger-loop. + +## Race condition + +`dispatch-workflow.sh` tunnistaa jokaisen dispatchatun runin uniikilla +`dispatch_id`-tunnisteella `display_title`-kentästä. Vaikka useampi +artifakti dispatchaisi samaan aikaan ja useita workflow-runeja olisi +käynnissä rinnakkain, jokainen skripti löytää oikean runinsa. + +## Sääntöjä + +1. **Token ei kirjoita code repoon.** GitOps-repon workflow ei tarvitse + oikeuksia code repoon. Kaikki status-kutsut kohdistuvat vain + GitOps-repoon. Code repo asettaa oman statusnsa itse. +2. **Ei provider-workflowta.** GitOps-päivitys ei ole reusable workflow. + GitOps-repo ajaa `scripts/gitops-update.sh`:n suoraan. +3. **Vain `workflow_dispatch`.** GitOps-repon workflow:ta ei triggeröidä + pushista — se laukeaa vain dispatch-kutsusta. +4. **Dispatch ei palauta tarkkaa SHA:**ta. Code repo ei tiedä GitOps- + commitin SHA:ta ennen dispatch-valmistumista. Status asetetaan + dispatchin exit-koodin perusteella, ei GitOps-commitin tiedoilla. +5. **`dispatch_id` on pakollinen kohde-workflow'ssa** — ilman sitä + `dispatch-workflow.sh` ei löydä oikeaa runia moniajo-tilanteessa. +6. **`[skip ci]` commit-viestissä.** Pakollinen trigger-loopin estoon.