fix(ci): fix yaml formatting in workflow heredocs

This commit is contained in:
quotentiroler
2026-02-06 15:52:38 -08:00
parent 4f07791455
commit 845eafaacb
12 changed files with 184 additions and 207 deletions

View File

@@ -14,23 +14,23 @@ inputs:
color:
description: Embed color (decimal)
required: false
default: '3447003'
default: "3447003"
username:
description: Bot username
required: false
default: 'OpenClaw CI'
default: "OpenClaw CI"
avatar_url:
description: Bot avatar URL
required: false
default: 'https://avatars.githubusercontent.com/u/182880377'
default: "https://avatars.githubusercontent.com/u/182880377"
timestamp:
description: Include timestamp
required: false
default: 'true'
default: "true"
fields:
description: JSON array of embed fields
required: false
default: '[]'
default: "[]"
runs:
using: composite
@@ -42,10 +42,10 @@ runs:
if [ "${{ inputs.timestamp }}" = "true" ]; then
TIMESTAMP="\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\","
fi
# Escape description for JSON (use double quotes for variable expansion)
DESCRIPTION=$(echo "${{ inputs.description }}" | jq -Rs .)
PAYLOAD=$(cat <<EOF
{
"username": "${{ inputs.username }}",
@@ -60,7 +60,7 @@ runs:
}
EOF
)
curl -H "Content-Type: application/json" \
-d "$PAYLOAD" \
"${{ inputs.webhook_url }}"

View File

@@ -8,13 +8,13 @@ on:
# Called by testing-strategy.yml for releases
outputs:
checks_result:
description: 'Result of core checks'
description: "Result of core checks"
value: ${{ jobs.checks.result }}
secrets_result:
description: 'Result of secrets scan'
description: "Result of secrets scan"
value: ${{ jobs.secrets.result }}
windows_result:
description: 'Result of Windows checks'
description: "Result of Windows checks"
value: ${{ jobs.checks-windows.result }}
concurrency:

View File

