|
| 1 | +name: release-pr |
| 2 | + |
| 3 | +on: |
| 4 | + push: |
| 5 | + branches: |
| 6 | + - main |
| 7 | + |
| 8 | +permissions: |
| 9 | + contents: read |
| 10 | + pull-requests: write |
| 11 | + |
| 12 | +jobs: |
| 13 | + release-pr: |
| 14 | + name: 🚀 Create or update release PR |
| 15 | + runs-on: ubuntu-slim |
| 16 | + if: github.repository == 'npmx-dev/npmx.dev' |
| 17 | + |
| 18 | + steps: |
| 19 | + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 20 | + with: |
| 21 | + fetch-depth: 0 |
| 22 | + |
| 23 | + - name: 🔍 Check for unreleased commits |
| 24 | + id: check |
| 25 | + run: | |
| 26 | + git fetch origin release |
| 27 | + COMMITS=$(git log origin/release..origin/main --oneline) |
| 28 | + if [ -z "$COMMITS" ]; then |
| 29 | + echo "skip=true" >> "$GITHUB_OUTPUT" |
| 30 | + echo "No new commits to release" |
| 31 | + else |
| 32 | + echo "skip=false" >> "$GITHUB_OUTPUT" |
| 33 | + echo "Found unreleased commits:" |
| 34 | + echo "$COMMITS" |
| 35 | + fi |
| 36 | +
|
| 37 | + - name: 📝 Generate changelog body |
| 38 | + if: steps.check.outputs.skip == 'false' |
| 39 | + id: changelog |
| 40 | + run: | |
| 41 | + # Get the latest tag, or use initial commit if no tags exist |
| 42 | + LATEST_TAG=$(git describe --tags --abbrev=0 origin/release 2>/dev/null || echo "") |
| 43 | +
|
| 44 | + if [ -z "$LATEST_TAG" ]; then |
| 45 | + FROM_REF=$(git rev-list --max-parents=0 HEAD) |
| 46 | + CURRENT_VERSION="0.0.0" |
| 47 | + else |
| 48 | + FROM_REF="$LATEST_TAG" |
| 49 | + CURRENT_VERSION="${LATEST_TAG#v}" |
| 50 | + fi |
| 51 | +
|
| 52 | + # Categorize commits |
| 53 | + FEATURES="" |
| 54 | + FIXES="" |
| 55 | + CHORES="" |
| 56 | + OTHER="" |
| 57 | +
|
| 58 | + while IFS= read -r line; do |
| 59 | + [ -z "$line" ] && continue |
| 60 | + SHA=$(echo "$line" | cut -d' ' -f1) |
| 61 | + MSG=$(echo "$line" | cut -d' ' -f2-) |
| 62 | + ENTRY="- $MSG (\`$SHA\`)" |
| 63 | +
|
| 64 | + if echo "$MSG" | grep -qE '^feat(\(|:)'; then |
| 65 | + FEATURES="${FEATURES}${ENTRY}\n" |
| 66 | + elif echo "$MSG" | grep -qE '^fix(\(|:)'; then |
| 67 | + FIXES="${FIXES}${ENTRY}\n" |
| 68 | + elif echo "$MSG" | grep -qE '^(chore|ci|build|perf|refactor|style|test|docs)(\(|:)'; then |
| 69 | + CHORES="${CHORES}${ENTRY}\n" |
| 70 | + else |
| 71 | + OTHER="${OTHER}${ENTRY}\n" |
| 72 | + fi |
| 73 | + done <<< "$(git log "$FROM_REF"..origin/main --oneline --no-merges)" |
| 74 | +
|
| 75 | + # Determine next version |
| 76 | + HAS_BREAKING=$(git log "$FROM_REF"..origin/main --format='%B' | grep -c 'BREAKING CHANGE\|!:' || true) |
| 77 | + HAS_FEAT=$(git log "$FROM_REF"..origin/main --oneline --no-merges | grep -cE '^[a-f0-9]+ feat(\(|:)' || true) |
| 78 | +
|
| 79 | + IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" |
| 80 | +
|
| 81 | + if [ "$HAS_BREAKING" -gt 0 ] && [ "$MAJOR" -gt 0 ]; then |
| 82 | + NEXT_VERSION="$((MAJOR + 1)).0.0" |
| 83 | + elif [ "$HAS_FEAT" -gt 0 ]; then |
| 84 | + NEXT_VERSION="${MAJOR}.$((MINOR + 1)).0" |
| 85 | + else |
| 86 | + NEXT_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))" |
| 87 | + fi |
| 88 | +
|
| 89 | + echo "next_version=v${NEXT_VERSION}" >> "$GITHUB_OUTPUT" |
| 90 | +
|
| 91 | + # Build the PR body |
| 92 | + BODY="This PR will deploy the following changes to production (\`npmx.dev\`).\n\n" |
| 93 | + BODY="${BODY}**Next version: \`v${NEXT_VERSION}\`** (current: \`v${CURRENT_VERSION}\`)\n\n" |
| 94 | +
|
| 95 | + if [ -n "$FEATURES" ]; then |
| 96 | + BODY="${BODY}### Features\n\n${FEATURES}\n" |
| 97 | + fi |
| 98 | + if [ -n "$FIXES" ]; then |
| 99 | + BODY="${BODY}### Fixes\n\n${FIXES}\n" |
| 100 | + fi |
| 101 | + if [ -n "$CHORES" ]; then |
| 102 | + BODY="${BODY}### Other Changes\n\n${CHORES}\n" |
| 103 | + fi |
| 104 | + if [ -n "$OTHER" ]; then |
| 105 | + BODY="${BODY}### Uncategorized\n\n${OTHER}\n" |
| 106 | + fi |
| 107 | +
|
| 108 | + BODY="${BODY}---\n\n" |
| 109 | + BODY="${BODY}> Merging this PR will:\n" |
| 110 | + BODY="${BODY}> - Deploy to \`npmx.dev\` via Vercel\n" |
| 111 | + BODY="${BODY}> - Create a \`v${NEXT_VERSION}\` tag and GitHub Release\n" |
| 112 | + BODY="${BODY}> - Publish \`npmx-connector@${NEXT_VERSION}\` to npm" |
| 113 | +
|
| 114 | + # Write body to file, truncating if needed (GitHub limits PR body to 65536 chars) |
| 115 | + echo -e "$BODY" > /tmp/pr-body.md |
| 116 | + if [ "$(wc -c < /tmp/pr-body.md)" -gt 60000 ]; then |
| 117 | + COMMIT_COUNT=$(git log "$FROM_REF"..origin/main --oneline --no-merges | wc -l) |
| 118 | + COMPARE_URL="https://github.com/npmx-dev/npmx.dev/compare/${FROM_REF}...main" |
| 119 | + TRUNCATED="This PR will deploy the following changes to production (\`npmx.dev\`).\n\n" |
| 120 | + TRUNCATED="${TRUNCATED}**Next version: \`v${NEXT_VERSION}\`** (current: \`v${CURRENT_VERSION}\`)\n\n" |
| 121 | + TRUNCATED="${TRUNCATED}> **${COMMIT_COUNT} commits** are included in this release. The full changelog is too large to display here.\n>\n" |
| 122 | + TRUNCATED="${TRUNCATED}> [View full diff on GitHub](${COMPARE_URL})\n\n" |
| 123 | + TRUNCATED="${TRUNCATED}---\n\n" |
| 124 | + TRUNCATED="${TRUNCATED}> Merging this PR will:\n" |
| 125 | + TRUNCATED="${TRUNCATED}> - Deploy to \`npmx.dev\` via Vercel\n" |
| 126 | + TRUNCATED="${TRUNCATED}> - Create a \`v${NEXT_VERSION}\` tag and GitHub Release\n" |
| 127 | + TRUNCATED="${TRUNCATED}> - Publish \`npmx-connector@${NEXT_VERSION}\` to npm" |
| 128 | + echo -e "$TRUNCATED" > /tmp/pr-body.md |
| 129 | + fi |
| 130 | +
|
| 131 | + - name: 🚀 Create or update release PR |
| 132 | + if: steps.check.outputs.skip == 'false' |
| 133 | + env: |
| 134 | + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 135 | + NEXT_VERSION: ${{ steps.changelog.outputs.next_version }} |
| 136 | + run: | |
| 137 | + EXISTING_PR=$(gh pr list --base release --head main --state open --json number --jq '.[0].number') |
| 138 | +
|
| 139 | + if [ -n "$EXISTING_PR" ]; then |
| 140 | + gh pr edit "$EXISTING_PR" \ |
| 141 | + --title "chore: release ${NEXT_VERSION}" \ |
| 142 | + --body-file /tmp/pr-body.md |
| 143 | + echo "Updated existing PR #${EXISTING_PR}" |
| 144 | + else |
| 145 | + gh pr create \ |
| 146 | + --base release \ |
| 147 | + --head main \ |
| 148 | + --title "chore: release ${NEXT_VERSION}" \ |
| 149 | + --body-file /tmp/pr-body.md \ |
| 150 | + --label "release" |
| 151 | + echo "Created new release PR" |
| 152 | + fi |
0 commit comments