Skip to content

Commit 619556d

Browse files
authored
fix: pin release image base manifests
Pin the Go builder and Debian runtime to compatible multi-architecture manifest digests. Record both base image digests in release evidence and document the post-fix exact-main baseline.
1 parent cfa550b commit 619556d

4 files changed

Lines changed: 74 additions & 14 deletions

File tree

.github/workflows/release-image.yml

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,43 @@ jobs:
9393
echo "source_sha must be reachable from main at workflow commit $WORKFLOW_SHA" >&2
9494
exit 1
9595
fi
96+
mapfile -t base_images < <(
97+
awk '
98+
toupper($1) == "FROM" {
99+
for (i = 2; i <= NF; i++) {
100+
if ($i !~ /^--/) {
101+
print $i
102+
break
103+
}
104+
}
105+
}
106+
' source/docker/Dockerfile
107+
)
108+
if [[ "${#base_images[@]}" -ne 2 ]]; then
109+
echo "Dockerfile must contain exactly one builder and one runtime FROM image" >&2
110+
exit 1
111+
fi
112+
if [[ ! "${base_images[0]}" =~ ^([^@[:space:]]+)@(sha256:[0-9a-f]{64})$ ]]; then
113+
echo "builder image must include a readable tag and full sha256 manifest digest" >&2
114+
exit 1
115+
fi
116+
builder_image="${BASH_REMATCH[1]}"
117+
builder_image_digest="${BASH_REMATCH[2]}"
118+
if [[ ! "${base_images[1]}" =~ ^([^@[:space:]]+)@(sha256:[0-9a-f]{64})$ ]]; then
119+
echo "runtime image must include a readable tag and full sha256 manifest digest" >&2
120+
exit 1
121+
fi
122+
runtime_image="${BASH_REMATCH[1]}"
123+
runtime_image_digest="${BASH_REMATCH[2]}"
96124
short_sha="${SOURCE_SHA:0:8}"
97-
echo "sha_tag=sha-$short_sha" >> "$GITHUB_OUTPUT"
98-
echo "build_time=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> "$GITHUB_OUTPUT"
125+
{
126+
echo "sha_tag=sha-$short_sha"
127+
echo "build_time=$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
128+
echo "builder_image=$builder_image"
129+
echo "builder_image_digest=$builder_image_digest"
130+
echo "runtime_image=$runtime_image"
131+
echo "runtime_image_digest=$runtime_image_digest"
132+
} >> "$GITHUB_OUTPUT"
99133
100134
- name: Set up QEMU
101135
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3
@@ -166,6 +200,10 @@ jobs:
166200
BUILD_TIME: ${{ steps.metadata.outputs.build_time }}
167201
BUILD_DIGEST: ${{ steps.build.outputs.digest }}
168202
WORKFLOW_SHA: ${{ github.sha }}
203+
BUILDER_IMAGE: ${{ steps.metadata.outputs.builder_image }}
204+
BUILDER_IMAGE_DIGEST: ${{ steps.metadata.outputs.builder_image_digest }}
205+
RUNTIME_IMAGE: ${{ steps.metadata.outputs.runtime_image }}
206+
RUNTIME_IMAGE_DIGEST: ${{ steps.metadata.outputs.runtime_image_digest }}
169207
run: |
170208
set -euo pipefail
171209
source release-tools/.github/scripts/dockerhub-manifest.sh
@@ -242,6 +280,10 @@ jobs:
242280
--arg amd64_digest "$amd64_digest" \
243281
--arg arm64_digest "$arm64_digest" \
244282
--arg build_time "$BUILD_TIME" \
283+
--arg builder_image "$BUILDER_IMAGE" \
284+
--arg builder_image_digest "$BUILDER_IMAGE_DIGEST" \
285+
--arg runtime_image "$RUNTIME_IMAGE" \
286+
--arg runtime_image_digest "$RUNTIME_IMAGE_DIGEST" \
245287
'{
246288
image: $image,
247289
source_sha: $source_sha,
@@ -255,6 +297,16 @@ jobs:
255297
"linux/amd64": $amd64_digest,
256298
"linux/arm64": $arm64_digest
257299
},
300+
base_images: {
301+
builder: {
302+
image: $builder_image,
303+
digest: $builder_image_digest
304+
},
305+
runtime: {
306+
image: $runtime_image,
307+
digest: $runtime_image_digest
308+
}
309+
},
258310
build_time: $build_time
259311
}' > release-metadata.json
260312
@@ -265,6 +317,8 @@ jobs:
265317
echo "- Version tag: \`$IMAGE:$VERSION\`"
266318
echo "- Commit tag: \`$IMAGE:$SHA_TAG\`"
267319
echo "- Digest: \`$BUILD_DIGEST\`"
320+
echo "- Builder: \`$BUILDER_IMAGE@$BUILDER_IMAGE_DIGEST\`"
321+
echo "- Runtime: \`$RUNTIME_IMAGE@$RUNTIME_IMAGE_DIGEST\`"
268322
} >> "$GITHUB_STEP_SUMMARY"
269323
270324
- name: Upload release evidence

