mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
Add all new CI/CD workflow files for the staged branch promotion pipeline (develop alpha beta main). Push triggers are intentionally disabled workflows use workflow_dispatch or workflow_call only. No changes to existing ci.yml. All workflows are inert until activated in a follow-up PR. New workflows: - feature-pr.yml: auto-create PRs to develop (disabled) - hotfix-pr.yml: emergency hotfix PRs to main (disabled) - promote-branch.yml: staged promotion (disabled) - release-orchestrator.yml: release pipeline (disabled) - deployment-strategy.yml: npm + Docker deploy (workflow_call) - testing-strategy.yml: progressive test gates (workflow_call) - generate-changelog.yml: changelog generation (workflow_call) - version-operations.yml: version bumping (workflow_call) - release.yml: manual release trigger (workflow_dispatch) - rollback.yml: emergency rollback (workflow_dispatch) - discord-notify action: reusable notification Also adds pipeline docs and updates CONTRIBUTING.md with future branch strategy (clearly marked as not yet active). Split from #10755 for safe, additive merge.
266 lines
9.0 KiB
YAML
266 lines
9.0 KiB
YAML
name: Release Orchestrator
|
|
|
|
# Orchestrates staged releases for openclaw
|
|
#
|
|
# This workflow is called when code is promoted to main (stable release)
|
|
# or can be triggered manually for alpha/beta releases from their branches.
|
|
#
|
|
# Flow: version → changelog → test → deploy → release
|
|
|
|
# NOTE: push-to-main trigger disabled until staging pipeline is activated.
|
|
# To enable: uncomment the push block below.
|
|
# push:
|
|
# branches:
|
|
# - main
|
|
# paths-ignore:
|
|
# - "docs/**"
|
|
# - "*.md"
|
|
# - ".github/workflows/docs-*.yml"
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
release_type:
|
|
description: "Release type: alpha, beta, or stable"
|
|
required: true
|
|
type: string
|
|
source_branch:
|
|
description: "Source branch for the release"
|
|
required: true
|
|
type: string
|
|
dry_run:
|
|
description: "Perform a dry run without publishing"
|
|
required: false
|
|
type: boolean
|
|
default: false
|
|
outputs:
|
|
version:
|
|
description: "The released version"
|
|
value: ${{ jobs.version.outputs.new_version }}
|
|
release_url:
|
|
description: "URL to the GitHub release"
|
|
value: ${{ jobs.release.outputs.release_url }}
|
|
status:
|
|
description: "Release status"
|
|
value: ${{ jobs.release.outputs.status }}
|
|
secrets:
|
|
NPM_TOKEN:
|
|
required: false
|
|
DISCORD_WEBHOOK_URL:
|
|
required: false
|
|
|
|
concurrency:
|
|
group: release-${{ github.ref_name }}
|
|
cancel-in-progress: false
|
|
|
|
permissions:
|
|
contents: write
|
|
packages: write
|
|
|
|
jobs:
|
|
# Determine release parameters (push vs workflow_call)
|
|
determine-params:
|
|
name: Determine Parameters
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
release_type: ${{ steps.params.outputs.release_type }}
|
|
source_branch: ${{ steps.params.outputs.source_branch }}
|
|
dry_run: ${{ steps.params.outputs.dry_run }}
|
|
steps:
|
|
- name: Set parameters
|
|
id: params
|
|
run: |
|
|
# When triggered by push to main, use stable defaults
|
|
if [ "${{ github.event_name }}" = "push" ]; then
|
|
echo "release_type=stable" >> $GITHUB_OUTPUT
|
|
echo "source_branch=main" >> $GITHUB_OUTPUT
|
|
echo "dry_run=false" >> $GITHUB_OUTPUT
|
|
else
|
|
# workflow_call - use provided inputs
|
|
echo "release_type=${{ inputs.release_type }}" >> $GITHUB_OUTPUT
|
|
echo "source_branch=${{ inputs.source_branch }}" >> $GITHUB_OUTPUT
|
|
echo "dry_run=${{ inputs.dry_run }}" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
# Get commits since last release
|
|
get-commits:
|
|
name: Get Commits
|
|
needs: determine-params
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
commits: ${{ steps.commits.outputs.commits }}
|
|
has_changes: ${{ steps.commits.outputs.has_changes }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
ref: ${{ needs.determine-params.outputs.source_branch }}
|
|
|
|
- name: Get commits since last tag
|
|
id: commits
|
|
run: |
|
|
# Get latest tag for this release type
|
|
case "${{ needs.determine-params.outputs.release_type }}" in
|
|
alpha)
|
|
PATTERN="v*-alpha.*"
|
|
;;
|
|
beta)
|
|
PATTERN="v*-beta.*"
|
|
;;
|
|
stable)
|
|
PATTERN="v[0-9]*.[0-9]*.[0-9]*"
|
|
;;
|
|
esac
|
|
|
|
# Filter out prerelease tags for stable (glob * matches -alpha/-beta suffixes)
|
|
if [ "${{ needs.determine-params.outputs.release_type }}" = "stable" ]; then
|
|
LATEST_TAG=$(git tag -l "$PATTERN" --sort=-v:refname | grep -v -E '-(alpha|beta)\.' | head -1)
|
|
else
|
|
LATEST_TAG=$(git tag -l "$PATTERN" --sort=-v:refname | head -1)
|
|
fi
|
|
|
|
if [ -z "$LATEST_TAG" ]; then
|
|
# No previous tag, use all commits
|
|
LATEST_TAG=$(git rev-list --max-parents=0 HEAD)
|
|
echo "No previous ${{ needs.determine-params.outputs.release_type }} tag found, using initial commit"
|
|
else
|
|
echo "Latest ${{ needs.determine-params.outputs.release_type }} tag: $LATEST_TAG"
|
|
fi
|
|
|
|
COMMITS=$(git log ${LATEST_TAG}..HEAD --oneline --format="- %s (%h)")
|
|
|
|
if [ -z "$COMMITS" ]; then
|
|
echo "has_changes=false" >> $GITHUB_OUTPUT
|
|
echo "commits=" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "has_changes=true" >> $GITHUB_OUTPUT
|
|
DELIM="COMMITS_$(openssl rand -hex 16)"
|
|
echo "commits<<${DELIM}" >> $GITHUB_OUTPUT
|
|
echo "$COMMITS" >> $GITHUB_OUTPUT
|
|
echo "${DELIM}" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
# Version operations
|
|
version:
|
|
name: Version
|
|
needs: [determine-params, get-commits]
|
|
if: needs.get-commits.outputs.has_changes == 'true'
|
|
uses: ./.github/workflows/version-operations.yml
|
|
with:
|
|
release_type: ${{ needs.determine-params.outputs.release_type }}
|
|
source_branch: ${{ needs.determine-params.outputs.source_branch }}
|
|
should_bump: true
|
|
dry_run: ${{ needs.determine-params.outputs.dry_run }}
|
|
|
|
# Generate changelog
|
|
changelog:
|
|
name: Changelog
|
|
needs: [determine-params, get-commits, version]
|
|
if: needs.get-commits.outputs.has_changes == 'true'
|
|
uses: ./.github/workflows/generate-changelog.yml
|
|
with:
|
|
version: ${{ needs.version.outputs.new_version }}
|
|
release_type: ${{ needs.determine-params.outputs.release_type }}
|
|
|
|
# Run full test suite for the release type
|
|
test:
|
|
name: Test
|
|
needs: [determine-params, get-commits, version]
|
|
if: needs.get-commits.outputs.has_changes == 'true'
|
|
uses: ./.github/workflows/testing-strategy.yml
|
|
with:
|
|
test_stage: ${{ needs.determine-params.outputs.release_type }}
|
|
app_version: ${{ needs.version.outputs.new_version }}
|
|
secrets: inherit
|
|
|
|
# Deploy (npm + Docker)
|
|
deploy:
|
|
name: Deploy
|
|
needs: [determine-params, version, test]
|
|
if: ${{ needs.determine-params.outputs.dry_run != 'true' && needs.test.outputs.test_status == 'passed' }}
|
|
uses: ./.github/workflows/deployment-strategy.yml
|
|
with:
|
|
deployment_stage: ${{ needs.determine-params.outputs.release_type }}
|
|
app_version: ${{ needs.version.outputs.new_version }}
|
|
source_branch: ${{ needs.determine-params.outputs.source_branch }}
|
|
secrets: inherit
|
|
|
|
# Create GitHub release
|
|
release:
|
|
name: GitHub Release
|
|
needs: [determine-params, version, changelog, deploy]
|
|
if: ${{ needs.determine-params.outputs.dry_run != 'true' }}
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
release_url: ${{ steps.create-release.outputs.html_url }}
|
|
status: ${{ steps.status.outputs.status }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ needs.determine-params.outputs.source_branch }}
|
|
|
|
- name: Create GitHub Release
|
|
id: create-release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
tag_name: v${{ needs.version.outputs.new_version }}
|
|
name: openclaw ${{ needs.version.outputs.new_version }}
|
|
body: ${{ needs.changelog.outputs.changelog }}
|
|
prerelease: ${{ needs.determine-params.outputs.release_type != 'stable' }}
|
|
draft: false
|
|
|
|
- name: Set status
|
|
id: status
|
|
run: echo "status=success" >> $GITHUB_OUTPUT
|
|
|
|
# Notify on success
|
|
notify-success:
|
|
name: Notify Success
|
|
needs: [determine-params, version, release]
|
|
if: ${{ !cancelled() && needs.release.result == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Discord notification
|
|
if: env.DISCORD_WEBHOOK_URL != ''
|
|
uses: ./.github/actions/discord-notify
|
|
with:
|
|
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
|
title: "🎉 Released: openclaw v${{ needs.version.outputs.new_version }}"
|
|
description: |
|
|
**Type**: ${{ needs.determine-params.outputs.release_type }}
|
|
**Release**: ${{ needs.release.outputs.release_url }}
|
|
color: "3066993"
|
|
|
|
# Notify on failure
|
|
notify-failure:
|
|
name: Notify Failure
|
|
needs: [determine-params, version, test, deploy, release]
|
|
if: |
|
|
!cancelled() &&
|
|
needs.version.result != 'skipped' &&
|
|
(needs.test.result == 'failure' || needs.deploy.result == 'failure' || needs.release.result == 'failure')
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Discord notification
|
|
if: env.DISCORD_WEBHOOK_URL != ''
|
|
uses: ./.github/actions/discord-notify
|
|
with:
|
|
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
|
title: "❌ Release Failed: ${{ needs.determine-params.outputs.release_type }}"
|
|
description: |
|
|
**Branch**: ${{ needs.determine-params.outputs.source_branch }}
|
|
**Tests**: ${{ needs.test.outputs.test_status }}
|
|
[View Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
|
color: "15158332"
|