Merge pull request #6201 from elizaOS/feat/unified-api-elizaos #347
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: NPM Release | |
| # Unified workflow for all NPM releases: | |
| # - Alpha: On merge to develop branch | |
| # - Beta: On merge to main branch | |
| # - Production: On GitHub release creation | |
| # | |
| # Version Management: | |
| # - Uses lerna version and publish commands with consistent patterns | |
| # - Commits version changes to git FIRST, then publishes to NPM | |
| # - Prevents infinite loops with [skip ci] in commit messages | |
| # - Proper error handling without masking critical failures | |
| on: | |
| push: | |
| branches: | |
| - develop # Triggers alpha releases | |
| - main # Triggers beta releases | |
| paths-ignore: | |
| - '**/*.md' | |
| - 'docs/**' | |
| - '.github/**/*.md' | |
| - 'LICENSE' | |
| - '.gitignore' | |
| - '.dockerignore' | |
| - '**/*.example' | |
| - '.vscode/**' | |
| - '.devcontainer/**' | |
| release: | |
| types: [created] # Triggers production releases | |
| workflow_dispatch: | |
| inputs: | |
| release_type: | |
| description: 'Manual release type (only for prerelease testing)' | |
| required: true | |
| type: choice | |
| options: | |
| - alpha | |
| - beta | |
| # Note: 'latest' removed - production releases MUST be done via GitHub releases | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| # Skip if commit message contains [skip ci] | |
| if: ${{ !contains(github.event.head_commit.message || '', '[skip ci]') }} | |
| permissions: | |
| contents: write | |
| packages: write | |
| id-token: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # If triggered by a release, we're in detached HEAD state | |
| # Create a temporary branch that matches Lerna's allowBranch pattern | |
| if [[ "${{ github.event_name }}" == "release" ]]; then | |
| echo "Creating temporary release branch from tag..." | |
| git checkout -b release/production-${{ github.sha }} | |
| fi | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '23.3.0' | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: '1.2.21' | |
| - name: Install dependencies | |
| run: bun install | |
| # Determine release type and version | |
| - name: Determine release type | |
| id: release_type | |
| run: | | |
| if [[ "${{ github.event_name }}" == "release" ]]; then | |
| echo "type=latest" >> $GITHUB_OUTPUT | |
| echo "dist_tag=latest" >> $GITHUB_OUTPUT | |
| # Extract version from tag (remove 'v' prefix if present) | |
| VERSION="${{ github.event.release.tag_name }}" | |
| VERSION="${VERSION#v}" | |
| echo "version=${VERSION}" >> $GITHUB_OUTPUT | |
| echo "is_release_event=true" >> $GITHUB_OUTPUT | |
| elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| echo "type=${{ github.event.inputs.release_type }}" >> $GITHUB_OUTPUT | |
| echo "dist_tag=${{ github.event.inputs.release_type }}" >> $GITHUB_OUTPUT | |
| echo "is_release_event=false" >> $GITHUB_OUTPUT | |
| elif [[ "${{ github.ref }}" == "refs/heads/develop" ]]; then | |
| echo "type=alpha" >> $GITHUB_OUTPUT | |
| echo "dist_tag=alpha" >> $GITHUB_OUTPUT | |
| echo "is_release_event=false" >> $GITHUB_OUTPUT | |
| elif [[ "${{ github.ref }}" == "refs/heads/main" ]]; then | |
| echo "type=beta" >> $GITHUB_OUTPUT | |
| echo "dist_tag=beta" >> $GITHUB_OUTPUT | |
| echo "is_release_event=false" >> $GITHUB_OUTPUT | |
| fi | |
| # Version Management | |
| - name: Version packages | |
| id: version | |
| run: | | |
| RELEASE_TYPE="${{ steps.release_type.outputs.type }}" | |
| CURRENT_VERSION=$(node -p "require('./lerna.json').version") | |
| echo "Current version: ${CURRENT_VERSION}" | |
| # Helper functions for version manipulation | |
| get_base_version() { | |
| echo "$1" | sed 's/-.*$//' | |
| } | |
| get_prerelease_type() { | |
| if [[ "$1" =~ -([a-z]+)\. ]]; then | |
| echo "${BASH_REMATCH[1]}" | |
| else | |
| echo "" | |
| fi | |
| } | |
| bump_version() { | |
| local version="$1" | |
| local type="$2" # major, minor, patch | |
| IFS='.' read -r major minor patch <<< "$version" | |
| patch=${patch%%-*} # Remove any prerelease suffix | |
| case "$type" in | |
| major) | |
| echo "$((major + 1)).0.0" | |
| ;; | |
| minor) | |
| echo "${major}.$((minor + 1)).0" | |
| ;; | |
| patch) | |
| echo "${major}.${minor}.$((patch + 1))" | |
| ;; | |
| *) | |
| echo "$version" | |
| ;; | |
| esac | |
| } | |
| # Determine version strategy based on release type and current version | |
| if [[ "${{ github.event_name }}" == "release" ]]; then | |
| # Production release from GitHub release tag | |
| VERSION="${{ steps.release_type.outputs.version }}" | |
| echo "📦 Production release: Setting exact version to ${VERSION}" | |
| bunx lerna version ${VERSION} \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push \ | |
| --allow-branch release/* | |
| elif [[ "${RELEASE_TYPE}" == "alpha" ]]; then | |
| echo "🚀 Alpha release workflow..." | |
| PRERELEASE_TYPE=$(get_prerelease_type "$CURRENT_VERSION") | |
| BASE_VERSION=$(get_base_version "$CURRENT_VERSION") | |
| if [[ "$PRERELEASE_TYPE" == "alpha" ]]; then | |
| # Already on alpha, just increment the alpha counter | |
| echo "Incrementing alpha version from ${CURRENT_VERSION}..." | |
| bunx lerna version prerelease \ | |
| --preid alpha \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push | |
| else | |
| # Not on alpha (could be stable, beta, etc) | |
| # Check if there are existing alpha tags for this base version | |
| echo "Checking for existing alpha tags for base version ${BASE_VERSION}..." | |
| # Fetch all tags to ensure we have the complete history | |
| git fetch --tags | |
| # Find the highest existing alpha version for this base | |
| EXISTING_ALPHAS=$(git tag -l "v${BASE_VERSION}-alpha.*" 2>/dev/null | sort -V | tail -n 1) | |
| if [[ -n "$EXISTING_ALPHAS" ]]; then | |
| # Found existing alpha tags, continue from there | |
| LAST_ALPHA_VERSION=${EXISTING_ALPHAS#v} | |
| echo "Found existing alpha version: ${LAST_ALPHA_VERSION}" | |
| # Set to the last version, then increment | |
| bunx lerna version ${LAST_ALPHA_VERSION} \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push | |
| # Now increment to get the next alpha | |
| echo "Incrementing from ${LAST_ALPHA_VERSION}..." | |
| bunx lerna version prerelease \ | |
| --preid alpha \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push | |
| else | |
| # No existing alpha tags, safe to start at .0 | |
| echo "No existing alpha tags found, starting at ${BASE_VERSION}-alpha.0" | |
| bunx lerna version "${BASE_VERSION}-alpha.0" \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push | |
| fi | |
| fi | |
| elif [[ "${RELEASE_TYPE}" == "beta" ]]; then | |
| echo "🔵 Beta release workflow..." | |
| PRERELEASE_TYPE=$(get_prerelease_type "$CURRENT_VERSION") | |
| BASE_VERSION=$(get_base_version "$CURRENT_VERSION") | |
| if [[ "$PRERELEASE_TYPE" == "beta" ]]; then | |
| # Already on beta, just increment the beta counter | |
| echo "Incrementing beta version from ${CURRENT_VERSION}..." | |
| bunx lerna version prerelease \ | |
| --preid beta \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push | |
| else | |
| # Not on beta (could be stable, alpha, etc) | |
| # Check if there are existing beta tags for this base version | |
| echo "Checking for existing beta tags for base version ${BASE_VERSION}..." | |
| # Fetch all tags to ensure we have the complete history | |
| git fetch --tags | |
| # Find the highest existing beta version for this base | |
| EXISTING_BETAS=$(git tag -l "v${BASE_VERSION}-beta.*" 2>/dev/null | sort -V | tail -n 1) | |
| if [[ -n "$EXISTING_BETAS" ]]; then | |
| # Found existing beta tags, continue from there | |
| LAST_BETA_VERSION=${EXISTING_BETAS#v} | |
| echo "Found existing beta version: ${LAST_BETA_VERSION}" | |
| # Set to the last version, then increment | |
| bunx lerna version ${LAST_BETA_VERSION} \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push | |
| # Now increment to get the next beta | |
| echo "Incrementing from ${LAST_BETA_VERSION}..." | |
| bunx lerna version prerelease \ | |
| --preid beta \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push | |
| else | |
| # No existing beta tags, safe to start at .0 | |
| echo "No existing beta tags found, starting at ${BASE_VERSION}-beta.0" | |
| bunx lerna version "${BASE_VERSION}-beta.0" \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push | |
| fi | |
| fi | |
| elif [[ "${RELEASE_TYPE}" == "latest" ]]; then | |
| # Manual workflow dispatch for 'latest' should NOT be used for version bumps! | |
| # Version bumps should ONLY come from GitHub releases with tags | |
| echo "❌ ERROR: Manual 'latest' releases are not allowed!" | |
| echo "Production version changes must be done through GitHub releases." | |
| echo "Please create a GitHub release with the desired version tag instead." | |
| exit 1 | |
| fi | |
| # Get the new version | |
| VERSION=$(node -p "require('./lerna.json').version") | |
| echo "version=${VERSION}" >> $GITHUB_OUTPUT | |
| # Update lockfile after version changes | |
| - name: Update lockfile | |
| run: | | |
| bun install --no-frozen-lockfile | |
| # Commit and push version changes BEFORE building and publishing | |
| # This ensures git is the source of truth | |
| - name: Commit version changes | |
| id: commit | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| RELEASE_TYPE="${{ steps.release_type.outputs.type }}" | |
| # Stage all changes | |
| git add -A | |
| # Check if there are changes to commit | |
| if git diff --staged --quiet; then | |
| echo "No changes to commit - this might indicate a problem" | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Commit with [skip ci] to prevent infinite loop | |
| git commit -m "chore: release v${VERSION} (${RELEASE_TYPE}) [skip ci]" | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| echo "commit_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT | |
| # Create and push git tag (only if not from a GitHub release) | |
| - name: Create git tag | |
| if: steps.release_type.outputs.is_release_event != 'true' && steps.commit.outputs.has_changes == 'true' | |
| id: tag | |
| run: | | |
| VERSION="${{ steps.version.outputs.version }}" | |
| TAG_NAME="v${VERSION}" | |
| # Check if tag already exists | |
| if git rev-parse "${TAG_NAME}" >/dev/null 2>&1; then | |
| echo "❌ Error: Tag ${TAG_NAME} already exists" | |
| echo "This indicates a version conflict that needs manual resolution" | |
| exit 1 | |
| fi | |
| # Create the tag | |
| git tag "${TAG_NAME}" | |
| echo "tag_created=true" >> $GITHUB_OUTPUT | |
| echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT | |
| # Push changes to git (fails the workflow if it can't push) | |
| - name: Push to git | |
| if: steps.commit.outputs.has_changes == 'true' | |
| run: | | |
| # Determine target branch for push | |
| if [[ "${{ github.event_name }}" == "release" ]]; then | |
| # For GitHub releases, push to main branch | |
| TARGET_BRANCH="main" | |
| echo "Pushing changes to main branch..." | |
| # Push the changes to main branch | |
| if ! git push origin HEAD:${TARGET_BRANCH} --follow-tags; then | |
| echo "❌ Error: Failed to push to git repository" | |
| echo "This could be due to:" | |
| echo " - Protected branch restrictions" | |
| echo " - Network issues" | |
| echo " - Permission problems" | |
| echo "" | |
| echo "The version has been updated locally but not published." | |
| echo "Manual intervention required to resolve the git push issue." | |
| exit 1 | |
| fi | |
| else | |
| # For other triggers, push to current branch | |
| if ! git push origin HEAD --follow-tags; then | |
| echo "❌ Error: Failed to push to git repository" | |
| echo "This could be due to:" | |
| echo " - Protected branch restrictions" | |
| echo " - Network issues" | |
| echo " - Permission problems" | |
| echo "" | |
| echo "The version has been updated locally but not published." | |
| echo "Manual intervention required to resolve the git push issue." | |
| exit 1 | |
| fi | |
| fi | |
| echo "✅ Successfully pushed version changes and tags to git" | |
| # Build packages with correct version numbers | |
| # Only happens AFTER git operations succeed | |
| - name: Build packages | |
| run: | | |
| echo "Building packages with version v${{ steps.version.outputs.version }}..." | |
| bun run build | |
| # Publish to NPM (only after git operations succeed) | |
| - name: Publish to NPM | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| DIST_TAG="${{ steps.release_type.outputs.dist_tag }}" | |
| # Configure npm for authentication | |
| echo "//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}" > ~/.npmrc | |
| # Publish with appropriate dist-tag | |
| if ! bunx lerna publish from-package \ | |
| --dist-tag ${DIST_TAG} \ | |
| --force-publish \ | |
| --yes \ | |
| --no-verify-access; then | |
| echo "❌ Error: Failed to publish to NPM" | |
| echo "" | |
| echo "Git has been updated with version v${{ steps.version.outputs.version }}" | |
| echo "but the packages were not published to NPM." | |
| echo "" | |
| echo "To recover:" | |
| echo " 1. Fix the NPM publishing issue" | |
| echo " 2. Run 'npm run release:${DIST_TAG}' locally with proper credentials" | |
| echo " 3. Or re-run this workflow" | |
| exit 1 | |
| fi | |
| echo "✅ Successfully published to NPM with dist-tag: ${DIST_TAG}" | |
| # Create GitHub Release for alpha/beta (not for production, as it already exists) | |
| - name: Create GitHub release | |
| if: github.event_name != 'release' && steps.release_type.outputs.type != 'latest' && steps.tag.outputs.tag_created == 'true' | |
| uses: softprops/action-gh-release@v1 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| tag_name: ${{ steps.tag.outputs.tag_name }} | |
| name: ${{ steps.tag.outputs.tag_name }} | |
| body: | | |
| ${{ steps.release_type.outputs.type == 'alpha' && '🚀 Alpha Release' || '🔵 Beta Release' }} | |
| **Version:** `${{ steps.tag.outputs.tag_name }}` | |
| **Channel:** `${{ steps.release_type.outputs.dist_tag }}` | |
| ### Quick Start | |
| Install the CLI globally to get started: | |
| ```bash | |
| bun i -g @elizaos/cli@${{ steps.release_type.outputs.dist_tag }} | |
| ``` | |
| Or add packages to your project: | |
| ```bash | |
| bun add @elizaos/core@${{ steps.release_type.outputs.dist_tag }} | |
| bun add @elizaos/plugin-bootstrap@${{ steps.release_type.outputs.dist_tag }} | |
| ``` | |
| --- | |
| > **Note:** This is a ${{ steps.release_type.outputs.type }} release. Production releases use the `latest` tag and are triggered by GitHub releases on tags matching `v*.*.*`. | |
| draft: false | |
| prerelease: true | |
| - name: Summary | |
| if: always() | |
| run: | | |
| echo "# 📦 Release Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Version**: v${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Type**: ${{ steps.release_type.outputs.type }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Dist Tag**: ${{ steps.release_type.outputs.dist_tag }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Trigger**: ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Branch**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY | |
| if [[ "${{ steps.commit.outputs.has_changes }}" == "true" ]]; then | |
| echo "- **Commit SHA**: ${{ steps.commit.outputs.commit_sha }}" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [[ "${{ steps.tag.outputs.tag_created }}" == "true" ]]; then | |
| echo "- **Tag**: ${{ steps.tag.outputs.tag_name }}" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "## Quick Start" >> $GITHUB_STEP_SUMMARY | |
| echo "Install the CLI globally:" >> $GITHUB_STEP_SUMMARY | |
| echo '```bash' >> $GITHUB_STEP_SUMMARY | |
| echo "bun i -g @elizaos/cli@${{ steps.release_type.outputs.dist_tag }}" >> $GITHUB_STEP_SUMMARY | |
| echo '```' >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Or add to your project:" >> $GITHUB_STEP_SUMMARY | |
| echo '```bash' >> $GITHUB_STEP_SUMMARY | |
| echo "bun add @elizaos/core@${{ steps.release_type.outputs.dist_tag }}" >> $GITHUB_STEP_SUMMARY | |
| echo '```' >> $GITHUB_STEP_SUMMARY | |
| # Sync version back to develop after production release | |
| - name: Sync version to develop branch | |
| if: github.event_name == 'release' && success() | |
| continue-on-error: true # Don't fail the release if sync fails | |
| run: | | |
| echo "📤 Syncing production release back to develop branch..." | |
| # Get the released version | |
| RELEASED_VERSION="${{ steps.version.outputs.version }}" | |
| BASE_VERSION=$(echo "$RELEASED_VERSION" | sed 's/-.*$//') | |
| echo "Released version: ${RELEASED_VERSION}" | |
| echo "Base version: ${BASE_VERSION}" | |
| # Fetch latest develop | |
| git fetch origin develop:refs/remotes/origin/develop || { | |
| echo "⚠️ Could not fetch develop branch, skipping sync" | |
| exit 0 | |
| } | |
| # Get current develop version | |
| git checkout origin/develop -- lerna.json 2>/dev/null || true | |
| DEVELOP_VERSION=$(node -p "require('./lerna.json').version" 2>/dev/null || echo "unknown") | |
| # Restore our lerna.json | |
| git checkout HEAD -- lerna.json | |
| echo "Current develop version: ${DEVELOP_VERSION}" | |
| # Extract base versions for comparison | |
| DEVELOP_BASE=$(echo "$DEVELOP_VERSION" | sed 's/-.*$//') | |
| # Compare versions and auto-advance if needed | |
| if [[ "$DEVELOP_BASE" < "$BASE_VERSION" ]]; then | |
| # Develop is behind - update it to match production with alpha suffix | |
| NEXT_ALPHA="${BASE_VERSION}-alpha.0" | |
| echo "Develop is behind: ${DEVELOP_VERSION} → ${NEXT_ALPHA}" | |
| # Create a branch for the sync (using release/ prefix for lerna) | |
| git checkout -b release/sync-develop-${BASE_VERSION} origin/develop | |
| # Update version to match production base with alpha suffix | |
| bunx lerna version ${NEXT_ALPHA} \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push \ | |
| --allow-branch release/* | |
| # Update lockfile | |
| bun install --no-frozen-lockfile | |
| # Commit | |
| git add -A | |
| git commit -m "chore: sync to v${NEXT_ALPHA} after v${BASE_VERSION} release [skip ci]" \ | |
| -m "Automated version sync from production release" | |
| # Push to develop | |
| if git push origin HEAD:develop; then | |
| echo "✅ Successfully synced develop to ${NEXT_ALPHA}" | |
| else | |
| echo "⚠️ Could not push to develop (may be protected or already updated)" | |
| fi | |
| elif [[ "$DEVELOP_BASE" == "$BASE_VERSION" ]]; then | |
| # Develop is on same base as release - auto-advance to next patch | |
| echo "Develop matches release base, auto-advancing to next patch version..." | |
| # Calculate next patch version | |
| IFS='.' read -r major minor patch <<< "$BASE_VERSION" | |
| NEXT_PATCH="${major}.${minor}.$((patch + 1))" | |
| NEXT_ALPHA="${NEXT_PATCH}-alpha.0" | |
| echo "Auto-advancing: ${DEVELOP_VERSION} → ${NEXT_ALPHA}" | |
| # Create a branch for the sync (using release/ prefix for lerna) | |
| git checkout -b release/sync-develop-${BASE_VERSION} origin/develop | |
| # Update version to next patch with alpha suffix | |
| bunx lerna version ${NEXT_ALPHA} \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push \ | |
| --allow-branch release/* | |
| # Update lockfile | |
| bun install --no-frozen-lockfile | |
| # Commit | |
| git add -A | |
| git commit -m "chore: bump to v${NEXT_ALPHA} after v${BASE_VERSION} release [skip ci]" \ | |
| -m "Automated patch version bump from production release" | |
| # Push to develop | |
| if git push origin HEAD:develop; then | |
| echo "✅ Successfully auto-advanced develop to ${NEXT_ALPHA}" | |
| else | |
| echo "⚠️ Could not push to develop (may be protected or already updated)" | |
| fi | |
| else | |
| echo "✅ Develop (${DEVELOP_VERSION}) is already ahead of release (${RELEASED_VERSION})" | |
| # Develop is ahead - this is fine, means a new version is being worked on | |
| # Don't touch it - developer has manually set the next version | |
| fi | |
| # Also sync main branch to match production release | |
| echo "🔄 Syncing main branch to production release..." | |
| # Fetch latest main | |
| git fetch origin main:refs/remotes/origin/main || { | |
| echo "⚠️ Could not fetch main branch, skipping main sync" | |
| exit 0 | |
| } | |
| # Get current main version | |
| git checkout origin/main -- lerna.json 2>/dev/null || true | |
| MAIN_VERSION=$(node -p "require('./lerna.json').version" 2>/dev/null || echo "unknown") | |
| # Restore our lerna.json | |
| git checkout HEAD -- lerna.json | |
| echo "Current main version: ${MAIN_VERSION}" | |
| MAIN_BASE=$(echo "$MAIN_VERSION" | sed 's/-.*$//') | |
| # Main should follow develop's base version | |
| # First, get the new develop version that was just set | |
| git fetch origin develop:refs/remotes/origin/develop || { | |
| echo "⚠️ Could not fetch updated develop branch" | |
| NEW_DEVELOP_BASE="$BASE_VERSION" | |
| } | |
| if [[ -z "${NEW_DEVELOP_BASE}" ]]; then | |
| git checkout origin/develop -- lerna.json 2>/dev/null || true | |
| NEW_DEVELOP_VERSION=$(node -p "require('./lerna.json').version" 2>/dev/null || echo "${BASE_VERSION}-alpha.0") | |
| NEW_DEVELOP_BASE=$(echo "$NEW_DEVELOP_VERSION" | sed 's/-.*$//') | |
| git checkout HEAD -- lerna.json | |
| fi | |
| echo "New develop base: ${NEW_DEVELOP_BASE}" | |
| echo "Current main base: ${MAIN_BASE}" | |
| # Main should match develop's base version | |
| if [[ "$MAIN_BASE" != "$NEW_DEVELOP_BASE" ]]; then | |
| NEXT_BETA="${NEW_DEVELOP_BASE}-beta.0" | |
| echo "Updating main to match develop base: ${MAIN_VERSION} → ${NEXT_BETA}" | |
| # Create a branch for the sync (using release/ prefix for lerna) | |
| git checkout -b release/sync-main-${BASE_VERSION} origin/main | |
| # Update version | |
| bunx lerna version ${NEXT_BETA} \ | |
| --force-publish \ | |
| --yes \ | |
| --no-private \ | |
| --no-git-tag-version \ | |
| --no-push \ | |
| --allow-branch release/* | |
| # Update lockfile | |
| bun install --no-frozen-lockfile | |
| # Commit | |
| git add -A | |
| git commit -m "chore: sync to v${NEXT_BETA} after v${BASE_VERSION} release [skip ci]" \ | |
| -m "Automated version sync from production release (following develop)" | |
| # Push to main | |
| if git push origin HEAD:main; then | |
| echo "✅ Successfully synced main to ${NEXT_BETA}" | |
| else | |
| echo "⚠️ Could not push to main (may be protected or already updated)" | |
| fi | |
| else | |
| echo "✅ Main base version (${MAIN_BASE}) already matches develop base (${NEW_DEVELOP_BASE})" | |
| fi | |
| # Handle failure - create issue if the workflow failed | |
| - name: Create issue content file | |
| if: failure() && steps.version.outputs.version | |
| run: | | |
| cat > /tmp/issue-content.md << 'EOF' | |
| The release workflow failed for version v${{ steps.version.outputs.version }}. | |
| **Details:** | |
| - Release Type: ${{ steps.release_type.outputs.type }} | |
| - Workflow Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| - Triggered By: ${{ github.event_name }} | |
| - Git Changes Committed: ${{ steps.commit.outputs.has_changes || 'false' }} | |
| - Git Tag Created: ${{ steps.tag.outputs.tag_created || 'false' }} | |
| **Recovery Steps:** | |
| If git operations succeeded but NPM publish failed: | |
| - The version is already in git | |
| - Fix the NPM issue and run `npm run release:${{ steps.release_type.outputs.dist_tag }}` locally | |
| - Or re-run this workflow (it will skip git operations if no changes) | |
| If git operations failed: | |
| - No packages were published to NPM (safe state) | |
| - Fix the git issue (permissions, network, etc.) | |
| - Re-run the workflow | |
| **Action Required:** | |
| - Check the workflow logs for the specific failure point | |
| - Follow the appropriate recovery steps above | |
| EOF | |
| - name: Create failure issue | |
| if: failure() && steps.version.outputs.version | |
| uses: peter-evans/create-issue-from-file@v5 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| title: 'Release Failed: v${{ steps.version.outputs.version }}' | |
| content-filepath: /tmp/issue-content.md | |
| labels: | | |
| bug | |
| release | |
| automated issue |