arcade-mcp/.github/workflows/promote.yml
2025-06-17 12:57:44 -07:00

404 lines
15 KiB
YAML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: Promote to Production
on:
workflow_dispatch:
inputs:
commit_sha:
description: 'Specific commit SHA to cherry-pick (leave empty to promote everything in main)'
type: string
required: false
default: ''
worker_container_increment:
description: 'Worker container version increment. Defaults to patch.'
type: choice
options:
- major
- minor
- patch
required: false
default: 'patch'
jobs:
promote:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.PROMOTE_PAT_TOKEN }}
- name: Configure Git
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Setup branches
run: |
# Ensure we have latest changes
git fetch origin
# Create production branch if it doesn't exist
if ! git show-ref --verify --quiet refs/remotes/origin/production; then
echo "Creating production branch from main"
git checkout -b production origin/main
git push origin production
else
echo "Production branch already exists"
git checkout production
git pull origin production
fi
# Ensure main is up to date
git checkout main
git pull origin main
- name: Cherry-pick specific commit
if: ${{ inputs.commit_sha != '' }}
run: |
echo "Cherry-picking specific commit: ${{ inputs.commit_sha }}"
# Validate commit exists in main
if ! git cat-file -e "${{ inputs.commit_sha }}^{commit}"; then
echo "Error: Commit ${{ inputs.commit_sha }} does not exist"
exit 1
fi
# Check if commit is in main branch
if ! git merge-base --is-ancestor "${{ inputs.commit_sha }}" main; then
echo "Error: Commit ${{ inputs.commit_sha }} is not in main branch"
exit 1
fi
# Switch to production and cherry-pick
git checkout production
# Check if commit is already in production
if git merge-base --is-ancestor "${{ inputs.commit_sha }}" production; then
echo "Commit ${{ inputs.commit_sha }} is already in production branch"
exit 0
fi
# Cherry-pick the commit
COMMIT_MSG=$(git log -1 --pretty=format:"%s" "${{ inputs.commit_sha }}")
echo "Cherry-picking: $COMMIT_MSG"
if git cherry-pick "${{ inputs.commit_sha }}"; then
echo "✅ Successfully cherry-picked commit"
git push origin production
echo "commit_promoted=true" >> $GITHUB_ENV
else
echo "❌ Cherry-pick failed - conflicts may need resolution"
git cherry-pick --abort
exit 1
fi
- name: Recreate production from main
if: ${{ inputs.commit_sha == '' }}
run: |
echo "Recreating production branch from main"
# Get current commit on main for summary
MAIN_COMMIT=$(git rev-parse main)
MAIN_COMMIT_MSG=$(git log -1 --pretty=format:"%s" main)
echo "Main branch is at: $MAIN_COMMIT ($MAIN_COMMIT_MSG)"
# Check if production branch exists remotely
if git show-ref --verify --quiet refs/remotes/origin/production; then
PROD_COMMIT=$(git rev-parse origin/production)
PROD_COMMIT_MSG=$(git log -1 --pretty=format:"%s" origin/production)
echo "Current production is at: $PROD_COMMIT ($PROD_COMMIT_MSG)"
# Check if they're already the same
if [ "$MAIN_COMMIT" = "$PROD_COMMIT" ]; then
echo "Production is already up to date with main"
echo "production_updated=false" >> $GITHUB_ENV
exit 0
fi
echo "Deleting existing production branch"
git push origin --delete production
# Also delete local production branch if it exists
if git show-ref --verify --quiet refs/heads/production; then
echo "Deleting local production branch"
git branch -D production
fi
else
echo "Production branch doesn't exist, will create new one"
fi
# Create new production branch from main
echo "Creating new production branch from main"
git checkout main
git checkout -b production
git push origin production
echo "✅ Successfully recreated production branch from main"
echo "production_updated=true" >> $GITHUB_ENV
echo "main_commit=$MAIN_COMMIT" >> $GITHUB_ENV
- name: Create summary
if: always()
run: |
echo "## Promotion Summary" >> $GITHUB_STEP_SUMMARY
if [ "${{ inputs.commit_sha }}" != "" ]; then
echo "### Single Commit Promotion" >> $GITHUB_STEP_SUMMARY
echo "**Target Commit:** \`${{ inputs.commit_sha }}\`" >> $GITHUB_STEP_SUMMARY
if [ "${commit_promoted:-false}" = "true" ]; then
echo "✅ **Status:** Successfully promoted to production" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Status:** Promotion failed or commit already existed" >> $GITHUB_STEP_SUMMARY
fi
else
echo "### Bulk Promotion" >> $GITHUB_STEP_SUMMARY
if [ "${production_updated:-false}" = "true" ]; then
echo "✅ **Status:** Successfully updated production branch" >> $GITHUB_STEP_SUMMARY
else
echo " **Status:** No new commits to promote or production already up to date" >> $GITHUB_STEP_SUMMARY
fi
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Branches" >> $GITHUB_STEP_SUMMARY
echo "- **Source:** main" >> $GITHUB_STEP_SUMMARY
echo "- **Target:** production" >> $GITHUB_STEP_SUMMARY
calculate-version:
runs-on: ubuntu-latest
needs: promote
outputs:
version: ${{ steps.calculate_version.outputs.version }}
previous_version: ${{ steps.calculate_version.outputs.previous_version }}
steps:
- name: Checkout production branch
uses: actions/checkout@v4
with:
ref: production
fetch-depth: 0
- name: Get latest release and calculate next version
id: calculate_version
run: |
# Get the latest release version from GitHub
LATEST_RELEASE=$(gh release view --json tagName --jq .tagName 2>/dev/null || echo "")
if [ -z "$LATEST_RELEASE" ]; then
echo "No previous releases found, starting at 0.0.0"
CURRENT_VERSION="0.0.0"
else
# Remove 'v' prefix if present
CURRENT_VERSION=${LATEST_RELEASE#v}
echo "Latest release: $LATEST_RELEASE (version: $CURRENT_VERSION)"
fi
# Split version into components
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
# Increment based on input
case "${{ inputs.worker_container_increment }}" in
major)
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
;;
minor)
MINOR=$((MINOR + 1))
PATCH=0
;;
patch)
PATCH=$((PATCH + 1))
;;
esac
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
echo "New version: $NEW_VERSION"
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "previous_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-and-push:
needs: calculate-version
strategy:
matrix:
include:
- arch: amd64
os: ubuntu-latest
- arch: arm64
os: linux-arm64
runs-on: ${{ matrix.os }}
permissions:
contents: write
packages: write
env:
VERSION: ${{ needs.calculate-version.outputs.version }}
REGISTRY: ghcr.io
steps:
- name: Checkout production branch
uses: actions/checkout@v4
with:
ref: production
fetch-depth: 0
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build distributions
run: |
echo "Building full distribution for production release..."
make full-dist
- name: Build and push Worker images
run: |
# Build both worker and worker-base images
make docker VERSION=${{ env.VERSION }} ARCH=${{ matrix.arch }}
make docker-base VERSION=${{ env.VERSION }} ARCH=${{ matrix.arch }}
# Push to GHCR only
make publish-ghcr VERSION=${{ env.VERSION }} ARCH=${{ matrix.arch }}
push-manifest:
runs-on: ubuntu-latest
needs: [calculate-version, build-and-push]
permissions:
contents: write
packages: write
env:
VERSION: ${{ needs.calculate-version.outputs.version }}
REGISTRY: ghcr.io
steps:
- name: Checkout production branch
uses: actions/checkout@v4
with:
ref: production
fetch-depth: 0
- name: Log in to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push manifest to GHCR
working-directory: ./docker
run: |
make ghcr-manifest VERSION=${{ env.VERSION }}
make ghcr-manifest INSTALL_TOOLKITS=false VERSION=${{ env.VERSION }}
create-release:
runs-on: ubuntu-latest
needs: [calculate-version, push-manifest]
permissions:
contents: write
env:
VERSION: ${{ needs.calculate-version.outputs.version }}
PREVIOUS_VERSION: ${{ needs.calculate-version.outputs.previous_version }}
steps:
- name: Checkout production branch
uses: actions/checkout@v4
with:
ref: production
fetch-depth: 0
token: ${{ secrets.PROMOTE_PAT_TOKEN }}
- name: Generate release notes
id: generate_release_notes
run: |
echo "# Release ${{ env.VERSION }}" > release_notes.md
echo "" >> release_notes.md
echo "## Container Images" >> release_notes.md
echo "" >> release_notes.md
echo "### GitHub Container Registry (GHCR)" >> release_notes.md
echo "\`\`\`bash" >> release_notes.md
echo "docker pull ghcr.io/arcadeai/worker:${{ env.VERSION }}" >> release_notes.md
echo "docker pull ghcr.io/arcadeai/worker-base:${{ env.VERSION }}" >> release_notes.md
echo "\`\`\`" >> release_notes.md
echo "" >> release_notes.md
echo "## What's Changed" >> release_notes.md
echo "" >> release_notes.md
if [ "${{ inputs.commit_sha }}" != "" ]; then
# Case 1: Specific commit SHA to cherry-pick was provided
echo "### Cherry-picked commit:" >> release_notes.md
echo "" >> release_notes.md
git log -1 --pretty=format:"- **%h** %s (%an)" "${{ inputs.commit_sha }}" >> release_notes.md
echo "" >> release_notes.md
echo "_This release contains a single cherry-picked commit from main._" >> release_notes.md
else
# Case 2: Bulk promotion (production == main)
echo "### Commits in this release:" >> release_notes.md
echo "" >> release_notes.md
# Get the list of previous releases to find the last release
PREV_RELEASE_DATE=$(gh release list --limit 5 --json publishedAt,tagName --jq '.[1].publishedAt // empty' 2>/dev/null || echo "")
if [ -z "$PREV_RELEASE_DATE" ]; then
# First promotion ever
echo "**Initial release** - Recent commits from main branch:" >> release_notes.md
echo "" >> release_notes.md
git log -20 --pretty=format:"- **%h** %s (%an)" --reverse >> release_notes.md
else
# Show commits since the previous promotion
echo "Changes since previous release ($(echo $PREV_RELEASE_DATE | cut -d'T' -f1)):" >> release_notes.md
echo "" >> release_notes.md
git log --since="$PREV_RELEASE_DATE" --pretty=format:"- **%h** %s (%an)" --reverse >> release_notes.md
echo "" >> release_notes.md
# Fallback if no commits since last release (shouldn't happen, but just in case)
if [ $(git log --since="$PREV_RELEASE_DATE" --oneline | wc -l) -eq 0 ]; then
echo "_No commits found since previous release. Showing recent commits:_" >> release_notes.md
echo "" >> release_notes.md
git log -10 --pretty=format:"- **%h** %s (%an)" --reverse >> release_notes.md
fi
fi
echo "" >> release_notes.md
echo "_This release includes all changes from the main branch._" >> release_notes.md
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create GitHub Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ env.VERSION }}
release_name: Release v${{ env.VERSION }}
body_path: release_notes.md
draft: false
prerelease: false
- name: Update workflow summary
run: |
echo "" >> $GITHUB_STEP_SUMMARY
echo "## 🚀 Container Release" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Version Information" >> $GITHUB_STEP_SUMMARY
echo "- **Previous Version:** v${{ env.PREVIOUS_VERSION }}" >> $GITHUB_STEP_SUMMARY
echo "- **New Version:** v${{ env.VERSION }}" >> $GITHUB_STEP_SUMMARY
echo "- **Increment Type:** ${{ inputs.worker_container_increment }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Container Images Published to GHCR" >> $GITHUB_STEP_SUMMARY
echo "- ✅ ghcr.io/arcadeai/worker:${{ env.VERSION }}" >> $GITHUB_STEP_SUMMARY
echo "- ✅ ghcr.io/arcadeai/worker-base:${{ env.VERSION }}" >> $GITHUB_STEP_SUMMARY