fix: eliminate race conditions in release workflow chain
The promote-floating-tags and helm-pages workflows now trigger automatically via workflow_run when publish-docker.yml completes, instead of being dispatched immediately by create-release.yml. This ensures Docker images are fully available before: - Floating tags (rc, latest, major.minor) are promoted - Helm chart smoke tests try to pull the image Key changes: - promote-floating-tags.yml: Add workflow_run trigger, extract tag from triggering workflow, wait for BOTH pulse and agent images - helm-pages.yml: Add workflow_run trigger, extract version from triggering workflow - create-release.yml: Remove manual dispatch for these workflows
This commit is contained in:
parent
9ba4a55520
commit
bd7d2c22f7
3 changed files with 126 additions and 43 deletions
29
.github/workflows/create-release.yml
vendored
29
.github/workflows/create-release.yml
vendored
|
|
@ -493,33 +493,10 @@ jobs:
|
||||||
|
|
||||||
echo "✓ Docker publish workflow dispatched"
|
echo "✓ Docker publish workflow dispatched"
|
||||||
|
|
||||||
- name: Trigger floating tag promotion
|
|
||||||
if: ${{ github.event.inputs.draft_only != 'true' }}
|
|
||||||
continue-on-error: true # Non-fatal if dispatch fails
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.WORKFLOW_PAT }}
|
|
||||||
run: |
|
|
||||||
TAG="${{ needs.extract_version.outputs.tag }}"
|
|
||||||
IS_PRERELEASE="${{ needs.extract_version.outputs.is_prerelease }}"
|
|
||||||
echo "Triggering floating tag promotion for ${TAG} (prerelease: ${IS_PRERELEASE})..."
|
|
||||||
|
|
||||||
gh workflow run promote-floating-tags.yml -f tag="${TAG}" -f prerelease="${IS_PRERELEASE}"
|
# NOTE: Floating tag promotion and Helm chart release workflows now trigger
|
||||||
|
# automatically when publish-docker.yml completes via workflow_run.
|
||||||
echo "✓ Floating tag promotion workflow dispatched"
|
# No need to dispatch them manually - this eliminates race conditions.
|
||||||
|
|
||||||
- name: Trigger Helm chart release
|
|
||||||
if: ${{ github.event.inputs.draft_only != 'true' }}
|
|
||||||
continue-on-error: true # Non-fatal if dispatch fails
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.WORKFLOW_PAT }}
|
|
||||||
run: |
|
|
||||||
TAG="${{ needs.extract_version.outputs.tag }}"
|
|
||||||
VERSION="${TAG#v}"
|
|
||||||
echo "Triggering Helm chart release for ${VERSION}..."
|
|
||||||
|
|
||||||
gh workflow run helm-pages.yml -f chart_version="${VERSION}"
|
|
||||||
|
|
||||||
echo "✓ Helm chart release workflow dispatched"
|
|
||||||
|
|
||||||
- name: Trigger demo server update
|
- name: Trigger demo server update
|
||||||
if: ${{ github.event.inputs.draft_only != 'true' }}
|
if: ${{ github.event.inputs.draft_only != 'true' }}
|
||||||
|
|
|
||||||
33
.github/workflows/helm-pages.yml
vendored
33
.github/workflows/helm-pages.yml
vendored
|
|
@ -1,8 +1,11 @@
|
||||||
name: Release Helm Chart to GitHub Pages
|
name: Release Helm Chart to GitHub Pages
|
||||||
|
|
||||||
# Only triggered manually or by create-release.yml
|
# Triggered automatically when publish-docker.yml completes, or manually
|
||||||
# Removing release: published trigger prevents race conditions
|
# We wait for Docker publish because the smoke test pulls the Docker image
|
||||||
on:
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["Publish Docker Images"]
|
||||||
|
types: [completed]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
chart_version:
|
chart_version:
|
||||||
|
|
@ -15,6 +18,8 @@ permissions:
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# Only run if workflow_dispatch OR if workflow_run completed successfully
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
@ -56,9 +61,31 @@ jobs:
|
||||||
|
|
||||||
- name: Determine chart version
|
- name: Determine chart version
|
||||||
id: version
|
id: version
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ inputs.chart_version }}"
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
|
# Manual dispatch - use input directly
|
||||||
|
VERSION="${{ inputs.chart_version }}"
|
||||||
|
else
|
||||||
|
# workflow_run trigger - extract version from the triggering workflow
|
||||||
|
RUN_ID="${{ github.event.workflow_run.id }}"
|
||||||
|
echo "Extracting version from workflow run ${RUN_ID}..."
|
||||||
|
|
||||||
|
WORKFLOW_DATA=$(gh api repos/${{ github.repository }}/actions/runs/${RUN_ID})
|
||||||
|
TAG=$(echo "$WORKFLOW_DATA" | jq -r '.display_title' | grep -oP 'v?\d+\.\d+\.\d+(-[a-zA-Z]+\.\d+)?' || echo "")
|
||||||
|
|
||||||
|
if [ -z "$TAG" ]; then
|
||||||
|
echo "::error::Could not extract version from workflow_run"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove leading 'v' if present
|
||||||
|
VERSION="${TAG#v}"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Chart version: $VERSION"
|
||||||
|
|
||||||
- name: Update Chart.yaml version
|
- name: Update Chart.yaml version
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
107
.github/workflows/promote-floating-tags.yml
vendored
107
.github/workflows/promote-floating-tags.yml
vendored
|
|
@ -1,8 +1,10 @@
|
||||||
name: Promote Floating Tags
|
name: Promote Floating Tags
|
||||||
|
|
||||||
# Only triggered by create-release.yml after Docker images are published
|
# Triggered automatically when publish-docker.yml completes, or manually
|
||||||
# This prevents race conditions with release: published event
|
|
||||||
on:
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["Publish Docker Images"]
|
||||||
|
types: [completed]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
tag:
|
tag:
|
||||||
|
|
@ -18,15 +20,56 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
promote-images:
|
promote-images:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# Only run if workflow_dispatch OR if workflow_run completed successfully
|
||||||
|
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
env:
|
|
||||||
OWNER: ${{ github.repository_owner }}
|
|
||||||
TAG: ${{ inputs.tag }}
|
|
||||||
PRERELEASE: ${{ inputs.prerelease }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Extract tag from trigger
|
||||||
|
id: extract
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
|
# Manual dispatch - use inputs directly
|
||||||
|
TAG="${{ inputs.tag }}"
|
||||||
|
PRERELEASE="${{ inputs.prerelease }}"
|
||||||
|
else
|
||||||
|
# workflow_run trigger - extract from the triggering workflow's inputs
|
||||||
|
# The publish-docker workflow was triggered with a tag input
|
||||||
|
RUN_ID="${{ github.event.workflow_run.id }}"
|
||||||
|
echo "Extracting inputs from workflow run ${RUN_ID}..."
|
||||||
|
|
||||||
|
# Get the workflow run details to extract the tag
|
||||||
|
WORKFLOW_DATA=$(gh api repos/${{ github.repository }}/actions/runs/${RUN_ID})
|
||||||
|
TAG=$(echo "$WORKFLOW_DATA" | jq -r '.head_branch // ""')
|
||||||
|
|
||||||
|
# If head_branch is main, we need to get it from the run's inputs
|
||||||
|
# The inputs are stored in the run's display_title or we parse from artifacts
|
||||||
|
if [ "$TAG" = "main" ] || [ -z "$TAG" ]; then
|
||||||
|
# Try to get from run name which typically includes the tag
|
||||||
|
TAG=$(echo "$WORKFLOW_DATA" | jq -r '.display_title' | grep -oP 'v\d+\.\d+\.\d+(-[a-zA-Z]+\.\d+)?' || echo "")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$TAG" ]; then
|
||||||
|
echo "::error::Could not extract tag from workflow_run"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect prerelease from tag
|
||||||
|
if [[ "$TAG" =~ -rc\.[0-9]+$ ]] || [[ "$TAG" =~ -alpha\.[0-9]+$ ]] || [[ "$TAG" =~ -beta\.[0-9]+$ ]]; then
|
||||||
|
PRERELEASE="true"
|
||||||
|
else
|
||||||
|
PRERELEASE="false"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||||
|
echo "prerelease=${PRERELEASE}" >> $GITHUB_OUTPUT
|
||||||
|
echo "Promoting floating tags for ${TAG} (prerelease: ${PRERELEASE})"
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
- name: Log in to Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
|
|
@ -40,27 +83,52 @@ jobs:
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Wait for Docker image to be available
|
- name: Wait for Docker images to be available
|
||||||
|
env:
|
||||||
|
TAG: ${{ steps.extract.outputs.tag }}
|
||||||
run: |
|
run: |
|
||||||
echo "Waiting for rcourtman/pulse:${TAG} to be available..."
|
echo "Waiting for Docker images with tag ${TAG} to be available..."
|
||||||
MAX_ATTEMPTS=30
|
MAX_ATTEMPTS=30
|
||||||
ATTEMPT=0
|
ATTEMPT=0
|
||||||
|
|
||||||
|
# Wait for main pulse image
|
||||||
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
|
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
|
||||||
if docker manifest inspect rcourtman/pulse:${TAG} > /dev/null 2>&1; then
|
if docker manifest inspect rcourtman/pulse:${TAG} > /dev/null 2>&1; then
|
||||||
echo "Image rcourtman/pulse:${TAG} is available!"
|
echo "Image rcourtman/pulse:${TAG} is available!"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
ATTEMPT=$((ATTEMPT + 1))
|
||||||
|
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS - pulse image not yet available, waiting 10s..."
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $ATTEMPT -ge $MAX_ATTEMPTS ]; then
|
||||||
|
echo "Timeout waiting for pulse Docker image"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Also wait for agent image
|
||||||
|
ATTEMPT=0
|
||||||
|
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
|
||||||
|
if docker manifest inspect rcourtman/pulse-docker-agent:${TAG} > /dev/null 2>&1; then
|
||||||
|
echo "Image rcourtman/pulse-docker-agent:${TAG} is available!"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
ATTEMPT=$((ATTEMPT + 1))
|
ATTEMPT=$((ATTEMPT + 1))
|
||||||
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS - Image not yet available, waiting 10s..."
|
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS - agent image not yet available, waiting 10s..."
|
||||||
sleep 10
|
sleep 10
|
||||||
done
|
done
|
||||||
echo "Timeout waiting for Docker image"
|
|
||||||
|
echo "Timeout waiting for agent Docker image"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Promote Pulse server image tags
|
- name: Promote Pulse server image tags
|
||||||
|
env:
|
||||||
|
TAG: ${{ steps.extract.outputs.tag }}
|
||||||
|
PRERELEASE: ${{ steps.extract.outputs.prerelease }}
|
||||||
|
OWNER: ${{ github.repository_owner }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
TAG="${TAG}"
|
|
||||||
VERSION="${TAG#v}"
|
VERSION="${TAG#v}"
|
||||||
BASE_VERSION="${VERSION%%-*}"
|
BASE_VERSION="${VERSION%%-*}"
|
||||||
IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE_VERSION"
|
IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE_VERSION"
|
||||||
|
|
@ -90,9 +158,12 @@ jobs:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Promote Docker agent image tags
|
- name: Promote Docker agent image tags
|
||||||
|
env:
|
||||||
|
TAG: ${{ steps.extract.outputs.tag }}
|
||||||
|
PRERELEASE: ${{ steps.extract.outputs.prerelease }}
|
||||||
|
OWNER: ${{ github.repository_owner }}
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
TAG="${TAG}"
|
|
||||||
VERSION="${TAG#v}"
|
VERSION="${TAG#v}"
|
||||||
BASE_VERSION="${VERSION%%-*}"
|
BASE_VERSION="${VERSION%%-*}"
|
||||||
IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE_VERSION"
|
IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE_VERSION"
|
||||||
|
|
@ -120,9 +191,17 @@ jobs:
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Promotion summary
|
- name: Promotion summary
|
||||||
|
env:
|
||||||
|
TAG: ${{ steps.extract.outputs.tag }}
|
||||||
|
PRERELEASE: ${{ steps.extract.outputs.prerelease }}
|
||||||
run: |
|
run: |
|
||||||
|
VERSION="${TAG#v}"
|
||||||
|
BASE_VERSION="${VERSION%%-*}"
|
||||||
|
IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE_VERSION"
|
||||||
|
MAJOR_MINOR="$MAJOR.${MINOR:-0}"
|
||||||
|
|
||||||
if [ "$PRERELEASE" = "true" ]; then
|
if [ "$PRERELEASE" = "true" ]; then
|
||||||
echo "Updated rc tags to point to ${TAG} for both server and agent images."
|
echo "✅ Updated :rc tags to point to ${TAG} for both server and agent images."
|
||||||
else
|
else
|
||||||
echo "Updated latest/${MAJOR_MINOR}/${MAJOR} tags to point to ${TAG} for both server and agent images."
|
echo "✅ Updated :latest, :${MAJOR_MINOR}, :${MAJOR} tags to point to ${TAG} for both server and agent images."
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue