Skip to content

Commit 5bf3f06

Browse files
committed
feat(docker-promote): tag to promote is expected to be an artifact produced by CI workflow
1 parent 1ed795a commit 5bf3f06

3 files changed

Lines changed: 44 additions & 19 deletions

File tree

‎.github/workflows/docker-promote.yml‎

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ on:
2626
description: "Registry the image lives in"
2727
type: string
2828
default: "docker.io"
29-
source-tag-prefix:
30-
description: "Prefix of the tag the CI tags production images with, before the short commit sha (e.g. `dev-`)"
31-
type: string
32-
default: "dev-"
3329
security-scan:
3430
description: "Enable Trivy Security Scan"
3531
type: boolean
@@ -61,7 +57,6 @@ jobs:
6157
CI_WORKFLOW: ${{ inputs.ci-workflow }}
6258
IMAGE: ${{ inputs.image-name }}
6359
REGISTRY: ${{ inputs.registry }}
64-
SOURCE_TAG_PREFIX: ${{ inputs.source-tag-prefix }}
6560

6661
steps:
6762
- name: Checkout
@@ -123,12 +118,10 @@ jobs:
123118
124119
echo "✅ Tag $TAG matches $component version $declared"
125120
126-
# The CI tags images built from a production branch as
127-
# <source-tag-prefix><short sha>, and the release tag points at that commit.
128121
echo "version=$version" | tee -a "$GITHUB_OUTPUT"
129-
echo "source_tag=${SOURCE_TAG_PREFIX}$(echo "$GITHUB_SHA" | cut -c1-8)" | tee -a "$GITHUB_OUTPUT"
130122
131123
- name: Wait for the CI run that built this commit
124+
id: wait
132125
env:
133126
GH_TOKEN: ${{ github.token }}
134127
run: |
@@ -137,15 +130,18 @@ jobs:
137130
# being promoted. So the image is normally still missing when this job starts.
138131
for _ in $(seq 1 60); do
139132
run=$(gh run list --commit "$GITHUB_SHA" --workflow "$CI_WORKFLOW" --limit 1 \
140-
--json status,conclusion \
141-
--jq '.[] | "\(.status) \(.conclusion)"' 2>/dev/null || true)
133+
--json status,conclusion,databaseId \
134+
--jq '.[] | "\(.status) \(.conclusion) \(.databaseId)"' 2>/dev/null || true)
142135
status=${run%% *}
143-
conclusion=${run##* }
136+
rest=${run#* }
137+
conclusion=${rest%% *}
138+
run_id=${rest##* }
144139
145140
case "$status" in
146141
completed)
147142
if [ "$conclusion" = "success" ]; then
148143
echo "✅ CI succeeded for $GITHUB_SHA"
144+
echo "run_id=$run_id" >> "$GITHUB_OUTPUT"
149145
exit 0
150146
fi
151147
echo "❌ CI for $GITHUB_SHA concluded '$conclusion'; refusing to release"
@@ -164,6 +160,28 @@ jobs:
164160
echo "❌ Timed out after 30 minutes waiting for the CI run on $GITHUB_SHA"
165161
exit 1
166162
163+
- name: Download the image tag CI built for this commit
164+
id: ci-tag
165+
continue-on-error: true
166+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
167+
with:
168+
name: docker-image-tag
169+
path: ci-image-tag
170+
run-id: ${{ steps.wait.outputs.run_id }}
171+
github-token: ${{ github.token }}
172+
173+
- name: Resolve the source tag to promote
174+
id: source-tag
175+
run: |
176+
if [ ! -f "ci-image-tag/image-tag.txt" ]; then
177+
echo "❌ CI run did not publish the docker-image-tag artifact; cannot determine the image to promote"
178+
echo " The CI workflow ($CI_WORKFLOW) must upload a 'docker-image-tag' artifact containing the tag it pushed"
179+
exit 1
180+
fi
181+
source_tag=$(cat "ci-image-tag/image-tag.txt")
182+
echo "✅ CI recorded image tag $source_tag"
183+
echo "source_tag=$source_tag" | tee -a "$GITHUB_OUTPUT"
184+
167185
- name: Login to Docker Registry
168186
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
169187
with:
@@ -178,7 +196,7 @@ jobs:
178196
- name: Resolve the digest CI built for this commit
179197
id: tested
180198
env:
181-
SOURCE_TAG: ${{ steps.check.outputs.source_tag }}
199+
SOURCE_TAG: ${{ steps.source-tag.outputs.source_tag }}
182200
run: |
183201
digest=$(docker buildx imagetools inspect "$IMAGE:$SOURCE_TAG" \
184202
--format '{{.Manifest.Digest}}')
@@ -206,7 +224,7 @@ jobs:
206224

207225
- name: Promote the tested image to the release tag
208226
env:
209-
SOURCE_TAG: ${{ steps.check.outputs.source_tag }}
227+
SOURCE_TAG: ${{ steps.source-tag.outputs.source_tag }}
210228
VERSION: ${{ steps.check.outputs.version }}
211229
DIGEST: ${{ steps.tested.outputs.digest }}
212230
run: |
@@ -231,7 +249,7 @@ jobs:
231249
232250
- name: Summarise the release
233251
env:
234-
SOURCE_TAG: ${{ steps.check.outputs.source_tag }}
252+
SOURCE_TAG: ${{ steps.source-tag.outputs.source_tag }}
235253
VERSION: ${{ steps.check.outputs.version }}
236254
DIGEST: ${{ steps.tested.outputs.digest }}
237255
run: |

‎docker-promote/README.md‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ The calling workflow must trigger promotion from a release tag such as `vX.Y.Z`,
1313

1414
- Validates that the release tag matches the version release-please recorded for the component
1515
- Waits for the CI workflow run for the tagged commit to complete successfully
16+
- Reuses the exact image tag CI recorded for the commit from the `docker-image-tag` artifact, and fails if the CI run did not publish one
1617
- Resolves the source image digest before promotion
1718
- Optionally re-scans the tested digest with Trivy
1819
- Copies the tested manifest to the release tag
@@ -26,7 +27,6 @@ The calling workflow must trigger promotion from a release tag such as `vX.Y.Z`,
2627
| `image-name` | Fully qualified OCI image name, without a tag | Yes | - |
2728
| `registry` | Registry hosting the image | No | `"docker.io"` |
2829
| `security-scan` | Enable the Trivy security scan before promotion | No | `true` |
29-
| `source-tag-prefix` | Prefix used by CI before the short commit SHA | No | `"dev-"` |
3030
| `trivy-version` | Trivy security scanner version | No | `"v0.71.0"` |
3131

3232
## Secrets
@@ -38,6 +38,8 @@ The calling workflow must trigger promotion from a release tag such as `vX.Y.Z`,
3838

3939
## Example Usage
4040

41+
The source tag is not passed to this workflow: it is fetched from the `docker-image-tag` artifact that the CI run for the tagged commit uploaded (published by `docker-build` when `push: true`).
42+
4143
```yaml
4244
name: Release Docker Image
4345

@@ -54,13 +56,17 @@ jobs:
5456
with:
5557
image-name: docker-regis.iex.ec/my-service
5658
registry: docker-regis.iex.ec
57-
source-tag-prefix: dev-
5859
security-scan: false
5960
secrets:
6061
username: ${{ secrets.NEXUS_USERNAME }}
6162
password: ${{ secrets.NEXUS_PASSWORD }}
6263
```
6364
65+
The flow:
66+
67+
1. **CI workflow** — builds the image, tags it (e.g. via a `prepare` job), pushes it, and uploads the applied tag as the `docker-image-tag` artifact.
68+
2. **Release workflow** — this one. On `vX.Y.Z` (or `component-vX.Y.Z`) it waits for that CI run, downloads the artifact to learn the exact tag CI pushed, and promotes that digest to the release tag.
69+
6470
## Notes
6571

6672
- The release tag must point at a commit that is contained in `main`; the workflow refuses to release a tag created elsewhere.
@@ -69,7 +75,7 @@ jobs:
6975
- A tag without a component is matched against the `.` package, or against the single entry of the manifest.
7076
- A component is matched against a manifest key by full path or by its last segment, so `post-compute` resolves the key `post-compute` and a workspace path such as `crates/post-compute`.
7177
- The project must be released by release-please in manifest mode; `.release-please-manifest.json` is committed in the same PR as the version bump, so at the tagged commit it holds the version being released.
72-
- The source tag is formed as `<source-tag-prefix><short-commit-sha>`, for example `dev-1a2b3c4d`.
78+
- The source tag is the tag `docker-build` recorded in the `docker-image-tag` artifact of the CI run for the commit, so any CI tag scheme (`dev-<sha>`, `feature-<sha>`, `cid.<sha>.main`, …) is supported. Promotion fails with a clear error if the CI run did not publish the artifact.
7379
- Set `security-scan: false` to skip the promotion-time Trivy scan. The CI build should still perform its configured scan.
7480
- Keep `trivy-version` in sync with the version used by `docker-build` so a promotion finding reflects a vulnerability database update rather than a scanner change.
7581
- The registry credentials need read and write access because promotion creates the release tag.
@@ -81,6 +87,7 @@ jobs:
8187
- **No version recorded for the component** — the tag prefix is not a package in `.release-please-manifest.json`; check the `component` names in `release-please-config.json`.
8288
- **Tag version does not match the recorded version** — the tag is expected to be `v` or `<component>-v` followed by the version release-please released for that component.
8389
- **Timed out waiting for the CI run** — confirm that `ci-workflow` names the workflow that builds the image and that it has started for the tagged commit.
84-
- **Source image or digest cannot be resolved** — confirm that CI pushed `<image-name>:<source-tag-prefix><short-commit-sha>` to `registry`.
90+
- **CI did not publish the docker-image-tag artifact** — the CI workflow named by `ci-workflow` must build the image with a `docker-build` version that records and uploads the `docker-image-tag` artifact (`push: true`); promotion cannot infer the tag otherwise.
91+
- **Source image or digest cannot be resolved** — confirm that CI pushed `<image-name>:<source-tag>` to `registry`.
8592
- **Release tag does not point at the tested image** — the registry or buildx version may not support manifest promotion; keep the buildx setup step up to date.
8693
- **Trivy reports CRITICAL or HIGH vulnerabilities** — the promotion is stopped before the release tag is created; update the image and rerun the release.

‎docker-promote/workflow-sha256‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
52180f32c4fc50c4c53b09128a7380cda91d1041f26182c169262862a824fb40
1+
3bf93c62fbac5336bec03e0ebff19b048acdec31a22ea9ce223cea2901d81540

0 commit comments

Comments
 (0)