Release #18
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 | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: 'Dry run only (no git push, no tags, no npm publish)' | |
| required: false | |
| default: true | |
| type: boolean | |
| permissions: | |
| contents: write | |
| id-token: write | |
| issues: write | |
| pull-requests: write | |
| # Allow GitHub Actions to bypass branch protection | |
| # This is required for semantic-release to push version updates | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Verify admin permissions | |
| run: | | |
| # Use the repository's permission endpoint which works for both personal and org repos | |
| RESPONSE=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ | |
| -H "Accept: application/vnd.github.v3+json" \ | |
| "https://api.github.com/repos/${{ github.repository }}/collaborators/${{ github.actor }}/permission") | |
| # Extract permission using jq if available, otherwise use grep | |
| if command -v jq &> /dev/null; then | |
| PERMISSION=$(echo "$RESPONSE" | jq -r '.permission // empty') | |
| else | |
| PERMISSION=$(echo "$RESPONSE" | grep -o '"permission":"[^"]*"' | head -1 | cut -d'"' -f4) | |
| fi | |
| if [ -z "$PERMISSION" ]; then | |
| echo "Warning: Could not determine permission level. Response: $RESPONSE" | |
| echo "Note: workflow_dispatch requires write access, proceeding..." | |
| exit 0 | |
| fi | |
| if [ "$PERMISSION" != "admin" ]; then | |
| echo "Error: Only repository admins can trigger releases. Current permission: $PERMISSION" | |
| exit 1 | |
| fi | |
| echo "✓ Verified admin permission for ${{ github.actor }}" | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| fetch-depth: 0 | |
| token: ${{ secrets.RELEASE_TOKEN }} | |
| - name: Setup git branch | |
| run: | | |
| git fetch --all --tags --force | |
| git checkout -B main | |
| git branch --set-upstream-to=origin/main main | |
| # Show all tags for debugging | |
| echo "=== All git tags ===" | |
| git tag -l | |
| echo "" | |
| # Show current package.json version | |
| echo "=== Current package.json version ===" | |
| node -p "require('./package.json').version" | |
| - name: Debug branch info | |
| run: | | |
| echo "Current branch: $(git branch --show-current)" | |
| echo "All branches: $(git branch -a)" | |
| echo "Git remote: $(git remote -v)" | |
| echo "Git status: $(git status)" | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| registry-url: 'https://registry.npmjs.org' | |
| always-auth: true | |
| - run: npm ci | |
| - run: npm test --if-present | |
| - name: Configure git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Create initial tag if needed | |
| if: github.event.inputs.dry_run != 'true' | |
| run: | | |
| if ! git rev-parse --verify "v1.0.0-beta.1" >/dev/null 2>&1; then | |
| echo "Creating initial tag v1.0.0-beta.1" | |
| git tag -a "v1.0.0-beta.1" -m "chore: initial beta release" | |
| git push origin "v1.0.0-beta.1" || echo "Tag push failed (may not have permission or tag exists)" | |
| else | |
| echo "Tag v1.0.0-beta.1 already exists" | |
| fi | |
| - name: Dry run mode notice | |
| if: github.event.inputs.dry_run == 'true' | |
| run: | | |
| echo "==============================================" | |
| echo " DRY RUN MODE - No commits, tags, or publish" | |
| echo "==============================================" | |
| echo "Semantic-release will show what WOULD happen." | |
| echo "To perform a real release, run again and uncheck 'Dry run only'." | |
| echo "" | |
| - name: Debug semantic-release config | |
| run: | | |
| echo "=== .releaserc.json ===" | |
| cat .releaserc.json | |
| echo "" | |
| echo "=== Git branches ===" | |
| git branch -a | |
| echo "" | |
| echo "=== Current branch ===" | |
| git branch --show-current | |
| echo "" | |
| echo "=== Git tags ===" | |
| git tag | |
| - name: Get version before semantic-release | |
| id: version-before | |
| run: | | |
| VERSION_BEFORE=$(node -p "require('./package.json').version") | |
| echo "version=$VERSION_BEFORE" >> $GITHUB_OUTPUT | |
| echo "Current version: $VERSION_BEFORE" | |
| - name: Release with semantic-release | |
| id: release | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} | |
| run: | | |
| if [ "${{ github.event.inputs.dry_run }}" = "true" ]; then | |
| echo "Running semantic-release in DRY RUN mode..." | |
| npx semantic-release --dry-run | |
| else | |
| echo "Running semantic-release (REAL release)..." | |
| npx semantic-release | |
| fi | |
| - name: Publish to npm using trusted publishing | |
| if: github.event.inputs.dry_run != 'true' | |
| run: | | |
| echo "=== Publishing to npm with trusted publishing (OIDC) ===" | |
| # Ensure .npmrc is available (setup-node should have created it) | |
| if [ -f "$NPM_CONFIG_USERCONFIG" ]; then | |
| cp "$NPM_CONFIG_USERCONFIG" ~/.npmrc | |
| echo "✓ Using .npmrc for authentication" | |
| fi | |
| # Get versions | |
| VERSION_BEFORE="${{ steps.version-before.outputs.version }}" | |
| VERSION_AFTER=$(node -p "require('./package.json').version") | |
| echo "Version before: $VERSION_BEFORE" | |
| echo "Version after: $VERSION_AFTER" | |
| # Only publish if semantic-release created a new version | |
| if [ "$VERSION_BEFORE" != "$VERSION_AFTER" ]; then | |
| echo "✓ New version detected: $VERSION_AFTER" | |
| echo "Publishing to npm..." | |
| # Publish using npm publish which supports OIDC/trusted publishing | |
| npm publish --provenance --access public | |
| echo "✓ Published $VERSION_AFTER to npm" | |
| else | |
| echo "No version change detected (version: $VERSION_AFTER)" | |
| echo "Skipping npm publish - no new release was created" | |
| fi | |
| - name: Dry run - skip npm publish | |
| if: github.event.inputs.dry_run == 'true' | |
| run: | | |
| echo "==============================================" | |
| echo " DRY RUN - Skipping npm publish" | |
| echo "==============================================" | |
| echo "Version that WOULD have been published: $(node -p "require('./package.json').version")" | |
| echo "To publish for real, run the workflow again with 'Dry run only' unchecked." |