Dave Kowalski 70e1a06816
validate / validate (pull_request) Successful in 4s
fix(image-deploy): monotonic tag guard — refuse backwards newTag writes
Two concurrent release runs can race such that the older build's
image-deploy step finishes after the newer build already committed its
tag. Without an ordering check, last-writer-wins and the manifest
silently reverts to a stale image.

Add _monotonic_check: reads origin/main's current newTag for the image
and exits 0 (with a loud SKIP log) if it is numerically greater than the
tag we are trying to write. The guard runs before the kustomize edit and
again after each rebase in the push-retry loop, so both race shapes are
covered:
- Clone-then-lose: second clone already sees the newer tag → initial
  guard fires before any commit.
- Concurrent-push: both cloned the same base; one pushes first; the
  other rebases, then the post-rebase guard fires before the re-push.

Fixes bug-313bg8bgezp3
2026-08-24 22:59:36 +00:00

action/image-deploy

Composite Gitea Action that pins an image tag in fritzlab/apps via kustomize edit set image, validates the rendered manifests, and pushes to apps-repo main. Retries on push conflict.

This is the standard "deploy" step for image producers (chrony, profiles, runner). After image-build + image-push, this writes the new tag into the GitOps target so ArgoCD can sync it.

Usage

- uses: actions/checkout@v4
- uses: https://code.fritzlab.net/action/image-build@v1
  with:
    image: code.fritzlab.net/fritzlab/chrony
    smoke-test: docker run --rm --entrypoint /usr/sbin/chronyd $IMAGE -v
- uses: https://code.fritzlab.net/action/image-push@v1
  with:
    image: code.fritzlab.net/fritzlab/chrony
    token: ${{ secrets.CI_BOT_TOKEN }}
    org: fritzlab
    name: chrony
- uses: https://code.fritzlab.net/action/image-deploy@v1
  with:
    image: code.fritzlab.net/fritzlab/chrony
    path: sjc001/infra/chrony/manifests
    token: ${{ secrets.CI_BOT_TOKEN }}

Inputs

Name Required Default Description
image yes Full image name without tag. Must match an entry already in the target kustomization.yaml images: block.
tag no github.run_number Tag to pin.
path yes Path inside fritzlab/apps to the manifests dir (e.g. sjc001/infra/chrony/manifests).
token yes CI_BOT_TOKEN with write access to fritzlab/apps.
apps-repo no code.fritzlab.net/fritzlab/apps Apps repo URL without protocol.
message no deploy <name> #<tag> Commit message override.

Behavior

  1. Shallow-clone fritzlab/apps to a temp dir.
  2. cd <path> and run kustomize edit set image <image>=<image>:<tag>.
  3. Run kustomize build . to validate the manifests still render. Fails the workflow if validation breaks — apps repo is left untouched.
  4. If no diff (apps repo already on this tag): exit 0 silently.
  5. Query the Gitea API for open PRs in the apps repo. If any open PR modifies the same kustomization.yaml and its patch contains the target tag, exit 0 — the PR is the intended control gate and the direct push is skipped. Fails open on API errors (push proceeds) to avoid blocking deploys during an outage.
  6. Otherwise commit + push to main. On push rejection (concurrent CI race), git pull --rebase and retry up to 3 times with linear backoff.

Notes

  • The image entry must already exist in kustomization.yaml. This action only updates the tag; it does not add or remove image entries. To switch image registry paths, edit kustomization.yaml by hand once, commit, then let CI resume.
  • git diff --quiet skip means re-running the same CI run (or an earlier run) is idempotent.
S
Description
fritzlab composite action: image-deploy
Readme
35 KiB