pull-request / validate (pull_request) Successful in 32s
The companion SHA tag step failed on the last build even though the image had already been pushed and deployed. Harbor's vulnerability policy blocks manifest reads while Trivy's scan is Running, and `imagetools create` copies the manifest, so Harbor answered 412 Precondition Failed. Two faults compounded. The loop gated on `imagetools inspect`, but a probe passing is no guarantee the copy will be allowed. And `create` sat outside the condition, so under the default `bash -e` its first failure aborted the step before `exit 0` or any further attempt: the log shows one waiting line, then the error, with 58 of 60 retries unused. Retry the real operation instead, inside the condition so a blocked copy is a reason to wait rather than fatal. The trade-off is that a permanently failing copy now takes the full ten minutes to report instead of failing fast; the previous behaviour failed fast but wrongly, on a transient block. Also records that only the primary tag matters for rollout, since the Flux image policy matches the timestamp tag, so a failure in this step costs the audit tag rather than the deployment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
96 lines
3.4 KiB
YAML
96 lines
3.4 KiB
YAML
name: build
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
tags: ['v*']
|
|
|
|
env:
|
|
REGISTRY: harbor.pmagnus.cloud
|
|
IMAGE: harbor.pmagnus.cloud/btcprice/btcprice
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.13'
|
|
# act_runner's Actions cache endpoint is unreachable from job
|
|
# containers and adds a five-minute timeout. Dependency install is
|
|
# fast enough here without setup-python's cache integration.
|
|
|
|
- name: Test
|
|
run: |
|
|
python -m pip install -r requirements-dev.txt
|
|
python -m pytest tests/
|
|
|
|
- name: Generate immutable tags
|
|
id: tags
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
SHA=$(git rev-parse --short HEAD)
|
|
case "$GITHUB_REF" in
|
|
refs/tags/v*) VERSION="${GITHUB_REF_NAME#v}" ;;
|
|
*) VERSION="main-$(date -u +%Y%m%d%H%M%S)" ;;
|
|
esac
|
|
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
|
|
echo "primary=$IMAGE:$VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "qualified=$IMAGE:$VERSION-$SHA" >> "$GITHUB_OUTPUT"
|
|
|
|
# anvil is not a NetBird peer. Host networking makes BuildKit use the
|
|
# runner host's pinned Harbor address; keep this until anvil is enrolled.
|
|
- uses: docker/setup-buildx-action@v3
|
|
with:
|
|
driver-opts: network=host
|
|
|
|
- uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ secrets.HARBOR_ROBOT_USER }}
|
|
password: ${{ secrets.HARBOR_ROBOT_TOKEN }}
|
|
|
|
- uses: docker/build-push-action@v6
|
|
with:
|
|
context: .
|
|
platforms: linux/amd64
|
|
push: true
|
|
# Harbor blocks manifest reads until Trivy has approved the image.
|
|
# Push one tag first, then add the companion tag after the scan.
|
|
tags: ${{ steps.tags.outputs.primary }}
|
|
build-args: |
|
|
BUILD_SHA=${{ steps.tags.outputs.sha }}
|
|
cache-from: type=registry,ref=${{ env.IMAGE }}:buildcache
|
|
cache-to: type=registry,ref=${{ env.IMAGE }}:buildcache,mode=max
|
|
|
|
# The primary tag is already pushed and deployable by the time this runs:
|
|
# the image policy selects `main-<timestamp>`, so a failure here costs the
|
|
# SHA audit tag, not the rollout.
|
|
- name: Wait for Harbor policy, then add SHA tag
|
|
shell: bash
|
|
env:
|
|
PRIMARY: ${{ steps.tags.outputs.primary }}
|
|
QUALIFIED: ${{ steps.tags.outputs.qualified }}
|
|
run: |
|
|
# Harbor's vulnerability policy blocks manifest reads while Trivy's scan
|
|
# is still Running, and `imagetools create` copies the manifest. Retry
|
|
# that copy itself rather than an `inspect` probe: a probe that passes is
|
|
# no guarantee the copy will, and under the default `bash -e` the first
|
|
# blocked copy aborted the step before its retries could run.
|
|
for attempt in $(seq 1 60); do
|
|
if docker buildx imagetools create --tag "$QUALIFIED" "$PRIMARY"; then
|
|
echo "tagged $QUALIFIED on attempt $attempt"
|
|
exit 0
|
|
fi
|
|
echo "waiting for Harbor scan/pull policy ($attempt/60)"
|
|
sleep 10
|
|
done
|
|
echo "Harbor did not approve $PRIMARY within 10 minutes" >&2
|
|
exit 1
|
|
|
|
- name: Summary
|
|
run: echo "pushed ${{ steps.tags.outputs.primary }} and ${{ steps.tags.outputs.qualified }}"
|