fix(cd): deploy production images by digest

This commit is contained in:
ogt
2026-07-14 16:10:43 +08:00
parent 92b4857601
commit 33f1c0b8f4
2 changed files with 74 additions and 4 deletions

View File

@@ -1733,6 +1733,25 @@ jobs:
docker push ${{ env.HARBOR }}/awoooi/api:${{ github.sha }}
run_docker_push_step api_push_latest "${DOCKER_API_PUSH_TIMEOUT_SECONDS:-600}" \
docker push ${{ env.HARBOR }}/awoooi/api:latest
API_IMAGE_DIGEST="$(
docker manifest inspect --verbose \
"${{ env.HARBOR }}/awoooi/api:${{ github.sha }}" | \
python3 -c '
import json
import re
import sys
payload = json.load(sys.stdin)
digest = payload.get("Descriptor", {}).get("digest", "")
if not re.fullmatch(r"sha256:[0-9a-f]{64}", digest):
raise SystemExit("api_harbor_digest_invalid")
print(digest)
'
)"
docker manifest inspect \
"${{ env.HARBOR }}/awoooi/api@${API_IMAGE_DIGEST}" >/dev/null
echo "API_IMAGE_DIGEST=${API_IMAGE_DIGEST}" >> "${GITHUB_ENV}"
echo "api_harbor_digest_verified=${API_IMAGE_DIGEST}"
# 2026-03-31 ogt: 移除中間通知,減少訊息雜訊
@@ -1848,6 +1867,25 @@ jobs:
docker push ${{ env.HARBOR }}/awoooi/web:${{ github.sha }}
run_docker_push_step web_push_latest "${DOCKER_WEB_PUSH_TIMEOUT_SECONDS:-600}" \
docker push ${{ env.HARBOR }}/awoooi/web:latest
WEB_IMAGE_DIGEST="$(
docker manifest inspect --verbose \
"${{ env.HARBOR }}/awoooi/web:${{ github.sha }}" | \
python3 -c '
import json
import re
import sys
payload = json.load(sys.stdin)
digest = payload.get("Descriptor", {}).get("digest", "")
if not re.fullmatch(r"sha256:[0-9a-f]{64}", digest):
raise SystemExit("web_harbor_digest_invalid")
print(digest)
'
)"
docker manifest inspect \
"${{ env.HARBOR }}/awoooi/web@${WEB_IMAGE_DIGEST}" >/dev/null
echo "WEB_IMAGE_DIGEST=${WEB_IMAGE_DIGEST}" >> "${GITHUB_ENV}"
echo "web_harbor_digest_verified=${WEB_IMAGE_DIGEST}"
- name: Release Docker Build Lock
if: always()
@@ -2611,6 +2649,16 @@ jobs:
export IMAGE_TAG="${{ github.sha }}"
HARBOR=192.168.0.110:5000
if ! printf '%s\n' "${API_IMAGE_DIGEST:-}" | \
grep -Eq '^sha256:[0-9a-f]{64}$'; then
echo "BLOCKER api_harbor_digest_missing"
exit 1
fi
if ! printf '%s\n' "${WEB_IMAGE_DIGEST:-}" | \
grep -Eq '^sha256:[0-9a-f]{64}$'; then
echo "BLOCKER web_harbor_digest_missing"
exit 1
fi
# ─── Step 1: Apply NetworkPolicy + ConfigMap + ServiceRegistry ───
# Preserve only live Deployment specs and Secret object existence.
@@ -2789,7 +2837,7 @@ jobs:
"KUBECTL='sudo kubectl --kubeconfig=/etc/rancher/k3s/k3s.yaml --server=${{ env.K8S_API_SERVER }}'; \$KUBECTL apply -f -"
echo "✅ Service Registry ConfigMap 已更新"
# ─── Step 2: 更新 kustomization.yaml image tag ───
# ─── Step 2: 更新 kustomization.yaml immutable image digest ───
# kustomize is an immutable dependency of the internal runner image.
# GitHub is frozen, so CD must never download tools from public GitHub.
export PATH="${HOME}/.local/bin:${PATH}"
@@ -2799,11 +2847,12 @@ jobs:
}
cd k8s/awoooi-prod
# kustomize edit set image 更新 tag
# Source SHA remains in AWOOOI_BUILD_COMMIT_SHA; workload identity is
# pinned to the independently read-back Harbor manifest digest.
kustomize edit set image \
192.168.0.110:5000/library/api:IMAGE_TAG_PLACEHOLDER=${HARBOR}/awoooi/api:${IMAGE_TAG}
192.168.0.110:5000/library/api:IMAGE_TAG_PLACEHOLDER=${HARBOR}/awoooi/api@${API_IMAGE_DIGEST}
kustomize edit set image \
192.168.0.110:5000/library/web:IMAGE_TAG_PLACEHOLDER=${HARBOR}/awoooi/web:${IMAGE_TAG}
192.168.0.110:5000/library/web:IMAGE_TAG_PLACEHOLDER=${HARBOR}/awoooi/web@${WEB_IMAGE_DIGEST}
cd ../..
# 2026-06-29 Codex: API deploy readback uses AWOOOI_BUILD_COMMIT_SHA

View File

@@ -469,6 +469,27 @@ def test_cd_docker_build_and_push_steps_are_bounded_and_classified() -> None:
assert marker in block
def test_cd_deploys_api_and_web_by_verified_harbor_digest() -> None:
text = _workflow_text()
expected = [
'"${{ env.HARBOR }}/awoooi/api:${{ github.sha }}"',
'"${{ env.HARBOR }}/awoooi/web:${{ github.sha }}"',
'echo "API_IMAGE_DIGEST=${API_IMAGE_DIGEST}" >> "${GITHUB_ENV}"',
'echo "WEB_IMAGE_DIGEST=${WEB_IMAGE_DIGEST}" >> "${GITHUB_ENV}"',
"api_harbor_digest_missing",
"web_harbor_digest_missing",
"${HARBOR}/awoooi/api@${API_IMAGE_DIGEST}",
"${HARBOR}/awoooi/web@${WEB_IMAGE_DIGEST}",
]
for marker in expected:
assert marker in text
deploy_block = text.split("- name: Deploy to K8s (ArgoCD GitOps)", 1)[1]
deploy_block = deploy_block.split("- name: Apply Runtime Image Mirror Policy", 1)[0]
assert "${HARBOR}/awoooi/api:${IMAGE_TAG}" not in deploy_block
assert "${HARBOR}/awoooi/web:${IMAGE_TAG}" not in deploy_block
def test_host_pressure_gate_ignores_stale_docker_stats_for_cpu_attribution() -> None:
text = WAIT_HOST_PRESSURE.read_text(encoding="utf-8")