From c89f5ae7731af27dfdf6895ad04167b435e01910 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Wed, 12 Nov 2025 13:24:54 +0000 Subject: [PATCH] Re-enable validation with Docker image pull retry logic Added exponential backoff retry logic to handle Docker Hub CDN propagation delays (2-5 minutes after push). Validation workflow now: - Retries Docker image pull up to 10 times - Uses exponential backoff: 30s, 60s, 120s, 120s... - Total timeout: ~10 minutes max - Continues with asset-only validation if image unavailable This keeps validation enabled (important for quality) while fixing the race condition that caused consistent failures. Related to #695 --- .github/workflows/release.yml | 27 +++++------- .github/workflows/validate-release-assets.yml | 44 ++++++++++++++----- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7fa5367..112fa77 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -436,20 +436,13 @@ jobs: echo "Review the draft release and publish when ready." echo "" - # Validation disabled - was causing race conditions with Docker image propagation - # The release workflow already has comprehensive checks: - # - Version guard - # - Preflight tests (backend + frontend + linting) - # - Docker image builds - # - Release asset creation with checksums - # Manual validation can be done after draft release creation if needed - # validate-release-assets: - # needs: create-release - # uses: ./.github/workflows/validate-release-assets.yml - # secrets: inherit - # with: - # tag: ${{ needs.create-release.outputs.tag }} - # version: ${{ inputs.version }} - # release_id: ${{ needs.create-release.outputs.release_id }} - # draft: true - # target_commitish: ${{ needs.create-release.outputs.target_commitish }} + validate-release-assets: + needs: create-release + uses: ./.github/workflows/validate-release-assets.yml + secrets: inherit + with: + tag: ${{ needs.create-release.outputs.tag }} + version: ${{ inputs.version }} + release_id: ${{ needs.create-release.outputs.release_id }} + draft: true + target_commitish: ${{ needs.create-release.outputs.target_commitish }} diff --git a/.github/workflows/validate-release-assets.yml b/.github/workflows/validate-release-assets.yml index 2b6b861..c4a8f42 100644 --- a/.github/workflows/validate-release-assets.yml +++ b/.github/workflows/validate-release-assets.yml @@ -157,23 +157,47 @@ jobs: if: steps.context.outputs.should_run == 'true' run: docker --version - - name: Pull Docker image (if available) + - name: Pull Docker image (with retry logic) if: steps.context.outputs.should_run == 'true' id: docker continue-on-error: true run: | IMAGE="rcourtman/pulse:${{ steps.context.outputs.tag }}" echo "Attempting to pull Docker image: $IMAGE" + echo "Docker Hub CDN propagation can take 2-5 minutes after push..." + echo "" - if docker pull "$IMAGE" 2>/dev/null; then - echo "✓ Docker image available: $IMAGE" - echo "image_available=true" >> $GITHUB_OUTPUT - echo "image=$IMAGE" >> $GITHUB_OUTPUT - else - echo "⚠️ Docker image not yet available: $IMAGE" - echo "⚠️ Will skip Docker image validation" - echo "image_available=false" >> $GITHUB_OUTPUT - fi + # Retry logic: 10 attempts with exponential backoff + # Total time: ~10 minutes max + MAX_ATTEMPTS=10 + ATTEMPT=1 + WAIT_TIME=30 + + while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do + echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Pulling image..." + + if docker pull "$IMAGE" 2>/dev/null; then + echo "✓ Docker image available: $IMAGE" + echo "image_available=true" >> $GITHUB_OUTPUT + echo "image=$IMAGE" >> $GITHUB_OUTPUT + exit 0 + fi + + if [ $ATTEMPT -lt $MAX_ATTEMPTS ]; then + echo "⚠️ Image not yet available, waiting ${WAIT_TIME}s before retry..." + sleep $WAIT_TIME + WAIT_TIME=$((WAIT_TIME * 2)) # Exponential backoff + if [ $WAIT_TIME -gt 120 ]; then + WAIT_TIME=120 # Cap at 2 minutes + fi + fi + + ATTEMPT=$((ATTEMPT + 1)) + done + + echo "⚠️ Docker image not available after $MAX_ATTEMPTS attempts" + echo "⚠️ Will skip Docker image validation" + echo "image_available=false" >> $GITHUB_OUTPUT - name: Run validation script if: steps.context.outputs.should_run == 'true'