From d48a7f805efbaea23fc5e1f0c85c0de65448edf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bauer?= Date: Tue, 30 Jun 2026 15:42:54 +0200 Subject: [PATCH] write non-timestamped tag to GitOps repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Bauer --- README.md | 21 +++++++++++++++-- action.yml | 2 +- scripts/generate-tags.sh | 11 +++++++++ tests/generate-tags.bats | 49 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8280cb8..5ffbe93 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ Whenever the action updates a GitOps file, it stamps the following annotations o |------------|-------| | `deploy.staffbase.com/repositoryFullName` | The source repository in `owner/repo` form (`$GITHUB_REPOSITORY`) | | `deploy.staffbase.com/commitSha` | The commit SHA being deployed (`$GITHUB_SHA`) | -| `deploy.staffbase.com/version` | The deployed image tag — `dev--` on `dev`, `main--` on `main`, `master--` on `master` (the UTC timestamp is inserted by default; with `docker-tag-timestamp: 'false'` it falls back to `-`), the version without the leading `v` on `v*` tag pushes, and the tag name on other tag pushes | +| `deploy.staffbase.com/version` | The image tag written to the GitOps repo — always the **non-timestamped** tag: `dev-` on `dev`, `main-` on `main`, `master-` on `master`, the version without the leading `v` on `v*` tag pushes, and the tag name on other tag pushes. See [GitOps tag](#gitops-tag) below | These keys mirror the [Swarmia Deployment API](https://help.swarmia.com/settings/organization/configuring-deployments-in-swarmia) field names and are read by `flux-deployment-reporter` to report deployments to Swarmia once Flux finishes reconciling. @@ -176,10 +176,27 @@ to fall back to the legacy `-` shape. > **Note:** with the timestamp enabled (the default) the build also pushes the plain > `-` tag alongside the timestamped one. That stable per-commit -> tag is what the release step retags into the version tag, so it must continue +> tag is what the release step retags into the version tag and what the action +> writes to the GitOps repo (see [GitOps tag](#gitops-tag)), so it must continue > to exist. It does not match the `^-[0-9]+-[0-9a-f]+$` filter below, so > Flux ignores it. +### GitOps tag + +The tag the action **builds and pushes** is the timestamped one (so Flux image +automation can sort it). The tag the action **writes to the external GitOps repo** +(`gitops-dev`/`gitops-stage`/`gitops-prod` files and the deployment annotations) is +always the **non-timestamped** tag — the stable `-` alias for +branch builds, and the plain tag for `v*`/custom builds (which never carry a +timestamp). + +This is deliberate and not configurable. When the action runs across separate +invocations — e.g. one step builds the image and a later step pushes and updates +GitOps — each invocation recomputes a *fresh* timestamp, so the timestamped tag +differs between them. The `-` alias is deterministic, so the +GitOps reference stays consistent and always points at an image that was actually +pushed. + With the timestamp enabled, use one `ImagePolicy` per environment, filtering by prefix: ```yaml diff --git a/action.yml b/action.yml index af4abae..d7a1932 100644 --- a/action.yml +++ b/action.yml @@ -195,7 +195,7 @@ runs: env: INPUT_DOCKER_REGISTRY: ${{ inputs.docker-registry }} INPUT_DOCKER_IMAGE: ${{ inputs.docker-image }} - INPUT_TAG: ${{ steps.preparation.outputs.tag }} + INPUT_TAG: ${{ steps.preparation.outputs.gitops_tag }} INPUT_PUSH: ${{ steps.preparation.outputs.push }} INPUT_GITOPS_USER: ${{ inputs.gitops-user }} INPUT_GITOPS_EMAIL: ${{ inputs.gitops-email }} diff --git a/scripts/generate-tags.sh b/scripts/generate-tags.sh index 4d48828..4a80dce 100755 --- a/scripts/generate-tags.sh +++ b/scripts/generate-tags.sh @@ -95,8 +95,19 @@ if [[ -n "${LATEST:-}" ]]; then TAG_LIST+=",${INPUT_DOCKER_REGISTRY}/${INPUT_DOCKER_IMAGE}:${LATEST}" fi +# GITOPS_TAG is the tag written to the external GitOps repo. It is always the +# non-timestamped tag: the stable - alias for branch builds +# (ALIAS_TAG), and the plain TAG for release/custom builds (which have no +# timestamp anyway). Decoupling it from the timestamped image TAG avoids a +# mismatch when the action runs in separate invocations (e.g. build then push): +# each invocation recomputes a fresh timestamp for TAG, but the alias is +# deterministic, so the GitOps reference stays consistent and points at an image +# that was actually pushed. +GITOPS_TAG="${ALIAS_TAG:-$TAG}" + set_output "build" "$BUILD" set_output "latest" "${LATEST:-}" set_output "push" "$PUSH" set_output "tag" "$TAG" set_output "tag_list" "$TAG_LIST" +set_output "gitops_tag" "$GITOPS_TAG" diff --git a/tests/generate-tags.bats b/tests/generate-tags.bats index e11ce43..d0358fe 100644 --- a/tests/generate-tags.bats +++ b/tests/generate-tags.bats @@ -282,6 +282,55 @@ teardown() { [[ "$tag_list" == "registry.staffbase.com/my-service:abcdef12" ]] } +# --- gitops_tag (always non-timestamped; written to the external GitOps repo) --- + +@test "gitops_tag drops the timestamp on dev branch (default)" { + export GITHUB_REF="refs/heads/dev" + run "$SCRIPT" + assert_success + assert_output_value "tag" "dev-20260602143055-abcdef12" + assert_output_value "gitops_tag" "dev-abcdef12" +} + +@test "gitops_tag drops the timestamp on main branch (default)" { + export GITHUB_REF="refs/heads/main" + run "$SCRIPT" + assert_success + assert_output_value "tag" "main-20260602143055-abcdef12" + assert_output_value "gitops_tag" "main-abcdef12" +} + +@test "gitops_tag equals tag when timestamp flag is off" { + export INPUT_DOCKER_TAG_TIMESTAMP="false" + export GITHUB_REF="refs/heads/main" + run "$SCRIPT" + assert_success + assert_output_value "tag" "main-abcdef12" + assert_output_value "gitops_tag" "main-abcdef12" +} + +@test "gitops_tag equals tag for version (prod) tags" { + export GITHUB_REF="refs/tags/v1.2.3" + run "$SCRIPT" + assert_success + assert_output_value "gitops_tag" "1.2.3" +} + +@test "gitops_tag equals tag for custom tags" { + export GITHUB_REF="refs/heads/main" + export INPUT_DOCKER_CUSTOM_TAG="my-custom-tag" + run "$SCRIPT" + assert_success + assert_output_value "gitops_tag" "my-custom-tag" +} + +@test "gitops_tag equals tag for feature branches" { + export GITHUB_REF="refs/heads/feature/my-feature" + run "$SCRIPT" + assert_success + assert_output_value "gitops_tag" "abcdef12" +} + # --- validation --- @test "fails when GITHUB_REF is missing" {