@@ -11,26 +11,26 @@ on:
workflow_call:
inputs:
deployment_stage:
description: 'Deployment stage: alpha, beta, or stable'
description: "Deployment stage: alpha, beta, or stable"
required: true
type: string
app_version:
description: 'Version of the application to deploy'
description: "Version of the application to deploy"
required: true
type: string
source_branch:
description: 'Source branch for deployment'
description: "Source branch for deployment"
required: true
type: string
outputs:
deployment_status:
description: 'Status of the deployment'
description: "Status of the deployment"
value: ${{ jobs.deploy-summary.outputs.status }}
npm_url:
description: 'npm package URL'
description: "npm package URL"
value: ${{ jobs.deploy-summary.outputs.npm_url }}
docker_url:
description: 'Docker image URL'
description: "Docker image URL"
value: ${{ jobs.deploy-summary.outputs.docker_url }}
secrets:
NPM_TOKEN:
@@ -74,7 +74,7 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 22.x
registry-url: 'https://registry.npmjs.org'
registry-url: "https://registry.npmjs.org"
- name: Setup pnpm (corepack retry)
run: |
@@ -122,9 +122,9 @@ jobs:
echo "npm_url=" >> $GITHUB_OUTPUT
exit 0
fi
NPM_TAG="${{ steps.npm-tag.outputs.tag }}"
if npm publish --tag "$NPM_TAG" --access public; then
echo "status=success" >> $GITHUB_OUTPUT
echo "npm_url=https://www.npmjs.com/package/openclaw/v/${{ inputs.app_version }}" >> $GITHUB_OUTPUT
@@ -255,13 +255,13 @@ jobs:
STAGE="${{ inputs.deployment_stage }}"
VERSION="${{ inputs.app_version }}"
IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
# Create version manifest
docker buildx imagetools create \
-t "${IMAGE}:${VERSION}" \
"${IMAGE}:${VERSION}-amd64" \
"${IMAGE}:${VERSION}-arm64"
# Create stage manifest (beta or latest)
if [ "$STAGE" = "stable" ]; then
docker buildx imagetools create \
@@ -294,16 +294,16 @@ jobs:
NPM_STATUS="${{ needs.npm-publish.outputs.status || 'skipped' }}"
NPM_URL="${{ needs.npm-publish.outputs.npm_url }}"
DOCKER_URL="${{ needs.docker-manifest.outputs.docker_url || '' }}"
echo "npm_url=$NPM_URL" >> $GITHUB_OUTPUT
echo "docker_url=$DOCKER_URL" >> $GITHUB_OUTPUT
if [ "$NPM_STATUS" = "success" ] || [ "$NPM_STATUS" = "skipped" ]; then
echo "status=success" >> $GITHUB_OUTPUT
else
echo "status=failed" >> $GITHUB_OUTPUT
fi
# Generate summary
echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
@@ -331,19 +331,19 @@ jobs:
uses: ./.github/actions/discord-notify
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
title: '🚀 Deployed: ${{ inputs.deployment_stage }} v${{ inputs.app_version }}'
title: "🚀 Deployed: ${{ inputs.deployment_stage }} v${{ inputs.app_version }}"
description: |
**npm**: ${{ needs.deploy-summary.outputs.npm_url || 'skipped' }}
**Docker**: ${{ needs.deploy-summary.outputs.docker_url || 'skipped' }}
color: '3066993'
color: "3066993"
- name: Discord failure notification
if: ${{ env.DISCORD_WEBHOOK_URL != '' && needs.deploy-summary.outputs.status != 'success' }}
uses: ./.github/actions/discord-notify
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
title: '❌ Deployment Failed: ${{ inputs.deployment_stage }}'
title: "❌ Deployment Failed: ${{ inputs.deployment_stage }}"
description: |
**Version**: ${{ inputs.app_version }}
[View Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
color: '15158332'
color: "15158332"

View File

@@ -6,9 +6,9 @@ name: Feature PR
on:
push:
branches:
- 'dev/**'
- 'feature/**'
- 'fix/**'
- "dev/**"
- "feature/**"
- "fix/**"
permissions:
contents: read
@@ -50,10 +50,10 @@ jobs:
run: |
BRANCH="${{ github.ref_name }}"
TARGET="${{ steps.target.outputs.branch }}"
# Check if PR already exists
EXISTING=$(gh pr list --head "$BRANCH" --base "$TARGET" --json number --jq '.[0].number // empty')
if [ -n "$EXISTING" ]; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "pr_number=$EXISTING" >> $GITHUB_OUTPUT
@@ -69,24 +69,30 @@ jobs:
run: |
BRANCH="${{ github.ref_name }}"
TARGET="${{ steps.target.outputs.branch }}"
# Extract title from branch name (dev/foo-bar → foo bar)
TITLE=$(echo "$BRANCH" | sed 's|^dev/||; s|^feature/||; s|^fix/||; s|-| |g; s|_| |g')
# Capitalize first letter
TITLE="$(echo "${TITLE:0:1}" | tr '[:lower:]' '[:upper:]')${TITLE:1}"
# Create PR body
BODY=$(cat << 'PRBODY'
Auto-created PR from feature branch.
## Changes
<!-- Describe your changes here -->
---
*This PR was auto-created by the feature-pr workflow.*
PRBODY
)
gh pr create \
--base "$TARGET" \
--head "$BRANCH" \
--title "$TITLE" \
--body "Auto-created PR from \`$BRANCH\` to \`$TARGET\`.
--body "$BODY"
## Changes
<!-- Describe your changes here -->
---
*This PR was auto-created by the feature-pr workflow.*"
echo "Created PR: $BRANCH → $TARGET"

View File

@@ -4,24 +4,24 @@ on:
workflow_call:
inputs:
version:
description: 'Version for the changelog'
description: "Version for the changelog"
required: true
type: string
commits:
description: 'JSON string of commits'
description: "JSON string of commits"
required: false
type: string
default: ''
default: ""
release_type:
description: 'Release type: alpha, beta, or stable'
description: "Release type: alpha, beta, or stable"
required: true
type: string
outputs:
changelog:
description: 'Generated changelog content'
description: "Generated changelog content"
value: ${{ jobs.generate.outputs.changelog }}
changelog_file:
description: 'Path to changelog file'
description: "Path to changelog file"
value: ${{ jobs.generate.outputs.changelog_file }}
jobs:
@@ -43,26 +43,26 @@ jobs:
VERSION="${{ inputs.version }}"
RELEASE_TYPE="${{ inputs.release_type }}"
DATE=$(date +%Y-%m-%d)
# Start building changelog
CHANGELOG="## v${VERSION} (${DATE})\n\n"
# Initialize sections
FEATURES=""
FIXES=""
DOCS=""
CHORES=""
OTHER=""
# Get commits since last tag
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -n "$LATEST_TAG" ]; then
COMMITS=$(git log ${LATEST_TAG}..HEAD --oneline --format="%s")
else
COMMITS=$(git log --oneline --format="%s" | head -50)
fi
# Categorize commits by conventional commit type
while IFS= read -r commit; do
if [ -z "$commit" ]; then
@@ -89,44 +89,44 @@ jobs:
OTHER="${OTHER}- ${commit}\n"
fi
done <<< "$COMMITS"
# Build final changelog
if [ -n "$FEATURES" ]; then
CHANGELOG="${CHANGELOG}### ✨ Features\n\n${FEATURES}\n"
fi
if [ -n "$FIXES" ]; then
CHANGELOG="${CHANGELOG}### 🐛 Bug Fixes\n\n${FIXES}\n"
fi
if [ -n "$DOCS" ]; then
CHANGELOG="${CHANGELOG}### 📚 Documentation\n\n${DOCS}\n"
fi
if [ -n "$CHORES" ]; then
CHANGELOG="${CHANGELOG}### 🔧 Maintenance\n\n${CHORES}\n"
fi
if [ -n "$OTHER" ]; then
CHANGELOG="${CHANGELOG}### Other Changes\n\n${OTHER}\n"
fi
# If no categorized commits, add a simple message
if [ -z "$FEATURES" ] && [ -z "$FIXES" ] && [ -z "$DOCS" ] && [ -z "$CHORES" ] && [ -z "$OTHER" ]; then
CHANGELOG="${CHANGELOG}No notable changes in this release.\n"
fi
# Add release metadata
CHANGELOG="${CHANGELOG}\n---\n\n"
CHANGELOG="${CHANGELOG}**Release Type**: ${RELEASE_TYPE}\n"
CHANGELOG="${CHANGELOG}**Full Changelog**: https://github.com/${{ github.repository }}/compare/${LATEST_TAG:-initial}...v${VERSION}\n"
# Escape for multiline output (use unique delimiter to avoid collision with commit messages)
echo "changelog<<__CHANGELOG_HEREDOC_DELIMITER__" >> $GITHUB_OUTPUT
echo -e "$CHANGELOG" >> $GITHUB_OUTPUT
echo "__CHANGELOG_HEREDOC_DELIMITER__" >> $GITHUB_OUTPUT
echo "changelog_file=CHANGELOG.md" >> $GITHUB_OUTPUT
# Also write to step summary
echo "## Generated Changelog" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

