fix: select staging runtime image digest
Deploy staging to test server / deploy (push) Successful in 2m31s

Resolve the linux amd64 descriptor from multi-entry ACR manifests so staging migration and deployment pin the runtime image correctly.
This commit is contained in:
linmeng
2026-07-30 10:49:29 +08:00
parent ba4142c6be
commit d3cc70737d
3 changed files with 7 additions and 6 deletions
+2 -2
View File
@@ -63,8 +63,8 @@ jobs:
if [[ "$ALLOW_ROLLBACK" != true ]]; then [[ "$DEPLOY_SHA" == "$staging_sha" ]] || { echo "forward redeploy must use staging head" >&2; exit 1; }; fi if [[ "$ALLOW_ROLLBACK" != true ]]; then [[ "$DEPLOY_SHA" == "$staging_sha" ]] || { echo "forward redeploy must use staging head" >&2; exit 1; }; fi
git cat-file -e "${DEPLOY_SHA}^{commit}" git cat-file -e "${DEPLOY_SHA}^{commit}"
printf '%s' "$REGISTRY_PASSWORD" | docker login "$REGISTRY_HOST" --username "$REGISTRY_USERNAME" --password-stdin printf '%s' "$REGISTRY_PASSWORD" | docker login "$REGISTRY_HOST" --username "$REGISTRY_USERNAME" --password-stdin
api_digest="$(docker manifest inspect "${IMAGE_REPOSITORY}:api-${DEPLOY_SHA}" --verbose | python3 -c 'import json,sys; d=json.load(sys.stdin); d=d[0] if isinstance(d,list) and len(d)==1 else d; print(d.get("Descriptor", d).get("digest", "") if isinstance(d,dict) else "")')" api_digest="$(docker manifest inspect "${IMAGE_REPOSITORY}:api-${DEPLOY_SHA}" --verbose | python3 -c 'import json,sys; d=json.load(sys.stdin); xs=d if isinstance(d,list) else [d]; xs=[x for x in xs if isinstance(x,dict) and isinstance(x.get("Descriptor",x),dict)]; x=next((x for x in xs if x.get("Descriptor",x).get("platform",{}).get("os")=="linux" and x.get("Descriptor",x).get("platform",{}).get("architecture")=="amd64"),None); print(x.get("Descriptor",x).get("digest","") if x else "")')"
web_digest="$(docker manifest inspect "${IMAGE_REPOSITORY}:web-${DEPLOY_SHA}" --verbose | python3 -c 'import json,sys; d=json.load(sys.stdin); d=d[0] if isinstance(d,list) and len(d)==1 else d; print(d.get("Descriptor", d).get("digest", "") if isinstance(d,dict) else "")')" web_digest="$(docker manifest inspect "${IMAGE_REPOSITORY}:web-${DEPLOY_SHA}" --verbose | python3 -c 'import json,sys; d=json.load(sys.stdin); xs=d if isinstance(d,list) else [d]; xs=[x for x in xs if isinstance(x,dict) and isinstance(x.get("Descriptor",x),dict)]; x=next((x for x in xs if x.get("Descriptor",x).get("platform",{}).get("os")=="linux" and x.get("Descriptor",x).get("platform",{}).get("architecture")=="amd64"),None); print(x.get("Descriptor",x).get("digest","") if x else "")')"
[[ "$api_digest" =~ ^sha256:[0-9a-f]{64}$ && "$web_digest" =~ ^sha256:[0-9a-f]{64}$ ]] || exit 1 [[ "$api_digest" =~ ^sha256:[0-9a-f]{64}$ && "$web_digest" =~ ^sha256:[0-9a-f]{64}$ ]] || exit 1
printf 'git_sha=%s\napi_digest=%s\nweb_digest=%s\n' "$DEPLOY_SHA" "$api_digest" "$web_digest" > "${RUNNER_TEMP}/manifest.env" printf 'git_sha=%s\napi_digest=%s\nweb_digest=%s\n' "$DEPLOY_SHA" "$api_digest" "$web_digest" > "${RUNNER_TEMP}/manifest.env"
node frontend/scripts/staging-image-manifest.mjs "${RUNNER_TEMP}/manifest.env" "$DEPLOY_SHA" "$IMAGE_REPOSITORY" >> "$GITHUB_OUTPUT" node frontend/scripts/staging-image-manifest.mjs "${RUNNER_TEMP}/manifest.env" "$DEPLOY_SHA" "$IMAGE_REPOSITORY" >> "$GITHUB_OUTPUT"
@@ -53,7 +53,7 @@ jobs:
[[ "$(git ls-remote origin refs/heads/staging | awk '{print $1}')" == "$DEPLOY_SHA" ]] || { echo "migration requires current staging head" >&2; exit 1; } [[ "$(git ls-remote origin refs/heads/staging | awk '{print $1}')" == "$DEPLOY_SHA" ]] || { echo "migration requires current staging head" >&2; exit 1; }
git cat-file -e "${DEPLOY_SHA}^{commit}" git cat-file -e "${DEPLOY_SHA}^{commit}"
printf '%s' "$REGISTRY_PASSWORD" | docker login "$REGISTRY_HOST" --username "$REGISTRY_USERNAME" --password-stdin printf '%s' "$REGISTRY_PASSWORD" | docker login "$REGISTRY_HOST" --username "$REGISTRY_USERNAME" --password-stdin
web_digest="$(docker manifest inspect "${IMAGE_REPOSITORY}:web-${DEPLOY_SHA}" --verbose | python3 -c 'import json,sys; d=json.load(sys.stdin); d=d[0] if isinstance(d,list) and len(d)==1 else d; print(d.get("Descriptor", d).get("digest", "") if isinstance(d,dict) else "")')" web_digest="$(docker manifest inspect "${IMAGE_REPOSITORY}:web-${DEPLOY_SHA}" --verbose | python3 -c 'import json,sys; d=json.load(sys.stdin); xs=d if isinstance(d,list) else [d]; xs=[x for x in xs if isinstance(x,dict) and isinstance(x.get("Descriptor",x),dict)]; x=next((x for x in xs if x.get("Descriptor",x).get("platform",{}).get("os")=="linux" and x.get("Descriptor",x).get("platform",{}).get("architecture")=="amd64"),None); print(x.get("Descriptor",x).get("digest","") if x else "")')"
[[ "$web_digest" =~ ^sha256:[0-9a-f]{64}$ ]] || exit 1 [[ "$web_digest" =~ ^sha256:[0-9a-f]{64}$ ]] || exit 1
echo "web_image=${IMAGE_REPOSITORY}@${web_digest}" >> "$GITHUB_OUTPUT" echo "web_image=${IMAGE_REPOSITORY}@${web_digest}" >> "$GITHUB_OUTPUT"
echo "deploy_sha=$DEPLOY_SHA" >> "$GITHUB_OUTPUT" echo "deploy_sha=$DEPLOY_SHA" >> "$GITHUB_OUTPUT"
@@ -212,10 +212,11 @@ test("staging owns its deployment control plane independently from main", () =>
} }
}); });
test("Gitea staging digest resolution accepts single-platform manifest arrays", () => { test("Gitea staging digest resolution selects linux amd64 from manifest arrays", () => {
for (const workflow of [read(giteaDeployWorkflow), read(giteaMigrationWorkflow)]) { for (const workflow of [read(giteaDeployWorkflow), read(giteaMigrationWorkflow)]) {
assert.match(workflow, /d=d\[0\] if isinstance\(d,list\) and len\(d\)==1 else d/); assert.match(workflow, /xs=d if isinstance\(d,list\) else \[d\]/);
assert.match(workflow, /if isinstance\(d,dict\) else ""/); assert.match(workflow, /get\("os"\)=="linux"/);
assert.match(workflow, /get\("architecture"\)=="amd64"/);
} }
}); });