Release Packages #2
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: Release Packages | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: "Dry run (no actual release)" | |
| required: true | |
| default: false | |
| type: boolean | |
| # Ensure only one release can run at a time | |
| concurrency: | |
| group: release | |
| cancel-in-progress: false | |
| # Minimal permissions for security | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| quality: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| - name: Enable Corepack | |
| run: corepack enable | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Run format check | |
| run: pnpm format | |
| release: | |
| name: Release | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| needs: quality | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Enable Corepack | |
| run: corepack enable | |
| - name: Get pnpm store directory | |
| id: pnpm-cache | |
| shell: bash | |
| run: | | |
| echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT | |
| - name: Setup pnpm cache | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm-store- | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Install Firebase CLI | |
| uses: nick-invision/retry@v3 | |
| with: | |
| timeout_minutes: 10 | |
| retry_wait_seconds: 60 | |
| max_attempts: 3 | |
| command: npm i -g firebase-tools@14 | |
| - name: Run tests with emulator | |
| run: pnpm test:emulator | |
| - name: Build packages | |
| run: pnpm turbo build | |
| - name: Verify build outputs | |
| run: | | |
| echo "Checking build outputs..." | |
| # Check all packages for dist directories | |
| MISSING_BUILDS="" | |
| for PKG_DIR in packages/*; do | |
| if [ -d "$PKG_DIR" ] && [ -f "$PKG_DIR/package.json" ]; then | |
| PKG_NAME=$(basename "$PKG_DIR") | |
| if [ ! -d "$PKG_DIR/dist" ]; then | |
| MISSING_BUILDS="$MISSING_BUILDS $PKG_NAME" | |
| fi | |
| fi | |
| done | |
| if [ -n "$MISSING_BUILDS" ]; then | |
| echo "❌ Build outputs missing for: $MISSING_BUILDS" | |
| exit 1 | |
| fi | |
| echo "✅ All build outputs verified" | |
| - name: Validate changesets | |
| run: | | |
| set -e | |
| CHANGESET_FILES=$(find .changeset -name "*.md" -type f ! -name "README.md" 2>/dev/null || true) | |
| if [ -z "$CHANGESET_FILES" ]; then | |
| echo "❌ No changesets found!" | |
| echo "" | |
| echo "Please create changesets locally with: pnpm changeset" | |
| echo "Changesets should be created during development, not during release." | |
| exit 1 | |
| fi | |
| CHANGESET_COUNT=$(echo "$CHANGESET_FILES" | wc -l | tr -d ' ') | |
| echo "✅ Found $CHANGESET_COUNT changeset(s):" | |
| echo "$CHANGESET_FILES" | while read -r file; do | |
| echo " - $(basename "$file")" | |
| done | |
| - name: Create Release Pull Request or Publish | |
| if: ${{ !inputs.dry_run }} | |
| id: changesets | |
| uses: changesets/action@v1 | |
| with: | |
| publish: pnpm release | |
| commit: "chore: version packages" | |
| title: "chore: version packages" | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Dry Run - Show Changes | |
| if: ${{ inputs.dry_run }} | |
| run: | | |
| echo "🔍 This is a dry run. The following changes would be made:" | |
| echo "" | |
| echo "📋 Changeset contents:" | |
| for file in .changeset/*.md; do | |
| if [ -f "$file" ] && [ "$(basename "$file")" != "README.md" ]; then | |
| echo "--- $(basename "$file") ---" | |
| cat "$file" | |
| echo "" | |
| fi | |
| done | |
| echo "📦 Version changes that would be applied:" | |
| # Configure git user for changeset version command | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # Save current HEAD reference before making changes | |
| ORIGINAL_HEAD=$(git rev-parse HEAD) | |
| # Create a temporary branch for dry run with unique name | |
| FALLBACK_ID=${GITHUB_RUN_ID:-$RANDOM$RANDOM} | |
| TEMP_BRANCH="dry-run-temp-$FALLBACK_ID" | |
| git checkout -b "$TEMP_BRANCH" | |
| pnpm changeset version | |
| echo "" | |
| echo "🔍 Changed files:" | |
| git diff --name-status "$ORIGINAL_HEAD" | |
| echo "" | |
| echo "🔍 Package version changes:" | |
| VERSION_CHANGES=$(git diff "$ORIGINAL_HEAD" -- '**/package.json' | grep -E "^[+-]\s*\"version\"" || true) | |
| if [ -z "$VERSION_CHANGES" ]; then | |
| echo " No version changes detected (this might indicate an issue with changesets)" | |
| else | |
| echo "$VERSION_CHANGES" | |
| fi | |
| # Clean up | |
| git checkout - | |
| git branch -D "$TEMP_BRANCH" | |
| echo "" | |
| echo "✅ Dry run completed successfully" |