View File

@@ -8,7 +8,7 @@ name: Hotfix PR
on:
push:
branches:
- 'hotfix/**'
- "hotfix/**"
permissions:
contents: read
@@ -30,9 +30,9 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH="${{ github.ref_name }}"
EXISTING=$(gh pr list --head "$BRANCH" --base main --json number --jq '.[0].number // empty')
if [ -n "$EXISTING" ]; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "pr_number=$EXISTING" >> $GITHUB_OUTPUT
@@ -47,39 +47,45 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BRANCH="${{ github.ref_name }}"
# Extract title from branch name
TITLE=$(echo "$BRANCH" | sed 's|^hotfix/||; s|-| |g; s|_| |g')
TITLE="🚨 HOTFIX: $(echo "${TITLE:0:1}" | tr '[:lower:]' '[:upper:]')${TITLE:1}"
# Create PR body
BODY=$(cat << 'PRBODY'
## 🚨 Emergency Hotfix
**This PR bypasses the normal staging pipeline.**
### What's broken?
<!-- Describe the production issue -->
### Root cause
<!-- Brief explanation of what went wrong -->
### Fix
<!-- What this hotfix does -->
### Verification
- [ ] Tested locally
- [ ] Reviewed by at least one other maintainer
- [ ] Post-merge monitoring plan in place
---
⚠️ **After merging:** Cherry-pick this fix to `develop`, `alpha`, and `beta` branches to keep them in sync.
*This PR was auto-created by the hotfix-pr workflow.*
PRBODY
)
gh pr create \
--base main \
--head "$BRANCH" \
--title "$TITLE" \
--label "hotfix,priority:critical" \
--body "## 🚨 Emergency Hotfix
--body "$BODY"
**This PR bypasses the normal staging pipeline.**
### What's broken?
<!-- Describe the production issue -->
### Root cause
<!-- Brief explanation of what went wrong -->
### Fix
<!-- What this hotfix does -->
### Verification
- [ ] Tested locally
- [ ] Reviewed by at least one other maintainer
- [ ] Post-merge monitoring plan in place
---
⚠️ **After merging:** Cherry-pick this fix to \`develop\`, \`alpha\`, and \`beta\` branches to keep them in sync.
*This PR was auto-created by the hotfix-pr workflow.*"
echo "Created hotfix PR: $BRANCH → main"
- name: Add urgent label

View File

@@ -20,7 +20,7 @@ on:
workflow_dispatch:
inputs:
source_branch:
description: 'Source branch to promote from'
description: "Source branch to promote from"
required: true
type: choice
options:
@@ -28,7 +28,7 @@ on:
- alpha
- beta
skip_tests:
description: 'Skip tests (use with caution)'
description: "Skip tests (use with caution)"
required: false
type: boolean
default: false
@@ -57,9 +57,9 @@ jobs:
else
SOURCE="${{ github.ref_name }}"
fi
echo "source=$SOURCE" >> $GITHUB_OUTPUT
case "$SOURCE" in
develop)
echo "target=alpha" >> $GITHUB_OUTPUT
@@ -117,10 +117,10 @@ jobs:
id: commits
run: |
TARGET="${{ needs.determine-target.outputs.target }}"
# Fetch target branch
git fetch origin $TARGET 2>/dev/null || true
# Get commits not in target
if git rev-parse origin/$TARGET >/dev/null 2>&1; then
COMMIT_COUNT=$(git rev-list --count origin/$TARGET..HEAD 2>/dev/null || echo "0")
@@ -129,7 +129,7 @@ jobs:
COMMIT_COUNT=$(git rev-list --count HEAD 2>/dev/null || echo "0")
COMMIT_SUMMARY=$(git log --oneline --format="- %s (%h)" 2>/dev/null | head -20 || echo "Initial promotion")
fi
echo "count=$COMMIT_COUNT" >> $GITHUB_OUTPUT
echo "summary<<EOF" >> $GITHUB_OUTPUT
echo "$COMMIT_SUMMARY" >> $GITHUB_OUTPUT
@@ -142,36 +142,36 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
branch: promote/${{ needs.determine-target.outputs.source }}-to-${{ needs.determine-target.outputs.target }}
base: ${{ needs.determine-target.outputs.target }}
title: '🚀 Promote: ${{ needs.determine-target.outputs.source }} → ${{ needs.determine-target.outputs.target }}'
title: "🚀 Promote: ${{ needs.determine-target.outputs.source }} → ${{ needs.determine-target.outputs.target }}"
body: |
## Staged Promotion
| Property | Value |
|----------|-------|
| Source | `${{ needs.determine-target.outputs.source }}` |
| Target | `${{ needs.determine-target.outputs.target }}` |
| Test Stage | `${{ needs.determine-target.outputs.test_stage }}` |
| Test Result | ${{ needs.run-tests.outputs.test_status == 'passed' && '✅ Passed' || (inputs.skip_tests && '⚠️ Skipped' || '❓ Unknown') }} |
### Changes (${{ steps.commits.outputs.count }} commits)
${{ steps.commits.outputs.summary }}
### Test Coverage by Stage
| Stage | Tests |
|-------|-------|
| develop | tsgo, lint, format, protocol, unit (Node + Bun) |
| alpha | + secrets scan |
| beta | + Windows tests |
| stable | + macOS tests, install smoke |
### Checklist
- [ ] Changes reviewed
- [ ] CI passing
- [ ] Ready to promote
---
*Auto-generated by the branch promotion workflow.*
labels: |
@@ -214,11 +214,11 @@ jobs:
uses: ./.github/actions/discord-notify
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
title: '🔄 Promotion PR: ${{ needs.determine-target.outputs.source }} → ${{ needs.determine-target.outputs.target }}'
title: "🔄 Promotion PR: ${{ needs.determine-target.outputs.source }} → ${{ needs.determine-target.outputs.target }}"
description: |
**PR**: ${{ needs.create-promotion-pr.outputs.pr_url }}
**Stage**: ${{ needs.determine-target.outputs.test_stage }}
color: '3447003'
color: "3447003"
# Handle failed tests
notify-failure:
@@ -240,9 +240,9 @@ jobs:
uses: ./.github/actions/discord-notify
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
title: '❌ Promotion Blocked: ${{ needs.determine-target.outputs.source }}'
title: "❌ Promotion Blocked: ${{ needs.determine-target.outputs.source }}"
description: |
**Target**: ${{ needs.determine-target.outputs.target }}
**Reason**: Tests failed
[View Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
color: '15158332'
color: "15158332"

View File

@@ -11,27 +11,27 @@ on:
workflow_call:
inputs:
release_type:
description: 'Release type: alpha, beta, or stable'
description: "Release type: alpha, beta, or stable"
required: true
type: string
source_branch:
description: 'Source branch for the release'
description: "Source branch for the release"
required: true
type: string
dry_run:
description: 'Perform a dry run without publishing'
description: "Perform a dry run without publishing"
required: false
type: boolean
default: false
outputs:
version:
description: 'The released version'
description: "The released version"
value: ${{ jobs.version.outputs.new_version }}
release_url:
description: 'URL to the GitHub release'
description: "URL to the GitHub release"
value: ${{ jobs.release.outputs.release_url }}
status:
description: 'Release status'
description: "Release status"
value: ${{ jobs.release.outputs.status }}
secrets:
NPM_TOKEN:
@@ -69,9 +69,9 @@ jobs:
PATTERN="v[0-9]*.[0-9]*.[0-9]*"
;;
esac
LATEST_TAG=$(git tag -l "$PATTERN" --sort=-v:refname | head -1)
if [ -z "$LATEST_TAG" ]; then
# No previous tag, use all commits
LATEST_TAG=$(git rev-list --max-parents=0 HEAD)
@@ -79,9 +79,9 @@ jobs:
else
echo "Latest ${{ inputs.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
@@ -184,11 +184,11 @@ jobs:
uses: ./.github/actions/discord-notify
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
title: '🎉 Released: openclaw v${{ needs.version.outputs.new_version }}'
title: "🎉 Released: openclaw v${{ needs.version.outputs.new_version }}"
description: |
**Type**: ${{ inputs.release_type }}
**Release**: ${{ needs.release.outputs.release_url }}
color: '3066993'
color: "3066993"
# Notify on failure
notify-failure:
@@ -207,9 +207,9 @@ jobs:
uses: ./.github/actions/discord-notify
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
title: '❌ Release Failed: ${{ inputs.release_type }}'
title: "❌ Release Failed: ${{ inputs.release_type }}"
description: |
**Branch**: ${{ inputs.source_branch }}
**Tests**: ${{ needs.test.outputs.test_status }}
[View Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
color: '15158332'
color: "15158332"

View File

@@ -4,23 +4,23 @@ name: Release
#
# Branch → Release Type mapping:
# alpha → releases from 'alpha' branch with -alpha.N suffix
# beta → releases from 'beta' branch with -beta.N suffix
# beta → releases from 'beta' branch with -beta.N suffix
# stable → releases from 'main' branch with YYYY.M.D version
on:
workflow_dispatch:
inputs:
release_type:
description: 'Release type'
description: "Release type"
required: true
type: choice
options:
- alpha
- beta
- stable
default: 'alpha'
default: "alpha"
dry_run:
description: 'Dry run (no publish)'
description: "Dry run (no publish)"
required: false
type: boolean
default: false

View File

@@ -11,17 +11,17 @@ on:
workflow_call:
inputs:
test_stage:
description: 'Testing stage: develop, alpha, beta, or stable'
description: "Testing stage: develop, alpha, beta, or stable"
required: true
type: string
app_version:
description: 'Version of the application being tested'
description: "Version of the application being tested"
required: false
type: string
default: 'dev'
default: "dev"
outputs:
test_status:
description: 'Overall test status'
description: "Overall test status"
value: ${{ jobs.test-summary.outputs.overall_status }}
secrets:
DISCORD_WEBHOOK_URL:
@@ -154,7 +154,7 @@ jobs:
echo "| CI (checks + secrets + windows) | ${{ needs.ci.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| macOS Checks | ${{ needs.checks-macos.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
echo "| Install Smoke | ${{ needs.install-smoke.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
# CI must pass
if [ "${{ needs.ci.result }}" != "success" ]; then
echo "overall_status=failed" >> $GITHUB_OUTPUT
@@ -162,11 +162,11 @@ jobs:
echo "### ❌ Core CI failed" >> $GITHUB_STEP_SUMMARY
exit 0
fi
# Stage-specific checks (stable only has extras)
STAGE="${{ inputs.test_stage }}"
FAILED=false
if [ "$STAGE" = "stable" ]; then
if [ "${{ needs.checks-macos.result }}" = "failure" ]; then
FAILED=true
@@ -175,7 +175,7 @@ jobs:
FAILED=true
fi
fi
if [ "$FAILED" = "true" ]; then
echo "overall_status=failed" >> $GITHUB_OUTPUT
echo "" >> $GITHUB_STEP_SUMMARY
@@ -203,17 +203,17 @@ jobs:
uses: ./.github/actions/discord-notify
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
title: '✅ Tests Passed: ${{ inputs.test_stage }} v${{ inputs.app_version }}'
description: 'All tests passed for ${{ inputs.test_stage }} stage!'
color: '3066993'
title: "✅ Tests Passed: ${{ inputs.test_stage }} v${{ inputs.app_version }}"
description: "All tests passed for ${{ inputs.test_stage }} stage!"
color: "3066993"
- name: Discord failure notification
if: ${{ env.DISCORD_WEBHOOK_URL != '' && needs.test-summary.outputs.overall_status != 'passed' }}
uses: ./.github/actions/discord-notify
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
title: '❌ Tests Failed: ${{ inputs.test_stage }} v${{ inputs.app_version }}'
title: "❌ Tests Failed: ${{ inputs.test_stage }} v${{ inputs.app_version }}"
description: |
Some tests failed for ${{ inputs.test_stage }} stage.
[View Logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
color: '15158332'
color: "15158332"

View File

@@ -9,32 +9,32 @@ on:
workflow_call:
inputs:
release_type:
description: 'Release type: alpha, beta, or stable'
description: "Release type: alpha, beta, or stable"
required: true
type: string
source_branch:
description: 'Source branch'
description: "Source branch"
required: true
type: string
should_bump:
description: 'Whether to bump the version'
description: "Whether to bump the version"
required: false
type: boolean
default: true
dry_run:
description: 'Perform a dry run without committing'
description: "Perform a dry run without committing"
required: false
type: boolean
default: false
outputs:
current_version:
description: 'Current version before bump'
description: "Current version before bump"
value: ${{ jobs.version.outputs.current_version }}
new_version:
description: 'New version after bump'
description: "New version after bump"
value: ${{ jobs.version.outputs.new_version }}
version_tag:
description: 'Version tag (with v prefix)'
description: "Version tag (with v prefix)"
value: ${{ jobs.version.outputs.version_tag }}
jobs:
@@ -70,13 +70,13 @@ jobs:
run: |
CURRENT="${{ steps.get-version.outputs.current }}"
RELEASE_TYPE="${{ inputs.release_type }}"
# Get current date components
YEAR=$(date +%Y)
MONTH=$(date +%-m)
DAY=$(date +%-d)
TODAY="${YEAR}.${MONTH}.${DAY}"
# Parse current version to check if it's today + same type
# Patterns: YYYY.M.D or YYYY.M.D-type.N
if [[ "$CURRENT" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-([a-z]+)\.([0-9]+))?$ ]]; then
@@ -88,7 +88,7 @@ jobs:
CURR_TYPE=""
CURR_NUM=0
fi
case "$RELEASE_TYPE" in
alpha)
if [ "$CURR_DATE" = "$TODAY" ] && [ "$CURR_TYPE" = "alpha" ]; then
@@ -117,7 +117,7 @@ jobs:
exit 1
;;
esac
echo "new=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "tag=v$NEW_VERSION" >> $GITHUB_OUTPUT
echo "New version: $NEW_VERSION"
@@ -126,7 +126,7 @@ jobs:
if: ${{ inputs.should_bump && !inputs.dry_run }}
run: |
NEW_VERSION="${{ steps.bump-version.outputs.new }}"
# Update package.json version
node -e "
const fs = require('fs');
@@ -134,7 +134,7 @@ jobs:
pkg.version = '$NEW_VERSION';
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
"
echo "Updated package.json to version $NEW_VERSION"
- name: Sync extension versions
@@ -152,13 +152,13 @@ jobs:
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
NEW_VERSION="${{ steps.bump-version.outputs.new }}"
# Stage all version-related changes
git add package.json
git add extensions/*/package.json 2>/dev/null || true
# Check if there are changes to commit
if git diff --cached --quiet; then
echo "No version changes to commit"
@@ -171,6 +171,6 @@ jobs:
if: ${{ inputs.should_bump && !inputs.dry_run }}
run: |
TAG="${{ steps.bump-version.outputs.tag }}"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"

View File

@@ -15,13 +15,13 @@ fix/* │ │ │ │
### Branch Purposes
| Branch | Purpose | npm tag | Who uses it |
|--------|---------|---------|-------------|
| `dev/*`, `feature/*`, `fix/*` | Active development | - | Contributors |
| `develop` | Integration branch | - | CI validation |
| `alpha` | Early testing | `@alpha` | Internal testers |
| `beta` | Pre-release testing | `@beta` | Beta testers |
| `main` | Production releases | `@latest` | Everyone |
| Branch | Purpose | npm tag | Who uses it |
| ----------------------------- | ------------------- | --------- | ---------------- |
| `dev/*`, `feature/*`, `fix/*` | Active development | - | Contributors |
| `develop` | Integration branch | - | CI validation |
| `alpha` | Early testing | `@alpha` | Internal testers |
| `beta` | Pre-release testing | `@beta` | Beta testers |
| `main` | Production releases | `@latest` | Everyone |
## Workflow Overview
@@ -51,12 +51,12 @@ Releases are triggered manually via the **Release** workflow:
## Test Coverage by Stage
| Stage | Tests Run |
|-------|-----------|
| Stage | Tests Run |
| ------- | ----------------------------------------------------- |
| develop | tsgo, lint, format, protocol, unit tests (Node + Bun) |
| alpha | + secrets scan |
| beta | + Windows tests |
| stable | + macOS tests, install smoke tests |
| alpha | + secrets scan |
| beta | + Windows tests |
| stable | + macOS tests, install smoke tests |
## Emergency Hotfixes
@@ -107,38 +107,3 @@ docker pull ghcr.io/openclaw/openclaw:2026.2.6
- **Stable**: `YYYY.M.D` (e.g., `2026.2.6`)
- **Beta**: `YYYY.M.D-beta.N` (e.g., `2026.2.6-beta.1`)
- **Alpha**: `YYYY.M.D-alpha.N` (e.g., `2026.2.6-alpha.3`)
## Maintainer Setup
After merging this pipeline to `main`, create the staging branches:
```bash
git checkout main && git pull
git branch develop && git push origin develop
git branch alpha && git push origin alpha
git branch beta && git push origin beta
```
### Recommended Branch Protection (GitHub Settings)
| Branch | Required reviews | Status checks | Force push |
|--------|-----------------|---------------|------------|
| `main` | 1 | All CI | ❌ |
| `beta` | 1 | All CI | ❌ |
| `alpha` | 0 | Core CI | ❌ |
| `develop` | 0 | Core CI | ❌ |
## Workflow Files
| Workflow | Purpose |
|----------|---------|
| `ci.yml` | Core CI (lint, test, build) |
| `feature-pr.yml` | Auto-PR from dev/* to develop |
| `hotfix-pr.yml` | Auto-PR from hotfix/* to main |
| `promote-branch.yml` | Stage promotion PRs |
| `testing-strategy.yml` | Stage-specific test suites |
| `deployment-strategy.yml` | npm + Docker publishing |
| `release-orchestrator.yml` | Full release coordination |
| `release.yml` | Manual release trigger |
| `version-operations.yml` | Version bumping |
| `generate-changelog.yml` | Changelog generation |