Create Draft Release #16
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: Create Draft Release | |
| on: | |
| workflow_dispatch: | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Generate GitHub App Token | |
| id: app-token | |
| uses: actions/create-github-app-token@v1 | |
| with: | |
| app-id: ${{ secrets.APP_ID }} | |
| private-key: ${{ secrets.APP_PRIVATE_KEY }} | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| token: ${{ steps.app-token.outputs.token }} | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ".nvmrc" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Configure Git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Determine version bump from merged PRs | |
| id: version | |
| run: | | |
| # Get the latest tag, default to v0.0.0 if no tags exist | |
| LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | |
| if [ -z "$LATEST_TAG" ]; then | |
| echo "No tags found, starting from version 0.0.0" | |
| LATEST_TAG="v0.0.0" | |
| # Get all merge commits in the repository | |
| MERGE_COMMITS=$(git log --merges --pretty=format:"%H %s") | |
| else | |
| echo "Latest tag: $LATEST_TAG" | |
| # Get all merged PRs since the last tag | |
| MERGE_COMMITS=$(git log ${LATEST_TAG}..HEAD --merges --pretty=format:"%H %s") | |
| fi | |
| # Check if there are any merge commits to process | |
| if [ -z "$MERGE_COMMITS" ]; then | |
| echo "⚠️ Warning: No merge commits found since $LATEST_TAG" | |
| echo "No PRs have been merged since the last release." | |
| echo "Defaulting to PATCH bump - please verify this is intentional." | |
| echo "" | |
| fi | |
| # Initialize semver bump level (0=none, 1=patch, 2=minor, 3=major) | |
| MAX_BUMP=1 # Default to patch if no explicit selection found | |
| # Parse each merge commit to find semver selections | |
| while IFS= read -r line; do | |
| if [ -z "$line" ]; then | |
| continue | |
| fi | |
| COMMIT_HASH=$(echo "$line" | awk '{print $1}') | |
| # Get the PR body from the merge commit | |
| PR_BODY=$(git log -1 --pretty=format:"%b" $COMMIT_HASH) | |
| # Check for semver selections in PR body (look for checked boxes) | |
| if echo "$PR_BODY" | grep -q "\[x\] \*\*MAJOR\*\*"; then | |
| echo "Found MAJOR bump in commit $COMMIT_HASH" | |
| MAX_BUMP=3 | |
| elif echo "$PR_BODY" | grep -q "\[x\] \*\*MINOR\*\*"; then | |
| echo "Found MINOR bump in commit $COMMIT_HASH" | |
| if [ $MAX_BUMP -lt 2 ]; then | |
| MAX_BUMP=2 | |
| fi | |
| fi | |
| done <<< "$MERGE_COMMITS" | |
| # Determine bump type | |
| case $MAX_BUMP in | |
| 3) | |
| BUMP_TYPE="major" | |
| ;; | |
| 2) | |
| BUMP_TYPE="minor" | |
| ;; | |
| 1) | |
| BUMP_TYPE="patch" | |
| ;; | |
| esac | |
| echo "Bump type: $BUMP_TYPE" | |
| # Export for later steps | |
| echo "bump_type=$BUMP_TYPE" >> $GITHUB_OUTPUT | |
| - name: Update version in package.json | |
| id: npm_version | |
| run: | | |
| # Use npm version to automatically bump the version | |
| npm version ${{ steps.version.outputs.bump_type }} --no-git-tag-version | |
| # Read the new version | |
| NEW_VERSION=$(node -p "require('./package.json').version") | |
| echo "New version: $NEW_VERSION" | |
| # Export for later steps | |
| echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT | |
| - name: Update CHANGELOG.md | |
| run: | | |
| VERSION="${{ steps.npm_version.outputs.version }}" | |
| DATE=$(date +%Y-%m-%d) | |
| # Create a temporary file | |
| TEMP_FILE=$(mktemp) | |
| # Read the changelog and update it | |
| # This awk script handles edge cases: | |
| # - Empty [Unreleased] sections | |
| # - [Unreleased] as the last section | |
| # - Multiple consecutive ## headers | |
| awk -v version="$VERSION" -v date="$DATE" ' | |
| BEGIN { found_unreleased = 0 } | |
| /## \[Unreleased\]/ { | |
| found_unreleased = 1 | |
| print "## [Unreleased]" | |
| print "" | |
| print "## [" version "] - " date | |
| next | |
| } | |
| # Skip empty lines immediately after [Unreleased] until we hit content or another ## | |
| found_unreleased && /^$/ && !seen_content { | |
| next | |
| } | |
| found_unreleased && /^## / { | |
| seen_content = 1 | |
| } | |
| found_unreleased && /./ { | |
| seen_content = 1 | |
| } | |
| { print } | |
| ' docs/CHANGELOG.md > "$TEMP_FILE" | |
| # Replace the original file | |
| mv "$TEMP_FILE" docs/CHANGELOG.md | |
| echo "✓ CHANGELOG.md updated: Added version $VERSION section" | |
| - name: Commit changelog and version bump | |
| run: | | |
| # Verify we're on the main branch | |
| CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) | |
| if [ "$CURRENT_BRANCH" != "main" ]; then | |
| echo "❌ Error: Release workflow must be run from main branch" | |
| echo "Current branch: $CURRENT_BRANCH" | |
| exit 1 | |
| fi | |
| git add docs/CHANGELOG.md package.json | |
| git add package-lock.json 2>/dev/null || true | |
| git commit -m "Release version ${{ steps.npm_version.outputs.version }}" | |
| git push origin main | |
| - name: Create and push tag | |
| run: | | |
| # Create the tag pointing to the commit with updated files | |
| git tag -a "v${{ steps.npm_version.outputs.version }}" -m "Release version ${{ steps.npm_version.outputs.version }}" | |
| git push origin "v${{ steps.npm_version.outputs.version }}" | |
| - name: Create Draft GitHub Release | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const tagName = 'v${{ steps.npm_version.outputs.version }}'; | |
| // Create a draft release with auto-generated notes for manual curation | |
| const release = await github.rest.repos.createRelease({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| tag_name: tagName, | |
| name: `Release ${tagName}`, | |
| body: `Release version ${{ steps.npm_version.outputs.version }} (${{ steps.version.outputs.bump_type }} bump)\n\nSee [CHANGELOG.md](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/main/docs/CHANGELOG.md) for details.`, | |
| draft: true, | |
| prerelease: false, | |
| generate_release_notes: true | |
| }); | |
| console.log(`Draft release created: ${release.data.html_url}`); | |
| console.log('Please review and publish the release manually from the GitHub UI.'); |