mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-02-18 00:17:39 +01:00
586 lines
22 KiB
YAML
586 lines
22 KiB
YAML
name: Pulse Release Pipeline
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: 'Version number (e.g., 4.30.0)'
|
|
required: true
|
|
type: string
|
|
release_notes:
|
|
description: 'Release notes (markdown) - generated by Claude'
|
|
required: true
|
|
type: string
|
|
draft_only:
|
|
description: 'Create draft release only (do not publish)'
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
|
|
concurrency:
|
|
group: release-${{ github.event.inputs.version || github.ref || github.run_id }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
extract_version:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
outputs:
|
|
version: ${{ steps.extract.outputs.version }}
|
|
tag: ${{ steps.extract.outputs.tag }}
|
|
is_prerelease: ${{ steps.extract.outputs.is_prerelease }}
|
|
steps:
|
|
- name: Extract version
|
|
id: extract
|
|
run: |
|
|
# Handle both tag push and workflow_dispatch
|
|
if [ "${{ github.event_name }}" = "push" ]; then
|
|
TAG="${GITHUB_REF#refs/tags/}"
|
|
VERSION="${TAG#v}"
|
|
else
|
|
VERSION=$(jq -r '.inputs.version // ""' "$GITHUB_EVENT_PATH" 2>/dev/null || echo "")
|
|
if [ -z "$VERSION" ]; then
|
|
echo "::error::workflow_dispatch must include a version input"
|
|
exit 1
|
|
fi
|
|
TAG="v${VERSION}"
|
|
fi
|
|
|
|
# Detect if this is a prerelease (RC, alpha, beta)
|
|
IS_PRERELEASE="false"
|
|
if [[ "$VERSION" =~ -rc\.[0-9]+$ ]] || [[ "$VERSION" =~ -alpha\.[0-9]+$ ]] || [[ "$VERSION" =~ -beta\.[0-9]+$ ]]; then
|
|
IS_PRERELEASE="true"
|
|
echo "Detected prerelease version: ${VERSION}"
|
|
fi
|
|
|
|
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
|
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
|
echo "is_prerelease=${IS_PRERELEASE}" >> $GITHUB_OUTPUT
|
|
echo "Version: ${VERSION}, Tag: ${TAG}, Prerelease: ${IS_PRERELEASE}"
|
|
|
|
version_guard:
|
|
needs: extract_version
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Ensure VERSION file matches requested version
|
|
run: |
|
|
FILE_VERSION=$(cat VERSION | tr -d '\n')
|
|
REQUESTED_VERSION="${{ needs.extract_version.outputs.version }}"
|
|
if [ "$FILE_VERSION" != "$REQUESTED_VERSION" ]; then
|
|
echo "::error::VERSION file ($FILE_VERSION) does not match requested version ($REQUESTED_VERSION)."
|
|
echo "The VERSION file must be updated and committed before running release."
|
|
exit 1
|
|
fi
|
|
echo "✓ VERSION file matches requested version ($REQUESTED_VERSION)"
|
|
|
|
preflight_tests:
|
|
needs:
|
|
- extract_version
|
|
- version_guard
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 90
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
env:
|
|
FRONTEND_DIST: frontend-modern/dist
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '20'
|
|
cache: 'npm'
|
|
cache-dependency-path: 'frontend-modern/package-lock.json'
|
|
|
|
- name: Install frontend dependencies
|
|
run: npm --prefix frontend-modern ci
|
|
|
|
- name: Restore frontend build cache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: frontend-modern/dist
|
|
key: frontend-build-${{ hashFiles('frontend-modern/package-lock.json', 'frontend-modern/src/**/*', 'frontend-modern/index.html', 'frontend-modern/postcss.config.cjs', 'frontend-modern/tailwind.config.cjs') }}
|
|
|
|
- name: Build frontend bundle for Go embed
|
|
run: |
|
|
if [ -d "$FRONTEND_DIST" ] && [ -f "$FRONTEND_DIST/index.html" ]; then
|
|
echo "Using cached frontend build";
|
|
else
|
|
npm --prefix frontend-modern run build;
|
|
fi
|
|
rm -rf internal/api/frontend-modern
|
|
mkdir -p internal/api/frontend-modern
|
|
cp -r frontend-modern/dist internal/api/frontend-modern/
|
|
|
|
- name: Lint frontend
|
|
run: npm --prefix frontend-modern run lint
|
|
|
|
- name: Install docker-compose
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y docker-compose
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: '1.24'
|
|
cache: true
|
|
|
|
- name: Run backend tests
|
|
run: make test
|
|
|
|
- name: Cache Playwright browsers
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.cache/ms-playwright
|
|
key: playwright-${{ runner.os }}-${{ hashFiles('tests/integration/package-lock.json') }}
|
|
|
|
- name: Prepare integration test dependencies
|
|
working-directory: tests/integration
|
|
run: |
|
|
npm ci
|
|
npx playwright install --with-deps chromium
|
|
|
|
- name: Build Pulse for integration tests
|
|
run: make build
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Log in to Docker Hub
|
|
uses: docker/login-action@v3
|
|
with:
|
|
username: ${{ secrets.DOCKER_USERNAME }}
|
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
|
|
- name: Log in to GHCR
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Build and push staging Docker images
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
context: .
|
|
target: runtime
|
|
platforms: linux/amd64 # amd64-only for faster preflight; multi-arch happens in release job
|
|
push: true
|
|
provenance: false
|
|
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/pulse:buildcache
|
|
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/pulse:buildcache,mode=max
|
|
build-args: |
|
|
PULSE_LICENSE_PUBLIC_KEY=${{ secrets.PULSE_LICENSE_PUBLIC_KEY }}
|
|
tags: |
|
|
ghcr.io/${{ github.repository_owner }}/pulse:staging-${{ needs.extract_version.outputs.tag }}
|
|
|
|
- name: Build and push staging Docker agent image
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
context: .
|
|
file: ./Dockerfile
|
|
target: agent_runtime
|
|
platforms: linux/amd64 # amd64-only for faster preflight; multi-arch happens in release job
|
|
push: true
|
|
provenance: false
|
|
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/pulse-docker-agent:buildcache
|
|
cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/pulse-docker-agent:buildcache,mode=max
|
|
build-args: |
|
|
PULSE_LICENSE_PUBLIC_KEY=${{ secrets.PULSE_LICENSE_PUBLIC_KEY }}
|
|
tags: |
|
|
ghcr.io/${{ github.repository_owner }}/pulse-docker-agent:staging-${{ needs.extract_version.outputs.tag }}
|
|
|
|
- name: Build Docker images for integration tests
|
|
run: |
|
|
docker build -t pulse-mock-github:test tests/integration/mock-github-server
|
|
docker build -t pulse:test -f Dockerfile --target runtime --cache-from ghcr.io/${{ github.repository_owner }}/pulse:buildcache --build-arg BUILDKIT_INLINE_CACHE=1 --build-arg PULSE_LICENSE_PUBLIC_KEY="$PULSE_LICENSE_PUBLIC_KEY" .
|
|
env:
|
|
PULSE_LICENSE_PUBLIC_KEY: ${{ secrets.PULSE_LICENSE_PUBLIC_KEY }}
|
|
|
|
- name: Run update integration smoke tests
|
|
working-directory: tests/integration
|
|
env:
|
|
MOCK_CHECKSUM_ERROR: "false"
|
|
MOCK_NETWORK_ERROR: "false"
|
|
MOCK_RATE_LIMIT: "false"
|
|
MOCK_STALE_RELEASE: "false"
|
|
run: |
|
|
docker-compose -f docker-compose.test.yml up -d
|
|
|
|
# Wait for services to be healthy
|
|
echo "Waiting for mock-github to be healthy..."
|
|
timeout 60 sh -c 'until docker inspect --format="{{json .State.Health.Status}}" pulse-mock-github | grep -q "healthy"; do sleep 2; done' || {
|
|
echo "Mock GitHub failed to become healthy"
|
|
docker logs pulse-mock-github
|
|
exit 1
|
|
}
|
|
|
|
echo "Waiting for pulse-test-server to be healthy..."
|
|
timeout 60 sh -c 'until docker inspect --format="{{json .State.Health.Status}}" pulse-test-server | grep -q "healthy"; do sleep 2; done' || {
|
|
echo "Pulse server failed to become healthy"
|
|
docker logs pulse-test-server
|
|
exit 1
|
|
}
|
|
|
|
echo "All services healthy, verifying port mapping..."
|
|
# Test that the host can actually reach the container through port mapping
|
|
for i in 1 2 3 4 5; do
|
|
if curl -f -s http://localhost:7655/api/health > /dev/null 2>&1; then
|
|
echo "Port mapping verified: Pulse server is reachable from host"
|
|
break
|
|
elif [ $i -eq 5 ]; then
|
|
echo "ERROR: Port mapping failed - cannot reach Pulse server from host"
|
|
echo "Container healthcheck passed, but host cannot connect via localhost:7655"
|
|
echo "Pulse server logs:"
|
|
docker logs pulse-test-server || true
|
|
echo "Mock GitHub logs:"
|
|
docker logs pulse-mock-github || true
|
|
exit 1
|
|
else
|
|
echo "Attempt $i: Server not yet reachable from host, waiting..."
|
|
sleep 2
|
|
fi
|
|
done
|
|
|
|
echo "Running API-level update integration test..."
|
|
UPDATE_API_BASE_URL=http://localhost:7655 go test ../../tests/integration/api -run TestUpdateFlowIntegration -count=1
|
|
|
|
echo "Skipping legacy Playwright update scenarios (removed until they can be rebuilt)"
|
|
docker-compose -f docker-compose.test.yml down -v
|
|
|
|
- name: Cleanup integration environment
|
|
if: always()
|
|
working-directory: tests/integration
|
|
run: docker-compose -f docker-compose.test.yml down -v || true
|
|
|
|
create_release:
|
|
needs:
|
|
- extract_version
|
|
- preflight_tests
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
permissions:
|
|
contents: write
|
|
outputs:
|
|
release_id: ${{ steps.create_release.outputs.release_id }}
|
|
release_url: ${{ steps.create_release.outputs.release_url }}
|
|
target_commitish: ${{ github.sha }}
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0 # Fetch all history for changelog generation
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: '1.24'
|
|
cache: true
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '20'
|
|
cache: 'npm'
|
|
cache-dependency-path: 'frontend-modern/package-lock.json'
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
# Install zip for Windows binaries
|
|
sudo apt-get update
|
|
sudo apt-get install -y zip
|
|
|
|
- name: Set up Helm
|
|
uses: azure/setup-helm@v4
|
|
with:
|
|
version: 'v3.15.2'
|
|
|
|
- name: Build release artifacts
|
|
run: |
|
|
echo "Building release ${{ needs.extract_version.outputs.tag }}..."
|
|
./scripts/build-release.sh ${{ needs.extract_version.outputs.version }}
|
|
env:
|
|
PULSE_LICENSE_PUBLIC_KEY: ${{ secrets.PULSE_LICENSE_PUBLIC_KEY }}
|
|
|
|
- name: Post-build health check
|
|
run: |
|
|
echo "Verifying server binary responds to --version and API health..."
|
|
if [ -x ./pulse ]; then
|
|
./pulse --version
|
|
elif [ -x ./cmd/pulse/pulse ]; then
|
|
./cmd/pulse/pulse --version
|
|
else
|
|
echo "::warning::Pulse binary not found after build-release; skipping version check"
|
|
fi
|
|
|
|
- name: Prepare release notes
|
|
id: generate_notes
|
|
run: |
|
|
VERSION="${{ needs.extract_version.outputs.version }}"
|
|
RELEASE_NOTES_INPUT=$(jq -r '.inputs.release_notes // ""' "$GITHUB_EVENT_PATH" 2>/dev/null || echo "")
|
|
|
|
# Save release notes to file
|
|
NOTES_FILE=$(mktemp)
|
|
|
|
if [ -n "$RELEASE_NOTES_INPUT" ]; then
|
|
echo "Using Claude-generated release notes from workflow input"
|
|
printf "%s\n" "$RELEASE_NOTES_INPUT" > "$NOTES_FILE"
|
|
else
|
|
echo "Tag-triggered release - using placeholder notes"
|
|
echo "Release $VERSION" > "$NOTES_FILE"
|
|
echo "" >> "$NOTES_FILE"
|
|
echo "See commit history for changes." >> "$NOTES_FILE"
|
|
fi
|
|
|
|
# Add installation instructions
|
|
{
|
|
echo ""
|
|
echo "## Installation"
|
|
echo ""
|
|
echo "**Docker (recommended):**"
|
|
echo '```bash'
|
|
echo "docker pull rcourtman/pulse:${VERSION}"
|
|
echo '```'
|
|
echo ""
|
|
echo "**Docker Compose:**"
|
|
echo "Update your \`docker-compose.yml\` to use \`rcourtman/pulse:${VERSION}\`"
|
|
echo ""
|
|
echo "See the [Installation Guide](https://github.com/rcourtman/Pulse#installation) for complete setup instructions."
|
|
} >> "$NOTES_FILE"
|
|
|
|
echo "notes_file=${NOTES_FILE}" >> $GITHUB_OUTPUT
|
|
|
|
echo "Release notes content:"
|
|
cat "$NOTES_FILE"
|
|
|
|
- name: Create tag
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
TAG="${{ needs.extract_version.outputs.tag }}"
|
|
HEAD_SHA=$(git rev-parse HEAD)
|
|
|
|
# Check if tag already exists on remote
|
|
REMOTE_TAG_SHA=$(git ls-remote --tags origin "refs/tags/${TAG}" | awk '{print $1}')
|
|
|
|
if [ -n "$REMOTE_TAG_SHA" ]; then
|
|
# Tag exists - check if it points to current HEAD
|
|
# For annotated tags, we need to dereference to get the commit
|
|
REMOTE_COMMIT_SHA=$(git ls-remote --tags origin "refs/tags/${TAG}^{}" | awk '{print $1}')
|
|
# If no dereferenced tag, it's a lightweight tag pointing directly to the commit
|
|
[ -z "$REMOTE_COMMIT_SHA" ] && REMOTE_COMMIT_SHA="$REMOTE_TAG_SHA"
|
|
|
|
if [ "$REMOTE_COMMIT_SHA" = "$HEAD_SHA" ]; then
|
|
echo "Tag ${TAG} already exists and points to HEAD - continuing"
|
|
else
|
|
echo "::error::Tag ${TAG} already exists but points to ${REMOTE_COMMIT_SHA}, not HEAD (${HEAD_SHA}). Delete the tag first: git push origin --delete ${TAG}"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "Creating tag ${TAG}..."
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
git tag -a "${TAG}" -m "Release ${TAG}"
|
|
git push origin "${TAG}"
|
|
fi
|
|
|
|
- name: Create draft release
|
|
id: create_release
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
TAG="${{ needs.extract_version.outputs.tag }}"
|
|
NOTES_FILE="${{ steps.generate_notes.outputs.notes_file }}"
|
|
IS_PRERELEASE="${{ needs.extract_version.outputs.is_prerelease }}"
|
|
|
|
# Check if a release already exists for this tag
|
|
# Note: gh api returns JSON error on 404, so check for valid release ID
|
|
EXISTING_RELEASE=$(gh api "repos/${{ github.repository }}/releases/tags/${TAG}" 2>/dev/null || echo "")
|
|
RELEASE_ID=$(echo "$EXISTING_RELEASE" | jq -r '.id // empty')
|
|
|
|
if [ -n "$RELEASE_ID" ]; then
|
|
RELEASE_URL=$(echo "$EXISTING_RELEASE" | jq -r '.html_url')
|
|
IS_DRAFT=$(echo "$EXISTING_RELEASE" | jq -r '.draft')
|
|
|
|
if [ "$IS_DRAFT" = "true" ]; then
|
|
echo "Draft release already exists for ${TAG} - updating it"
|
|
# Update the existing draft with new notes
|
|
gh api "repos/${{ github.repository }}/releases/${RELEASE_ID}" \
|
|
-X PATCH \
|
|
-F body="$(cat $NOTES_FILE)" \
|
|
-F prerelease=${IS_PRERELEASE} > /dev/null
|
|
else
|
|
echo "::error::Published release already exists for ${TAG}. Cannot re-release the same version."
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "Creating draft release for ${TAG}..."
|
|
# Tag must exist first - draft releases can't create tags (GitHub API limitation)
|
|
# See: https://github.com/cli/cli/issues/11589
|
|
RELEASE_JSON=$(gh api "repos/${{ github.repository }}/releases" \
|
|
-X POST \
|
|
-F tag_name="${TAG}" \
|
|
-F name="Pulse ${TAG}" \
|
|
-F body="$(cat $NOTES_FILE)" \
|
|
-F draft=true \
|
|
-F prerelease=${IS_PRERELEASE})
|
|
|
|
RELEASE_ID=$(echo "$RELEASE_JSON" | jq -r '.id')
|
|
RELEASE_URL=$(echo "$RELEASE_JSON" | jq -r '.html_url')
|
|
fi
|
|
|
|
rm -f "$NOTES_FILE"
|
|
|
|
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then
|
|
echo "::error::Failed to extract release ID from API response"
|
|
exit 1
|
|
fi
|
|
|
|
echo "release_url=${RELEASE_URL}" >> $GITHUB_OUTPUT
|
|
echo "release_id=${RELEASE_ID}" >> $GITHUB_OUTPUT
|
|
|
|
echo "✓ Draft release created: ${TAG} (ID: ${RELEASE_ID})"
|
|
- name: Upload checksums.txt
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
TAG="${{ needs.extract_version.outputs.tag }}"
|
|
|
|
echo "Uploading checksums.txt..."
|
|
gh release upload "${TAG}" release/checksums.txt --clobber
|
|
|
|
# Upload individual .sha256 files for backward compatibility
|
|
echo "Uploading .sha256 checksum files..."
|
|
gh release upload "${TAG}" release/*.sha256 --clobber
|
|
|
|
- name: Upload release assets
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
TAG="${{ needs.extract_version.outputs.tag }}"
|
|
|
|
echo "Uploading release assets..."
|
|
|
|
# Upload tarballs
|
|
gh release upload "${TAG}" release/*.tar.gz --clobber
|
|
|
|
# Upload Windows zip files
|
|
gh release upload "${TAG}" release/*.zip --clobber
|
|
|
|
# Upload Helm chart if it exists
|
|
if ls release/*.tgz 1> /dev/null 2>&1; then
|
|
echo "Uploading Helm chart..."
|
|
gh release upload "${TAG}" release/*.tgz --clobber
|
|
fi
|
|
|
|
# Upload install scripts as standalone assets
|
|
# Users can now use: curl -fsSL https://github.com/rcourtman/Pulse/releases/latest/download/install.sh | bash
|
|
# This ensures scripts are version-locked to the release, not pulled from main branch
|
|
gh release upload "${TAG}" release/install.sh --clobber
|
|
gh release upload "${TAG}" release/install-sensor-proxy.sh --clobber
|
|
gh release upload "${TAG}" release/install-docker.sh --clobber
|
|
gh release upload "${TAG}" release/pulse-auto-update.sh --clobber
|
|
|
|
- name: Publish release
|
|
if: ${{ github.event.inputs.draft_only != 'true' }}
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
TAG="${{ needs.extract_version.outputs.tag }}"
|
|
RELEASE_ID="${{ steps.create_release.outputs.release_id }}"
|
|
|
|
IS_PRERELEASE="${{ needs.extract_version.outputs.is_prerelease }}"
|
|
echo "Publishing release ${TAG} (prerelease: ${IS_PRERELEASE})..."
|
|
|
|
# Only mark as latest if this is NOT a prerelease
|
|
if [ "$IS_PRERELEASE" = "true" ]; then
|
|
gh api "repos/${{ github.repository }}/releases/${RELEASE_ID}" \
|
|
-X PATCH \
|
|
-F draft=false \
|
|
-F make_latest=false
|
|
echo "✓ Release published as prerelease: ${TAG}"
|
|
else
|
|
gh api "repos/${{ github.repository }}/releases/${RELEASE_ID}" \
|
|
-X PATCH \
|
|
-F draft=false \
|
|
-F make_latest=true
|
|
echo "✓ Release published as latest: ${TAG}"
|
|
fi
|
|
|
|
- name: Skip publish (draft only)
|
|
if: ${{ github.event.inputs.draft_only == 'true' }}
|
|
run: |
|
|
echo "Draft-only mode: Release remains as draft for review"
|
|
echo "View draft at: ${{ steps.create_release.outputs.release_url }}"
|
|
|
|
- name: Trigger Docker image publish
|
|
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 }}"
|
|
echo "Triggering Docker image publish for ${TAG}..."
|
|
|
|
# Publishing via API doesn't fire the release webhook, so we dispatch manually
|
|
# Requires WORKFLOW_PAT secret with 'repo' and 'workflow' scopes
|
|
gh workflow run publish-docker.yml -f tag="${TAG}"
|
|
|
|
echo "✓ Docker publish workflow dispatched"
|
|
|
|
|
|
# NOTE: Floating tag promotion and Helm chart release workflows now trigger
|
|
# automatically when publish-docker.yml completes via workflow_run.
|
|
# No need to dispatch them manually - this eliminates race conditions.
|
|
|
|
- name: Trigger demo server update
|
|
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 }}"
|
|
echo "Triggering demo server update for ${TAG}..."
|
|
|
|
gh workflow run update-demo-server.yml -f tag="${TAG}"
|
|
|
|
echo "✓ Demo server update workflow dispatched"
|
|
|
|
- name: Output release information
|
|
run: |
|
|
echo "✅ Release published successfully!"
|
|
echo ""
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo "📦 Release: ${{ needs.extract_version.outputs.tag }}"
|
|
echo "🔗 URL: ${{ steps.create_release.outputs.release_url }}"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
echo "Docker images, Helm chart, and demo server will be updated automatically."
|
|
echo ""
|
|
|
|
validate_release_assets:
|
|
needs:
|
|
- extract_version
|
|
- create_release
|
|
uses: ./.github/workflows/validate-release-assets.yml
|
|
secrets: inherit
|
|
with:
|
|
tag: ${{ needs.extract_version.outputs.tag }}
|
|
version: ${{ needs.extract_version.outputs.version }}
|
|
release_id: ${{ needs.create_release.outputs.release_id }}
|
|
draft: false
|
|
target_commitish: ${{ needs.create_release.outputs.target_commitish }}
|
|
|