Skip to content

Commit 297c6eb

Browse files
committed
CORE-4588 - add deployment labels to docker image
Signed-off-by: André Bauer <andre.bauer@staffbase.com>
1 parent 6bfcdaa commit 297c6eb

8 files changed

Lines changed: 170 additions & 7 deletions

File tree

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,27 @@ jobs:
108108
109109
### Deployment tracking annotations
110110
111-
Whenever the action updates a GitOps file, it stamps the following annotations onto the manifest's `metadata.annotations`:
111+
By default (`deployment-annotations: 'true'`), whenever the action updates a GitOps file it stamps the following annotations onto the manifest's `metadata.annotations`:
112112

113113
| Annotation | Value |
114114
|------------|-------|
115115
| `deploy.staffbase.com/repositoryFullName` | The source repository in `owner/repo` form (`$GITHUB_REPOSITORY`) |
116116
| `deploy.staffbase.com/commitSha` | The commit SHA being deployed (`$GITHUB_SHA`) |
117117
| `deploy.staffbase.com/version` | The image tag written to the GitOps repo — always the **non-timestamped** tag: `dev-<short-sha>` on `dev`, `main-<short-sha>` on `main`, `master-<short-sha>` 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 |
118118

119-
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.
119+
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. Set `deployment-annotations: 'false'` to skip them. The `deploy.staffbase.com` namespace is configurable via [`deployment-domain`](#inputs).
120+
121+
### Deployment tracking labels
122+
123+
Enabled by default (`deployment-labels: 'true'`). The same three values are stamped onto the **built Docker image** as OCI labels, using reverse-DNS keys — the [`deployment-domain`](#inputs) reversed (`deploy.staffbase.com` → `com.staffbase.deploy`):
124+
125+
| Label | Value |
126+
|-------|-------|
127+
| `com.staffbase.deploy.repositoryFullName` | The source repository in `owner/repo` form (`$GITHUB_REPOSITORY`) |
128+
| `com.staffbase.deploy.commitSha` | The commit SHA being deployed (`$GITHUB_SHA`) |
129+
| `com.staffbase.deploy.version` | The **non-timestamped** GitOps tag (same value as the `deploy.staffbase.com/version` annotation) |
130+
131+
> **Note:** labels are baked in at **build time**, so they are only applied on builds. Release (`v*`) and custom-tag runs that **retag** an existing image instead of rebuilding (see [Image tags](#image-tags--flux-image-automation)) do not get fresh labels — the retagged image keeps the labels from the branch build it was promoted from. This feature is independent of the annotations above; enable either, both, or neither.
120132

121133
## Inputs
122134

@@ -138,6 +150,9 @@ These keys mirror the [Swarmia Deployment API](https://help.swarmia.com/settings
138150
| `docker-build-platforms` | Sets the target platforms for build | linux/amd64 |
139151
| `docker-build-provenance` | Generate [provenance](https://docs.docker.com/build/attestations/slsa-provenance/) attestation for the build | `false` |
140152
| `docker-disable-retagging` | Disables retagging of existing images and run a new build instead | `false` |
153+
| `deployment-annotations` | Stamp deployment-tracking annotations (`deploy.staffbase.com/*`) onto updated GitOps manifests. See [Deployment tracking annotations](#deployment-tracking-annotations) | `true` |
154+
| `deployment-domain` | Key namespace for deployment-tracking metadata. Used verbatim for annotation keys (`<domain>/...`) and reversed to reverse-DNS for label keys (`com.staffbase.deploy.*`) | `deploy.staffbase.com` |
155+
| `deployment-labels` | Stamp deployment-tracking labels (`com.staffbase.deploy.*`) onto the built image. Only applied on builds, not on release/custom retags. See [Deployment tracking labels](#deployment-tracking-labels) | `true` |
141156
| `gitops-organization` | GitHub Organization for GitOps | `Staffbase` |
142157
| `gitops-repository` | GitHub Repository for GitOps | `mops` |
143158
| `gitops-user` | GitHub User for GitOps | `Staffbot` |

action.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ inputs:
6262
description: 'Disable retagging of existing images'
6363
required: false
6464
default: 'false'
65+
deployment-annotations:
66+
description: 'Stamp deployment-tracking annotations (deploy.staffbase.com/repositoryFullName, /commitSha, /version) onto the updated GitOps manifests. Enabled by default; set to ''false'' to skip.'
67+
required: false
68+
default: 'true'
69+
deployment-domain:
70+
description: 'Domain used as the key namespace for deployment-tracking metadata. Applied verbatim to GitOps annotation keys (<domain>/repositoryFullName) and reversed to reverse-DNS for image label keys (deploy.staffbase.com -> com.staffbase.deploy.repositoryFullName).'
71+
required: false
72+
default: 'deploy.staffbase.com'
73+
deployment-labels:
74+
description: 'Stamp deployment-tracking labels (repositoryFullName, commitSha, version) onto the built Docker image. Enabled by default; only applied on builds, not on release/custom retags. Set to ''false'' to skip.'
75+
required: false
76+
default: 'true'
6577
gitops-organization:
6678
description: 'GitHub Organization for GitOps'
6779
required: true
@@ -127,6 +139,13 @@ runs:
127139
INPUT_DOCKER_IMAGE: ${{ inputs.docker-image }}
128140
run: ${{ github.action_path }}/scripts/generate-tags.sh
129141

142+
- name: Generate Deployment Label Prefix
143+
id: deployment_labels
144+
shell: bash
145+
env:
146+
INPUT_DEPLOYMENT_DOMAIN: ${{ inputs.deployment-domain }}
147+
run: ${{ github.action_path }}/scripts/generate-label-prefix.sh
148+
130149
- name: Verify Architecture Match
131150
shell: bash
132151
if: steps.preparation.outputs.build == 'true'
@@ -158,6 +177,10 @@ runs:
158177
target: ${{ inputs.docker-build-target }}
159178
build-args: ${{ inputs.docker-build-args }}
160179
tags: ${{ steps.preparation.outputs.tag_list }}
180+
labels: |
181+
${{ inputs.deployment-labels == 'true' && format('{0}.repositoryFullName={1}', steps.deployment_labels.outputs.label_prefix, github.repository) || '' }}
182+
${{ inputs.deployment-labels == 'true' && format('{0}.commitSha={1}', steps.deployment_labels.outputs.label_prefix, github.sha) || '' }}
183+
${{ inputs.deployment-labels == 'true' && format('{0}.version={1}', steps.deployment_labels.outputs.label_prefix, steps.preparation.outputs.gitops_tag) || '' }}
161184
secrets: ${{ inputs.docker-build-secrets }}
162185
secret-files: ${{ inputs.docker-build-secret-files }}
163186
platforms: ${{ inputs.docker-build-platforms }}
@@ -205,6 +228,8 @@ runs:
205228
INPUT_GITOPS_DEV: ${{ inputs.gitops-dev }}
206229
INPUT_GITOPS_STAGE: ${{ inputs.gitops-stage }}
207230
INPUT_GITOPS_PROD: ${{ inputs.gitops-prod }}
231+
INPUT_DEPLOYMENT_ANNOTATIONS: ${{ inputs.deployment-annotations }}
232+
INPUT_DEPLOYMENT_DOMAIN: ${{ inputs.deployment-domain }}
208233
run: ${{ github.action_path }}/scripts/update-gitops.sh
209234

210235
- name: Emit Image Build Event to Upwind.io

scripts/generate-label-prefix.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
# Computes the reverse-DNS key prefix for deployment-tracking image labels,
3+
# derived from the deployment domain (deploy.staffbase.com -> com.staffbase.deploy).
4+
# Emitted as the `label_prefix` output and consumed by the Build step's image
5+
# `labels` input. The matching GitOps annotations use the forward "<domain>/..."
6+
# form instead (see scripts/lib/gitops-functions.sh).
7+
#
8+
# Optional env vars: INPUT_DEPLOYMENT_DOMAIN (defaults to "deploy.staffbase.com")
9+
10+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11+
# shellcheck source=lib/common.sh
12+
source "${SCRIPT_DIR}/lib/common.sh"
13+
14+
set_output "label_prefix" "$(reverse_domain "${INPUT_DEPLOYMENT_DOMAIN:-deploy.staffbase.com}")"

scripts/lib/common.sh

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,23 @@ set_output() {
3030
fi
3131
}
3232

33-
# --- Validation ---
33+
# reverse_domain turns a dotted domain into reverse-DNS order
34+
# (deploy.staffbase.com -> com.staffbase.deploy). Used to derive OCI image
35+
# label keys from the same deployment domain used verbatim for GitOps annotations.
36+
reverse_domain() {
37+
local domain="$1"
38+
local IFS='.'
39+
local -a parts
40+
read -ra parts <<< "$domain"
41+
local out="" i
42+
for (( i=${#parts[@]}-1; i>=0; i-- )); do
43+
out+="${parts[i]}"
44+
(( i > 0 )) && out+="."
45+
done
46+
printf '%s' "$out"
47+
}
3448

49+
# --- Validation ---
3550
require_env() {
3651
local var_name="$1"
3752
if [[ -z "${!var_name:-}" ]]; then

scripts/lib/gitops-functions.sh

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
# INPUT_DOCKER_REGISTRY, INPUT_DOCKER_IMAGE, INPUT_TAG, INPUT_PUSH,
77
# INPUT_GITOPS_USER, INPUT_GITOPS_TOKEN,
88
# INPUT_GITOPS_ORGANIZATION, INPUT_GITOPS_REPOSITORY,
9+
# INPUT_DEPLOYMENT_ANNOTATIONS (optional, defaults to "true"),
10+
# INPUT_DEPLOYMENT_DOMAIN (optional, defaults to "deploy.staffbase.com"),
911
# GITHUB_REPOSITORY, GITHUB_SHA, IMAGE
1012

1113
push_to_gitops_repo() {
@@ -48,10 +50,15 @@ update_file() {
4850
fi
4951
fi
5052

51-
echo "Writing deployment annotations to ${file}"
52-
yq -i '.metadata.annotations["deploy.staffbase.com/repositoryFullName"] = "'"${GITHUB_REPOSITORY}"'"' "${file}"
53-
yq -i '.metadata.annotations["deploy.staffbase.com/commitSha"] = "'"${GITHUB_SHA}"'"' "${file}"
54-
yq -i '.metadata.annotations["deploy.staffbase.com/version"] = "'"${INPUT_TAG}"'"' "${file}"
53+
if [[ "${INPUT_DEPLOYMENT_ANNOTATIONS:-true}" == "true" ]]; then
54+
local domain="${INPUT_DEPLOYMENT_DOMAIN:-deploy.staffbase.com}"
55+
echo "Writing deployment annotations to ${file}"
56+
yq -i '.metadata.annotations["'"${domain}"'/repositoryFullName"] = "'"${GITHUB_REPOSITORY}"'"' "${file}"
57+
yq -i '.metadata.annotations["'"${domain}"'/commitSha"] = "'"${GITHUB_SHA}"'"' "${file}"
58+
yq -i '.metadata.annotations["'"${domain}"'/version"] = "'"${INPUT_TAG}"'"' "${file}"
59+
else
60+
echo "Deployment annotations disabled (INPUT_DEPLOYMENT_ANNOTATIONS != true); skipping"
61+
fi
5562
}
5663

5764
process_file_updates() {

tests/generate-label-prefix.bats

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bats
2+
3+
load 'test_helper/setup'
4+
5+
SCRIPT="${BATS_TEST_DIRNAME}/../scripts/generate-label-prefix.sh"
6+
7+
setup() {
8+
setup_common
9+
}
10+
11+
teardown() {
12+
teardown_common
13+
}
14+
15+
@test "label_prefix defaults to reversed deploy.staffbase.com" {
16+
unset INPUT_DEPLOYMENT_DOMAIN
17+
run "$SCRIPT"
18+
assert_success
19+
assert_output_value "label_prefix" "com.staffbase.deploy"
20+
}
21+
22+
@test "label_prefix reverses a custom deployment domain" {
23+
export INPUT_DEPLOYMENT_DOMAIN="deploy.example.org"
24+
run "$SCRIPT"
25+
assert_success
26+
assert_output_value "label_prefix" "org.example.deploy"
27+
}

tests/lib-common.bats

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ teardown() {
5959
assert_output "OUTPUT key=value"
6060
}
6161

62+
# --- reverse_domain ---
63+
64+
@test "reverse_domain reverses a three-part domain" {
65+
run reverse_domain "deploy.staffbase.com"
66+
assert_success
67+
assert_output "com.staffbase.deploy"
68+
}
69+
70+
@test "reverse_domain reverses an arbitrary domain" {
71+
run reverse_domain "deploy.example.org"
72+
assert_success
73+
assert_output "org.example.deploy"
74+
}
75+
76+
@test "reverse_domain leaves a single segment unchanged" {
77+
run reverse_domain "localhost"
78+
assert_success
79+
assert_output "localhost"
80+
}
81+
6282
# --- require_env ---
6383

6484
@test "require_env succeeds when variable is set" {

tests/lib-gitops-functions.bats

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,46 @@ EOF
147147
! grep -qE 'deploy\.staffbase\.com/(repo|sha)"' "${TEST_TEMP_DIR}/yq_calls.log"
148148
}
149149

150+
@test "update_file writes annotations when INPUT_DEPLOYMENT_ANNOTATIONS is unset (default on)" {
151+
unset INPUT_DEPLOYMENT_ANNOTATIONS
152+
update_file "deployment.yaml" "spec.image" "$IMAGE"
153+
grep -q 'deploy.staffbase.com/repositoryFullName' "${TEST_TEMP_DIR}/yq_calls.log"
154+
}
155+
156+
@test "update_file skips annotations when INPUT_DEPLOYMENT_ANNOTATIONS is false" {
157+
export INPUT_DEPLOYMENT_ANNOTATIONS="false"
158+
update_file "deployment.yaml" "spec.image" "$IMAGE"
159+
! grep -q 'deploy.staffbase.com/' "${TEST_TEMP_DIR}/yq_calls.log"
160+
}
161+
162+
@test "update_file still updates the image field when annotations are disabled" {
163+
cat > "${TEST_TEMP_DIR}/mocks/yq" << 'YQ_MOCK'
164+
#!/usr/bin/env bash
165+
echo "yq $*" >> "${MOCK_CALLS_DIR}/yq_calls.log"
166+
if [[ "$*" == *"| type"* ]]; then echo "!!str"; fi
167+
exit 0
168+
YQ_MOCK
169+
chmod +x "${TEST_TEMP_DIR}/mocks/yq"
170+
export INPUT_DEPLOYMENT_ANNOTATIONS="false"
171+
update_file "deployment.yaml" "spec.image" "$IMAGE"
172+
grep -q "${IMAGE}" "${TEST_TEMP_DIR}/yq_calls.log"
173+
}
174+
175+
@test "update_file uses default deploy.staffbase.com domain when unset" {
176+
unset INPUT_DEPLOYMENT_DOMAIN
177+
update_file "deployment.yaml" "spec.image" "$IMAGE"
178+
grep -q 'deploy.staffbase.com/repositoryFullName' "${TEST_TEMP_DIR}/yq_calls.log"
179+
}
180+
181+
@test "update_file uses a custom INPUT_DEPLOYMENT_DOMAIN for annotation keys" {
182+
export INPUT_DEPLOYMENT_DOMAIN="deploy.example.org"
183+
update_file "deployment.yaml" "spec.image" "$IMAGE"
184+
grep -q 'deploy.example.org/repositoryFullName' "${TEST_TEMP_DIR}/yq_calls.log"
185+
grep -q 'deploy.example.org/commitSha' "${TEST_TEMP_DIR}/yq_calls.log"
186+
grep -q 'deploy.example.org/version' "${TEST_TEMP_DIR}/yq_calls.log"
187+
! grep -q 'deploy.staffbase.com/' "${TEST_TEMP_DIR}/yq_calls.log"
188+
}
189+
150190
# --- commit_changes ---
151191

152192
@test "commit_changes commits and pushes when push is true" {

0 commit comments

Comments
 (0)