Files
fuj-management/docs/plans/2026-06-12-1927-gitops-pr-action.md
Jan Novak 995abfacb2
All checks were successful
Deploy to K8s / deploy (push) Successful in 12s
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 <noreply@anthropic.com>
2026-06-12 19:28:29 +02:00

6.1 KiB

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, tagged gitea.home.hrajfrisbee.cz/kacerr/fuj-management:<git-tag>-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-<name>-<timestamp>, 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-<version>-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 }}:<tag>-go.
  4. Configure git identitygit config --global user.name/email for the bot.
  5. Authenticate teatea 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 "<resolved 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/<ts>-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.)