From 995abfacb275ec10270c80a9f8fd80822640465c Mon Sep 17 00:00:00 2001 From: Jan Novak Date: Fri, 12 Jun 2026 19:28:29 +0200 Subject: [PATCH] feat(ci): add gitops-update workflow to open image-bump PR in home-kubernetes After a successful Go image build, uh-cli opens a PR against kacerr/home-kubernetes that bumps the fuj-management Deployment (namespace fuj) to the newly published image tag. Supports workflow_run auto-trigger and workflow_dispatch with dry-run option. Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/gitops-update.yaml | 94 +++++++++++++++ .../plans/2026-06-12-1927-gitops-pr-action.md | 112 ++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 .gitea/workflows/gitops-update.yaml create mode 100644 docs/plans/2026-06-12-1927-gitops-pr-action.md diff --git a/.gitea/workflows/gitops-update.yaml b/.gitea/workflows/gitops-update.yaml new file mode 100644 index 0000000..19f32e1 --- /dev/null +++ b/.gitea/workflows/gitops-update.yaml @@ -0,0 +1,94 @@ +name: GitOps image update + +on: + # Auto-fires when "Build and Push" completes successfully (tag push). + workflow_run: + workflows: ["Build and Push"] + types: [completed] + + # Manual trigger for dry-runs and one-off bumps. + workflow_dispatch: + inputs: + tag: + description: "Git tag to deploy (without the -go suffix, e.g. 0.37)" + required: true + dry_run: + description: "Dry run — print diff, do not open a PR" + type: boolean + default: false + uh_cli_version: + description: "uh-cli version override (e.g. v0.2.0). Defaults to v0.1.0." + required: false + +env: + TEA_VERSION: "0.9.2" + # Resolved priority: manual input → repo/org variable → hardcoded default. + UH_CLI_VERSION: ${{ inputs.uh_cli_version || vars.UH_CLI_VERSION || 'v0.1.0' }} + +jobs: + gitops-pr: + runs-on: ubuntu-latest + # Skip if triggered by workflow_run that did not succeed. + if: > + github.event_name == 'workflow_dispatch' || + github.event.workflow_run.conclusion == 'success' + container: + image: ubuntu:latest + + env: + GITEA_TOKEN: ${{ secrets.GITOPS_TOKEN }} + + steps: + - name: Install git, curl, ca-certificates + run: | + apt-get update -qq + apt-get install -y --no-install-recommends git curl ca-certificates + + - name: Install tea + run: | + curl -fsSL \ + "https://gitea.com/gitea/tea/releases/download/v${TEA_VERSION}/tea-${TEA_VERSION}-linux-amd64" \ + -o /usr/local/bin/tea + chmod +x /usr/local/bin/tea + + - name: Install uh-cli + run: | + curl -fsSL \ + "https://gitea.home.hrajfrisbee.cz/kacerr/uh-cli/releases/download/${UH_CLI_VERSION}/uh-cli-${UH_CLI_VERSION}-linux-amd64" \ + -o /usr/local/bin/uh-cli + chmod +x /usr/local/bin/uh-cli + + - name: Resolve image tag + id: resolve + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + GIT_TAG="${{ inputs.tag }}" + else + # workflow_run: use the ref name of the triggering workflow (the pushed git tag). + GIT_TAG="${{ github.event.workflow_run.head_branch }}" + fi + IMAGE="gitea.home.hrajfrisbee.cz/${{ github.repository }}:${GIT_TAG}-go" + echo "image=${IMAGE}" >> "$GITHUB_OUTPUT" + echo "Resolved image: ${IMAGE}" + + - name: Configure git identity + run: | + git config --global user.name "uh-cli bot" + git config --global user.email "bot@hrajfrisbee.cz" + + - name: Authenticate tea + run: | + tea login add \ + --name ci \ + --url https://gitea.home.hrajfrisbee.cz \ + --token "$GITEA_TOKEN" + + - name: Open image-update PR (or dry run) + run: | + uh-cli -v gitops deployment update \ + --deployment-name fuj-management \ + --deployment-namespace fuj \ + --set-image "${{ steps.resolve.outputs.image }}" \ + --git-repo "https://kacerr:${GITEA_TOKEN}@gitea.home.hrajfrisbee.cz/kacerr/home-kubernetes" \ + --git-path gitops/home-kubernetes \ + ${{ (github.event_name == 'workflow_dispatch' && inputs.dry_run == 'true') && '--dry-run' || '' }} diff --git a/docs/plans/2026-06-12-1927-gitops-pr-action.md b/docs/plans/2026-06-12-1927-gitops-pr-action.md new file mode 100644 index 0000000..58097ce --- /dev/null +++ b/docs/plans/2026-06-12-1927-gitops-pr-action.md @@ -0,0 +1,112 @@ +# Plan: Gitea Action to open a gitops image-update PR for fuj-management + +## Context + +The Go image of this app is built and pushed by the `build-go` job in +[.gitea/workflows/build.yaml](.gitea/workflows/build.yaml), tagged +`gitea.home.hrajfrisbee.cz/kacerr/fuj-management:-go` (e.g. `0.37-go`). + +Kubernetes manifests live in a **separate** repo, +`gitea.home.hrajfrisbee.cz/kacerr/home-kubernetes`. Today, bumping the image in +the `fuj-management` Deployment (namespace `fuj`) is a manual edit there. + +We want CI to automate that bump: when a new Go image is built, open a PR against +`home-kubernetes` that swaps the image to the freshly built tag — using the +`uh-cli gitops deployment update` command. The user reviews/merges that PR in +Gitea (matching the existing branch-per-change, merge-in-browser workflow). + +Decisions confirmed with the user: +- **Separate workflow file** (not a job inside build.yaml). +- **New `GITOPS_TOKEN` secret** for home-kubernetes write + PR access. +- **uh-cli version pinned with a default, overridable via env/var/input.** + +## How uh-cli works (from `/Users/jan.novak/srv/go/uh-cli/docs/`) + +- `uh-cli gitops deployment update` clones `--git-repo`, walks `--git-path` + recursively for a `kind: Deployment` whose `metadata.name`/`namespace` match, + edits the first container image surgically, commits on a new branch + `gitops/update--`, pushes, and **opens the PR itself** via + `tea pr create`. PR base is always `main`; title/body are hardcoded (no flags). +- Requires on PATH: `git` and `tea` (tea only for the PR flow; `--force` skips it). +- Auth: token embedded in the `--git-repo` URL (`https://user:TOKEN@host/...`); + `tea login add` for PR creation; git identity via `git config`/env vars. +- `--dry-run` prints the unified diff and makes no git changes. Global `-v` + (placed **before** the subcommand) enables debug logging on stderr. +- Release binaries are named `uh-cli--linux-amd64` (version includes the + `v`), attached to the Gitea release. Latest tag today is **`v0.1.0`**. + +## Change: new workflow `.gitea/workflows/gitops-update.yaml` + +Triggers: +- `workflow_run` on `workflows: ["Build and Push"]`, `types: [completed]`, gated + to `conclusion == 'success'` — auto-fires after the image build succeeds. +- `workflow_dispatch` with inputs: `tag` (git tag without the `-go` suffix, e.g. + `0.37`), `dry_run` (boolean, default false), `uh_cli_version` (optional override). + +Single job `gitops-pr`, `runs-on: ubuntu-latest`, in a `container: ubuntu:latest` +for a hermetic install (matches the uh-cli CI doc pattern). Steps: + +1. **Install git, curl, ca-certificates, tea** — apt-get + download tea + `0.9.2` from `gitea.com/gitea/tea/releases/...` to `/usr/local/bin/tea`. +2. **Install uh-cli** — download + `https://gitea.home.hrajfrisbee.cz/kacerr/uh-cli/releases/download/${UH_CLI_VERSION}/uh-cli-${UH_CLI_VERSION}-linux-amd64` + to `/usr/local/bin/uh-cli`. + `UH_CLI_VERSION: ${{ inputs.uh_cli_version || vars.UH_CLI_VERSION || 'v0.1.0' }}`. +3. **Resolve image tag** — if `workflow_dispatch`, use `inputs.tag`; else use + `github.event.workflow_run.head_branch` (the pushed tag name). Output + `gitea.home.hrajfrisbee.cz/${{ github.repository }}:-go`. +4. **Configure git identity** — `git config --global user.name/email` for the bot. +5. **Authenticate tea** — `tea login add --name ci --url https://gitea.home.hrajfrisbee.cz --token "$GITEA_TOKEN"`. +6. **Open image-update PR** — run, with `--dry-run` appended only when the + dispatch `dry_run` input is true: + ``` + uh-cli -v gitops deployment update \ + --deployment-name fuj-management \ + --deployment-namespace fuj \ + --set-image "" \ + --git-repo "https://fuj-gitops-bot:${GITEA_TOKEN}@gitea.home.hrajfrisbee.cz/kacerr/home-kubernetes" \ + --git-path gitops/home-kubernetes + ``` + +`GITEA_TOKEN` is sourced from `secrets.GITOPS_TOKEN` at job level. + +Job-level guard: `if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}`. + +## Prerequisites (user must set up in Gitea — call out in handoff) + +1. **Create `GITOPS_TOKEN` secret** in the `fuj-management` repo: a Gitea token + for a user (`fuj-gitops-bot` or `kacerr`) that has **write + pull-request** + access to `kacerr/home-kubernetes`. The username in the `--git-repo` URL must + match that token's owner (adjust `fuj-gitops-bot` if using `kacerr`). +2. **uh-cli `v0.1.0` release assets must exist** (the `uh-cli-v0.1.0-linux-amd64` + binary attached to the release). If not yet published, cut that release in the + uh-cli repo first, or set `UH_CLI_VERSION` to a published tag. +3. **Confirm the manifest path**: `--git-path gitops/home-kubernetes` must contain + the `fuj-management` Deployment; `--deployment-namespace fuj` disambiguates. + Cannot verify from this repo — verify against home-kubernetes (narrow the path + if uh-cli reports an ambiguity error). + +## Files + +- **New**: `.gitea/workflows/gitops-update.yaml` (the workflow above). +- After it works: prepend a `CHANGELOG.md` entry; save this plan to + `docs/plans/-gitops-pr-action.md` per CLAUDE.md convention. + +## Branching + +Feature work → branch `feat/gitops-pr-action` off `main`, commit with the +`Co-Authored-By` trailer, push with `-u`, open the MR with +`tea pr create --base main --head feat/gitops-pr-action`. Do not merge from CLI. + +## Verification + +1. **Dry run (manual)**: trigger `gitops-update.yaml` via workflow_dispatch with + `tag=0.37`, `dry_run=true`. Confirm logs show the unified diff (image line + `…:0.37-go`) and `-v` debug milestones; **no PR** is created. +2. **Real run (manual)**: re-trigger with `dry_run=false`. Confirm a PR appears in + `home-kubernetes` against `main` with the image bump, and the PR URL is printed. +3. **Auto-trigger**: push a new git tag to fuj-management → `Build and Push` + completes → `gitops-update` fires via `workflow_run` and opens the PR. + (Note: `workflow_run`/`head_branch` behavior depends on this Gitea/act_runner + version; if it doesn't fire, manual `workflow_dispatch` is the fallback and the + plan still delivers the core capability.)