docker/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Step 1: Build stage
2-
FROM --platform=${BUILDPLATFORM} golang:latest AS builder
2+
FROM --platform=${BUILDPLATFORM} golang:1.23.12-bookworm@sha256:167053a2bb901972bf2c1611f8f52c44d5fe7e762e5cab213708d82c421614db AS builder
33

44
ARG TARGETOS
55
ARG TARGETARCH
@@ -55,7 +55,7 @@ RUN --mount=type=bind,target=. \
5555
fi
5656

5757
# Step 2: Final stage
58-
FROM debian:bookworm-slim
58+
FROM debian:bookworm-slim@sha256:abd67ffcfa541b485a3dff59865ab629aa048a6c613e639d36e7456b0b229241
5959

6060
ARG TARGETARCH
6161
ARG VERSION=dev

docs/release.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ published image can always be traced and rolled back.
88

99
| Tag | Example | Mutable | Created by |
1010
| --- | --- | --- | --- |
11-
| Development version | `v0.1.0-dev.20260821.1` | No | Release Image workflow |
11+
| Development version | `v0.1.0-dev.20260822.1` | No | Release Image workflow |
1212
| Release candidate | `v0.1.0-rc.1` | No | Release Image workflow |
1313
| Source commit | `sha-5e29d94d` | No | Release Image workflow |
1414
| Stable version | `v0.1.0` | No | Promote Image workflow after acceptance |
@@ -47,17 +47,22 @@ must resolve to the digest returned by the build. The workflow uploads
4747
`release-metadata.json` with the source and workflow SHAs, run ID and URL,
4848
immutable tags, top-level digest, per-platform digests, and build time.
4949

50-
The first restored baseline uses:
50+
The application Dockerfile must pin both its builder and runtime images with a
51+
readable tag and a full multi-architecture manifest digest. The workflow
52+
rejects an unpinned Dockerfile and records both base-image digests in the
53+
release evidence.
54+
55+
The first restored baseline will use:
5156

5257
```text
53-
source_sha: 5e29d94db535e51876ec9465c5ef78a8e2c2d92a
54-
version: v0.1.0-dev.20260821.1
55-
commit tag: sha-5e29d94d
58+
source_sha: exact main commit containing the pinned Dockerfile and release policy
59+
version: v0.1.0-dev.20260822.1
60+
commit tag: sha-<first 8 characters of source_sha>
5661
```
5762

58-
This baseline predates the `myduckserver --version` command. Its version and
59-
source commit must be queried from its OCI labels. Do not apply the release
60-
tooling commit to the baseline source tree.
63+
The earlier `5e29d94db535e51876ec9465c5ef78a8e2c2d92a` commit remains the fifth
64+
compatibility-restoration milestone, but it is not an image release source:
65+
its mutable builder tag now produces a binary incompatible with its runtime.
6166

6267
## Inspect build identity
6368

@@ -119,6 +124,7 @@ Every release record must keep:
119124
- full application source SHA;
120125
- prerelease, source-commit, and stable tags as applicable;
121126
- multi-architecture digest;
127+
- builder and runtime top-level manifest digests;
122128
- acceptance result and reference;
123129
- build or promotion time;
124130
- previous `latest` digest for rollback.

version_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ func TestVersionInfo(t *testing.T) {
2828
SourceRepository = originalSourceRepository
2929
})
3030

31-
Version = "v0.1.0-dev.20260821.1"
31+
Version = "v0.1.0-dev.20260822.1"
3232
GitCommit = "5e29d94db535e51876ec9465c5ef78a8e2c2d92a"
3333
BuildTime = "2026-08-21T15:35:20Z"
3434
SourceRepository = "https://github.com/apecloud/myduckserver"
3535

36-
want := "myduckserver version=v0.1.0-dev.20260821.1 commit=5e29d94db535e51876ec9465c5ef78a8e2c2d92a build_time=2026-08-21T15:35:20Z source=https://github.com/apecloud/myduckserver"
36+
want := "myduckserver version=v0.1.0-dev.20260822.1 commit=5e29d94db535e51876ec9465c5ef78a8e2c2d92a build_time=2026-08-21T15:35:20Z source=https://github.com/apecloud/myduckserver"
3737
if got := versionInfo(); got != want {
3838
t.Fatalf("versionInfo() = %q, want %q", got, want)
3939
}

0 commit comments

Comments
 (0)