fix(ci): retry transient GHCR unknown blob on release push
Layers often upload successfully; the final manifest push fails with unknown blob. Retry docker push and imagetools create a few times with backoff so the same local image can land without retagging. Co-Authored-By: Composer <noreply@cursor.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -79,9 +79,35 @@ jobs:
|
|||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Push per-arch tag
|
- name: Push per-arch tag
|
||||||
|
# GHCR sometimes rejects the final manifest with "unknown blob" after
|
||||||
|
# every layer reports Pushed (registry race / overwrite of the same
|
||||||
|
# version tag). Retries are idempotent for identical local bytes.
|
||||||
run: |
|
run: |
|
||||||
docker tag selfpost:e2e "ghcr.io/${{ github.repository }}:${{ needs.prepare.outputs.version }}-${{ matrix.arch }}"
|
set -euo pipefail
|
||||||
docker push "ghcr.io/${{ github.repository }}:${{ needs.prepare.outputs.version }}-${{ matrix.arch }}"
|
image="ghcr.io/${{ github.repository }}:${{ needs.prepare.outputs.version }}-${{ matrix.arch }}"
|
||||||
|
docker tag selfpost:e2e "$image"
|
||||||
|
attempt=1
|
||||||
|
max=4
|
||||||
|
backoff=15
|
||||||
|
while true; do
|
||||||
|
set +e
|
||||||
|
out=$(docker push "$image" 2>&1)
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
if [ "$rc" -eq 0 ]; then
|
||||||
|
printf '%s\n' "$out"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$out" >&2
|
||||||
|
if [ "$attempt" -ge "$max" ] || ! grep -qiE 'unknown blob|blob unknown|blob upload invalid|manifest unknown|received unexpected HTTP status: 5[0-9]{2}|429 Too Many Requests|temporarily unavailable' <<<"$out"; then
|
||||||
|
echo "::error::docker push failed for ${image} (attempt ${attempt}/${max}, exit ${rc})" >&2
|
||||||
|
exit "$rc"
|
||||||
|
fi
|
||||||
|
echo "::warning::transient GHCR error pushing ${image} (attempt ${attempt}/${max}); retry in ${backoff}s" >&2
|
||||||
|
sleep "$backoff"
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
backoff=$((backoff * 2))
|
||||||
|
done
|
||||||
|
|
||||||
merge:
|
merge:
|
||||||
needs: [prepare, build]
|
needs: [prepare, build]
|
||||||
@@ -97,8 +123,32 @@ jobs:
|
|||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Merge per-arch tags into the version manifest
|
- name: Merge per-arch tags into the version manifest
|
||||||
|
# Same GHCR "unknown blob" class of failure can hit imagetools create
|
||||||
|
# when assembling the multi-arch version tag.
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create \
|
set -euo pipefail
|
||||||
-t "ghcr.io/${{ github.repository }}:${{ needs.prepare.outputs.version }}" \
|
version_tag="ghcr.io/${{ github.repository }}:${{ needs.prepare.outputs.version }}"
|
||||||
"ghcr.io/${{ github.repository }}:${{ needs.prepare.outputs.version }}-amd64" \
|
amd64="ghcr.io/${{ github.repository }}:${{ needs.prepare.outputs.version }}-amd64"
|
||||||
"ghcr.io/${{ github.repository }}:${{ needs.prepare.outputs.version }}-arm64"
|
arm64="ghcr.io/${{ github.repository }}:${{ needs.prepare.outputs.version }}-arm64"
|
||||||
|
attempt=1
|
||||||
|
max=4
|
||||||
|
backoff=15
|
||||||
|
while true; do
|
||||||
|
set +e
|
||||||
|
out=$(docker buildx imagetools create -t "$version_tag" "$amd64" "$arm64" 2>&1)
|
||||||
|
rc=$?
|
||||||
|
set -e
|
||||||
|
if [ "$rc" -eq 0 ]; then
|
||||||
|
printf '%s\n' "$out"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
printf '%s\n' "$out" >&2
|
||||||
|
if [ "$attempt" -ge "$max" ] || ! grep -qiE 'unknown blob|blob unknown|blob upload invalid|manifest unknown|received unexpected HTTP status: 5[0-9]{2}|429 Too Many Requests|temporarily unavailable' <<<"$out"; then
|
||||||
|
echo "::error::imagetools create failed for ${version_tag} (attempt ${attempt}/${max}, exit ${rc})" >&2
|
||||||
|
exit "$rc"
|
||||||
|
fi
|
||||||
|
echo "::warning::transient GHCR error merging ${version_tag} (attempt ${attempt}/${max}); retry in ${backoff}s" >&2
|
||||||
|
sleep "$backoff"
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
backoff=$((backoff * 2))
|
||||||
|
done
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Release CI: retry `docker push` / `imagetools create` on transient GHCR
|
||||||
|
`unknown blob` (and similar) errors after layers already uploaded.
|
||||||
|
|
||||||
## [1.0.0] - 2026-08-09
|
## [1.0.0] - 2026-08-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
Reference in New Issue
Block a user