From 727f29b6c2862280c68591b2774a8d3dc9798d3e Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Wed, 2 Jul 2025 16:59:25 -0400 Subject: [PATCH 01/17] A release workflow github action Signed-off-by: Paul Sachs --- .github/workflows/prepare-release.yml | 86 +++++++++++++++++++ .github/workflows/publish-release.yml | 119 ++++++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 .github/workflows/prepare-release.yml create mode 100644 .github/workflows/publish-release.yml diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml new file mode 100644 index 00000000..fd52e999 --- /dev/null +++ b/.github/workflows/prepare-release.yml @@ -0,0 +1,86 @@ +name: Prepare Release + +on: + workflow_dispatch: + inputs: + version: + description: 'Version to release (e.g. 1.2.3)' + required: true + type: string + base_branch: + description: 'Base branch for release' + required: false + default: 'main' + type: string + +jobs: + prepare-release: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ inputs.base_branch }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Validate semver version + run: | + if ! echo "${{ inputs.version }}" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?(\+[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$'; then + echo "Error: '${{ inputs.version }}' is not a valid semver version" + echo "Expected format: MAJOR.MINOR.PATCH (e.g., 1.2.3) or with pre-release/build metadata (e.g., 1.2.3-alpha.1+build.1)" + exit 1 + fi + echo "โœ“ Version '${{ inputs.version }}' is valid semver" + + - name: Install dependencies + run: npm install + + - name: Create draft release + run: gh release create v${{ inputs.version }} --draft --generate-notes + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create release branch + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git checkout -b "release/prep-release-${{ inputs.version }}" + + - name: Set version and run build + run: | + npm run setversion ${{ inputs.version }} + + - name: Commit version changes + run: | + git add . + git commit -s -m "Release ${{ inputs.version }}" + git push --set-upstream origin "release/prep-release-${{ inputs.version }}" + + - name: Get release notes + id: release_notes + run: | + RELEASE_NOTES=$(gh release view v${{ inputs.version }} --json body | jq -r ".body") + echo "notes<> $GITHUB_OUTPUT + echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create pull request + run: | + gh pr create \ + --title "Release ${{ inputs.version }}" \ + --body "${{ steps.release_notes.outputs.notes }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 00000000..0ec2fea0 --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,119 @@ +name: Publish Release + +on: + pull_request: + types: [closed] + branches: + - main + +jobs: + publish-release: + runs-on: ubuntu-latest + # Only run if PR was merged and branch name starts with release/prep-release- + if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/prep-release-') + permissions: + contents: write + pull-requests: write + issues: write + + steps: + - name: Extract version from branch name + id: extract_version + run: | + BRANCH_NAME="${{ github.event.pull_request.head.ref }}" + VERSION=$(echo "$BRANCH_NAME" | sed 's/release\/prep-release-//') + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Checkout release branch + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: npm install + + - name: Get updated release notes from PR + id: pr_notes + run: | + RELEASE_NOTES=$(gh pr view ${{ github.event.pull_request.number }} --json body | jq -r ".body") + echo "notes<> $GITHUB_OUTPUT + echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish to npm + run: npm run release + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Publish GitHub release + run: | + gh release edit v${{ steps.extract_version.outputs.version }} \ + --notes "${{ steps.pr_notes.outputs.notes }}" \ + --draft=false + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Get release info for issue comments + id: release_info + run: | + # Get last two releases + RELEASES=$(gh api repos/${{ github.repository }}/releases --jq ".[0:2].[].name") + LATEST_RELEASE=$(echo "${RELEASES}" | head -1) + PREV_RELEASE=$(echo "${RELEASES}" | tail -1) + + echo "latest=$LATEST_RELEASE" >> $GITHUB_OUTPUT + echo "previous=$PREV_RELEASE" >> $GITHUB_OUTPUT + + RELEASE_URL=$(gh release view v${{ steps.extract_version.outputs.version }} --json url | jq -r ".url") + echo "release_url=$RELEASE_URL" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Comment on fixed issues + run: | + # Get PRs included in this release + START=$(gh release view ${{ steps.release_info.outputs.previous }} --json publishedAt | jq -r ".publishedAt") + END=$(gh release view ${{ steps.release_info.outputs.latest }} --json publishedAt | jq -r ".publishedAt") + PRS=$(gh pr list --search="merged:$START..$END" --json="number" | jq -r ".[].number") + + # For each PR, get the issues it fixes and comment on them + echo "$PRS" | while IFS= read -r pr; do + if [[ -z "$pr" ]]; then + continue + fi + + ISSUES=$(gh api graphql -F owner='${{ github.repository_owner }}' -F repo='${{ github.event.repository.name }}' -F pr=$pr -f query=' + query ($owner: String!, $repo: String!, $pr: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $pr) { + closingIssuesReferences(first: 100) { + nodes { + number + } + } + } + } + }' | jq -r '.data.repository.pullRequest.closingIssuesReferences.nodes[].number') + + echo "$ISSUES" | while IFS= read -r issue; do + issue=$(echo $issue | tr -d '\n') + if [[ -z "$issue" ]]; then + continue + fi + echo "Adding comment to issue $issue" + gh issue comment $issue -b "Released in [${{ steps.extract_version.outputs.version }}](${{ steps.release_info.outputs.release_url }}) ๐Ÿš€" + done + done + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From ee2c908b717d8ba2e14f45408382a930e34e1721 Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Wed, 2 Jul 2025 17:01:38 -0400 Subject: [PATCH 02/17] Format Signed-off-by: Paul Sachs --- .github/workflows/prepare-release.yml | 14 +++++++------- .github/workflows/publish-release.yml | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index fd52e999..9047e6a5 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -4,13 +4,13 @@ on: workflow_dispatch: inputs: version: - description: 'Version to release (e.g. 1.2.3)' + description: "Version to release (e.g. 1.2.3)" required: true type: string base_branch: - description: 'Base branch for release' + description: "Base branch for release" required: false - default: 'main' + default: "main" type: string jobs: @@ -19,7 +19,7 @@ jobs: permissions: contents: write pull-requests: write - + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -31,8 +31,8 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version-file: '.nvmrc' - cache: 'npm' + node-version-file: ".nvmrc" + cache: "npm" - name: Validate semver version run: | @@ -83,4 +83,4 @@ jobs: --title "Release ${{ inputs.version }}" \ --body "${{ steps.release_notes.outputs.notes }}" env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 0ec2fea0..f90b45f6 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -15,7 +15,7 @@ jobs: contents: write pull-requests: write issues: write - + steps: - name: Extract version from branch name id: extract_version @@ -34,9 +34,9 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version-file: '.nvmrc' - cache: 'npm' - registry-url: 'https://registry.npmjs.org' + node-version-file: ".nvmrc" + cache: "npm" + registry-url: "https://registry.npmjs.org" - name: Install dependencies run: npm install @@ -71,10 +71,10 @@ jobs: RELEASES=$(gh api repos/${{ github.repository }}/releases --jq ".[0:2].[].name") LATEST_RELEASE=$(echo "${RELEASES}" | head -1) PREV_RELEASE=$(echo "${RELEASES}" | tail -1) - + echo "latest=$LATEST_RELEASE" >> $GITHUB_OUTPUT echo "previous=$PREV_RELEASE" >> $GITHUB_OUTPUT - + RELEASE_URL=$(gh release view v${{ steps.extract_version.outputs.version }} --json url | jq -r ".url") echo "release_url=$RELEASE_URL" >> $GITHUB_OUTPUT env: @@ -86,7 +86,7 @@ jobs: START=$(gh release view ${{ steps.release_info.outputs.previous }} --json publishedAt | jq -r ".publishedAt") END=$(gh release view ${{ steps.release_info.outputs.latest }} --json publishedAt | jq -r ".publishedAt") PRS=$(gh pr list --search="merged:$START..$END" --json="number" | jq -r ".[].number") - + # For each PR, get the issues it fixes and comment on them echo "$PRS" | while IFS= read -r pr; do if [[ -z "$pr" ]]; then @@ -116,4 +116,4 @@ jobs: done done env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From bc3644d6d2bafa566046b158c9647fd4d41534cf Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Wed, 2 Jul 2025 17:22:32 -0400 Subject: [PATCH 03/17] Add some cleanup and fix potential edge cases Signed-off-by: Paul Sachs --- .github/workflows/publish-release.yml | 38 +++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index f90b45f6..86a5ddff 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -24,10 +24,10 @@ jobs: VERSION=$(echo "$BRANCH_NAME" | sed 's/release\/prep-release-//') echo "version=$VERSION" >> $GITHUB_OUTPUT - - name: Checkout release branch + - name: Checkout main branch uses: actions/checkout@v4 with: - ref: ${{ github.event.pull_request.head.ref }} + ref: main fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} @@ -117,3 +117,37 @@ jobs: done env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + cleanup-canceled-release: + runs-on: ubuntu-latest + # Only run if PR was closed without merge and branch name starts with release/prep-release- + if: github.event.pull_request.merged == false && startsWith(github.event.pull_request.head.ref, 'release/prep-release-') + permissions: + contents: write + pull-requests: write + + steps: + - name: Extract version from branch name + id: extract_version + run: | + BRANCH_NAME="${{ github.event.pull_request.head.ref }}" + VERSION=$(echo "$BRANCH_NAME" | sed 's/release\/prep-release-//') + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Delete draft release + run: | + if gh release view v${{ steps.extract_version.outputs.version }} --json isDraft | jq -r ".isDraft" | grep -q "true"; then + echo "Deleting draft release v${{ steps.extract_version.outputs.version }}" + gh release delete v${{ steps.extract_version.outputs.version }} --yes + else + echo "Release v${{ steps.extract_version.outputs.version }} is not a draft, skipping deletion" + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Delete release branch + continue-on-error: true + run: | + echo "Deleting release branch ${{ github.event.pull_request.head.ref }}" + git push origin --delete ${{ github.event.pull_request.head.ref }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 560086810bcbf7dd8fcaaa5acb9b819d79407a36 Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Wed, 2 Jul 2025 17:35:35 -0400 Subject: [PATCH 04/17] Add support for alternative base ref Signed-off-by: Paul Sachs --- .github/workflows/prepare-release.yml | 5 +++-- .github/workflows/publish-release.yml | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 9047e6a5..075caf2d 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -8,7 +8,7 @@ on: required: true type: string base_branch: - description: "Base branch for release" + description: "Base branch for release (e.g. release/v1.x, hotfix/v1.2.x)" required: false default: "main" type: string @@ -81,6 +81,7 @@ jobs: run: | gh pr create \ --title "Release ${{ inputs.version }}" \ - --body "${{ steps.release_notes.outputs.notes }}" + --body "${{ steps.release_notes.outputs.notes }}" \ + --base "${{ inputs.base_branch }}" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 86a5ddff..5c11a377 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -5,6 +5,8 @@ on: types: [closed] branches: - main + - 'release/**' + - 'hotfix/**' jobs: publish-release: @@ -24,10 +26,10 @@ jobs: VERSION=$(echo "$BRANCH_NAME" | sed 's/release\/prep-release-//') echo "version=$VERSION" >> $GITHUB_OUTPUT - - name: Checkout main branch + - name: Checkout base branch uses: actions/checkout@v4 with: - ref: main + ref: ${{ github.event.pull_request.base.ref }} fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} From da0dc69f4dd89d379cdc0ce76fbd2029478d4bcd Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Wed, 2 Jul 2025 17:50:05 -0400 Subject: [PATCH 05/17] Format again Signed-off-by: Paul Sachs --- .github/workflows/publish-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 5c11a377..49b8ab75 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -5,8 +5,8 @@ on: types: [closed] branches: - main - - 'release/**' - - 'hotfix/**' + - "release/**" + - "hotfix/**" jobs: publish-release: From e608382b66ac4c4a237ec02da41a28bc3f213d3f Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Tue, 15 Jul 2025 11:43:42 -0400 Subject: [PATCH 06/17] Review updates - Moved to npm ci - Split issue updates into a distinct action - Removed unnecessary registry option on publish setup Signed-off-by: Paul Sachs --- .github/workflows/prepare-release.yml | 11 +-- .github/workflows/publish-release.yml | 57 +-------------- .github/workflows/update-fixed-issues.yaml | 82 ++++++++++++++++++++++ 3 files changed, 84 insertions(+), 66 deletions(-) create mode 100644 .github/workflows/update-fixed-issues.yaml diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 075caf2d..6bb2d753 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -34,17 +34,8 @@ jobs: node-version-file: ".nvmrc" cache: "npm" - - name: Validate semver version - run: | - if ! echo "${{ inputs.version }}" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?(\+[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$'; then - echo "Error: '${{ inputs.version }}' is not a valid semver version" - echo "Expected format: MAJOR.MINOR.PATCH (e.g., 1.2.3) or with pre-release/build metadata (e.g., 1.2.3-alpha.1+build.1)" - exit 1 - fi - echo "โœ“ Version '${{ inputs.version }}' is valid semver" - - name: Install dependencies - run: npm install + run: npm ci - name: Create draft release run: gh release create v${{ inputs.version }} --draft --generate-notes diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 49b8ab75..49981adf 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -38,10 +38,9 @@ jobs: with: node-version-file: ".nvmrc" cache: "npm" - registry-url: "https://registry.npmjs.org" - name: Install dependencies - run: npm install + run: npm ci - name: Get updated release notes from PR id: pr_notes @@ -65,60 +64,6 @@ jobs: --draft=false env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Get release info for issue comments - id: release_info - run: | - # Get last two releases - RELEASES=$(gh api repos/${{ github.repository }}/releases --jq ".[0:2].[].name") - LATEST_RELEASE=$(echo "${RELEASES}" | head -1) - PREV_RELEASE=$(echo "${RELEASES}" | tail -1) - - echo "latest=$LATEST_RELEASE" >> $GITHUB_OUTPUT - echo "previous=$PREV_RELEASE" >> $GITHUB_OUTPUT - - RELEASE_URL=$(gh release view v${{ steps.extract_version.outputs.version }} --json url | jq -r ".url") - echo "release_url=$RELEASE_URL" >> $GITHUB_OUTPUT - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Comment on fixed issues - run: | - # Get PRs included in this release - START=$(gh release view ${{ steps.release_info.outputs.previous }} --json publishedAt | jq -r ".publishedAt") - END=$(gh release view ${{ steps.release_info.outputs.latest }} --json publishedAt | jq -r ".publishedAt") - PRS=$(gh pr list --search="merged:$START..$END" --json="number" | jq -r ".[].number") - - # For each PR, get the issues it fixes and comment on them - echo "$PRS" | while IFS= read -r pr; do - if [[ -z "$pr" ]]; then - continue - fi - - ISSUES=$(gh api graphql -F owner='${{ github.repository_owner }}' -F repo='${{ github.event.repository.name }}' -F pr=$pr -f query=' - query ($owner: String!, $repo: String!, $pr: Int!) { - repository(owner: $owner, name: $repo) { - pullRequest(number: $pr) { - closingIssuesReferences(first: 100) { - nodes { - number - } - } - } - } - }' | jq -r '.data.repository.pullRequest.closingIssuesReferences.nodes[].number') - - echo "$ISSUES" | while IFS= read -r issue; do - issue=$(echo $issue | tr -d '\n') - if [[ -z "$issue" ]]; then - continue - fi - echo "Adding comment to issue $issue" - gh issue comment $issue -b "Released in [${{ steps.extract_version.outputs.version }}](${{ steps.release_info.outputs.release_url }}) ๐Ÿš€" - done - done - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} cleanup-canceled-release: runs-on: ubuntu-latest # Only run if PR was closed without merge and branch name starts with release/prep-release- diff --git a/.github/workflows/update-fixed-issues.yaml b/.github/workflows/update-fixed-issues.yaml new file mode 100644 index 00000000..4b4ec026 --- /dev/null +++ b/.github/workflows/update-fixed-issues.yaml @@ -0,0 +1,82 @@ +name: Update Fixed Issues + +on: + release: + types: [published] + +jobs: + update-fixed-issues: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + issues: write + + steps: + - name: Extract version from release + id: extract_version + run: | + VERSION="${{ github.event.release.tag_name }}" + # Remove 'v' prefix if present + VERSION=$(echo "$VERSION" | sed 's/^v//') + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Get release info for issue comments + id: release_info + run: | + # Get last two releases + RELEASES=$(gh api repos/${{ github.repository }}/releases --jq ".[0:2].[].name") + LATEST_RELEASE=$(echo "${RELEASES}" | head -1) + PREV_RELEASE=$(echo "${RELEASES}" | tail -1) + + echo "latest=$LATEST_RELEASE" >> $GITHUB_OUTPUT + echo "previous=$PREV_RELEASE" >> $GITHUB_OUTPUT + + RELEASE_URL=$(gh release view v${{ steps.extract_version.outputs.version }} --json url | jq -r ".url") + echo "release_url=$RELEASE_URL" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Comment on fixed issues + run: | + # Get PRs included in this release + START=$(gh release view ${{ steps.release_info.outputs.previous }} --json publishedAt | jq -r ".publishedAt") + END=$(gh release view ${{ steps.release_info.outputs.latest }} --json publishedAt | jq -r ".publishedAt") + PRS=$(gh pr list --search="merged:$START..$END" --json="number" | jq -r ".[].number") + + # For each PR, get the issues it fixes and comment on them + echo "$PRS" | while IFS= read -r pr; do + if [[ -z "$pr" ]]; then + continue + fi + + ISSUES=$(gh api graphql -F owner='${{ github.repository_owner }}' -F repo='${{ github.event.repository.name }}' -F pr=$pr -f query=' + query ($owner: String!, $repo: String!, $pr: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $pr) { + closingIssuesReferences(first: 100) { + nodes { + number + } + } + } + } + }' | jq -r '.data.repository.pullRequest.closingIssuesReferences.nodes[].number') + + echo "$ISSUES" | while IFS= read -r issue; do + issue=$(echo $issue | tr -d '\n') + if [[ -z "$issue" ]]; then + continue + fi + echo "Adding comment to issue $issue" + gh issue comment $issue -b "Released in [${{ steps.extract_version.outputs.version }}](${{ steps.release_info.outputs.release_url }}) ๐Ÿš€" + done + done + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 495369045bd4f59317baacbdfcf3997a2b1aa75d Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Tue, 15 Jul 2025 11:47:51 -0400 Subject: [PATCH 07/17] Naming files consistently Signed-off-by: Paul Sachs --- .github/workflows/{add-to-project.yaml => add-to-project.yml} | 0 .../{update-fixed-issues.yaml => update-fixed-issues.yml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{add-to-project.yaml => add-to-project.yml} (100%) rename .github/workflows/{update-fixed-issues.yaml => update-fixed-issues.yml} (100%) diff --git a/.github/workflows/add-to-project.yaml b/.github/workflows/add-to-project.yml similarity index 100% rename from .github/workflows/add-to-project.yaml rename to .github/workflows/add-to-project.yml diff --git a/.github/workflows/update-fixed-issues.yaml b/.github/workflows/update-fixed-issues.yml similarity index 100% rename from .github/workflows/update-fixed-issues.yaml rename to .github/workflows/update-fixed-issues.yml From 7ec7e0914f00f4d71d779102deabb3989d4e9e61 Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Wed, 17 Sep 2025 15:40:48 -0400 Subject: [PATCH 08/17] Use npm trusted publishing Signed-off-by: Paul Sachs --- .github/workflows/publish-release.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 49981adf..f2786832 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -8,6 +8,10 @@ on: - "release/**" - "hotfix/**" +permissions: + id-token: write # Required for OIDC + contents: read + jobs: publish-release: runs-on: ubuntu-latest @@ -54,8 +58,6 @@ jobs: - name: Publish to npm run: npm run release - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Publish GitHub release run: | From fda61ca0845df5e1081c6e39cd69e3814f24ed55 Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Wed, 17 Sep 2025 15:43:11 -0400 Subject: [PATCH 09/17] Move permissions to single location Signed-off-by: Paul Sachs --- .github/workflows/publish-release.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index f2786832..8704dac7 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -8,16 +8,13 @@ on: - "release/**" - "hotfix/**" -permissions: - id-token: write # Required for OIDC - contents: read - jobs: publish-release: runs-on: ubuntu-latest # Only run if PR was merged and branch name starts with release/prep-release- if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/prep-release-') permissions: + id-token: write # Required for OIDC contents: write pull-requests: write issues: write From f743b8e15d61dabbf8c1e92530e58929125e5c42 Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Wed, 24 Sep 2025 17:21:23 -0400 Subject: [PATCH 10/17] Fixed a minor bug on cleanup and validated trusted publishing Also removed update fixed issues action which wouldn't trigger since the triggering event is a github action and github actions prevents cascading actions. Signed-off-by: Paul Sachs --- .github/workflows/publish-release.yml | 6 ++ .github/workflows/update-fixed-issues.yml | 82 ----------------------- 2 files changed, 6 insertions(+), 82 deletions(-) delete mode 100644 .github/workflows/update-fixed-issues.yml diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 8704dac7..f3e40881 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -72,6 +72,12 @@ jobs: pull-requests: write steps: + - name: Checkout base branch + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.base.ref }} + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} - name: Extract version from branch name id: extract_version run: | diff --git a/.github/workflows/update-fixed-issues.yml b/.github/workflows/update-fixed-issues.yml deleted file mode 100644 index 4b4ec026..00000000 --- a/.github/workflows/update-fixed-issues.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: Update Fixed Issues - -on: - release: - types: [published] - -jobs: - update-fixed-issues: - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - issues: write - - steps: - - name: Extract version from release - id: extract_version - run: | - VERSION="${{ github.event.release.tag_name }}" - # Remove 'v' prefix if present - VERSION=$(echo "$VERSION" | sed 's/^v//') - echo "version=$VERSION" >> $GITHUB_OUTPUT - - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get release info for issue comments - id: release_info - run: | - # Get last two releases - RELEASES=$(gh api repos/${{ github.repository }}/releases --jq ".[0:2].[].name") - LATEST_RELEASE=$(echo "${RELEASES}" | head -1) - PREV_RELEASE=$(echo "${RELEASES}" | tail -1) - - echo "latest=$LATEST_RELEASE" >> $GITHUB_OUTPUT - echo "previous=$PREV_RELEASE" >> $GITHUB_OUTPUT - - RELEASE_URL=$(gh release view v${{ steps.extract_version.outputs.version }} --json url | jq -r ".url") - echo "release_url=$RELEASE_URL" >> $GITHUB_OUTPUT - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Comment on fixed issues - run: | - # Get PRs included in this release - START=$(gh release view ${{ steps.release_info.outputs.previous }} --json publishedAt | jq -r ".publishedAt") - END=$(gh release view ${{ steps.release_info.outputs.latest }} --json publishedAt | jq -r ".publishedAt") - PRS=$(gh pr list --search="merged:$START..$END" --json="number" | jq -r ".[].number") - - # For each PR, get the issues it fixes and comment on them - echo "$PRS" | while IFS= read -r pr; do - if [[ -z "$pr" ]]; then - continue - fi - - ISSUES=$(gh api graphql -F owner='${{ github.repository_owner }}' -F repo='${{ github.event.repository.name }}' -F pr=$pr -f query=' - query ($owner: String!, $repo: String!, $pr: Int!) { - repository(owner: $owner, name: $repo) { - pullRequest(number: $pr) { - closingIssuesReferences(first: 100) { - nodes { - number - } - } - } - } - }' | jq -r '.data.repository.pullRequest.closingIssuesReferences.nodes[].number') - - echo "$ISSUES" | while IFS= read -r issue; do - issue=$(echo $issue | tr -d '\n') - if [[ -z "$issue" ]]; then - continue - fi - echo "Adding comment to issue $issue" - gh issue comment $issue -b "Released in [${{ steps.extract_version.outputs.version }}](${{ steps.release_info.outputs.release_url }}) ๐Ÿš€" - done - done - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 73177a9fa8e08dc1d45541d5731c94a9be8b3aef Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Thu, 25 Sep 2025 10:04:13 -0400 Subject: [PATCH 11/17] Updated semver version check with official regex Signed-off-by: Paul Sachs --- scripts/set-workspace-version.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/set-workspace-version.js b/scripts/set-workspace-version.js index deb647b5..2103eb77 100755 --- a/scripts/set-workspace-version.js +++ b/scripts/set-workspace-version.js @@ -18,7 +18,14 @@ import { readFileSync, writeFileSync, existsSync, globSync } from "node:fs"; import { dirname, join } from "node:path"; -if (process.argv.length !== 3 || !/^\d+\.\d+\.\d+$/.test(process.argv[2])) { +// Ensures that a valid semver version is provided +// See https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string +if ( + process.argv.length !== 3 || + !/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/.test( + process.argv[2], + ) +) { process.stderr.write( [ `USAGE: ${process.argv[1]} `, From 7446a78d7edaeab6063acef9179c0b31dedea64e Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Thu, 25 Sep 2025 10:07:05 -0400 Subject: [PATCH 12/17] Improved set version requirements Signed-off-by: Paul Sachs --- scripts/set-workspace-version.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/scripts/set-workspace-version.js b/scripts/set-workspace-version.js index 2103eb77..78814ad5 100755 --- a/scripts/set-workspace-version.js +++ b/scripts/set-workspace-version.js @@ -20,12 +20,9 @@ import { dirname, join } from "node:path"; // Ensures that a valid semver version is provided // See https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string -if ( - process.argv.length !== 3 || - !/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/.test( - process.argv[2], - ) -) { +const versionRegex = + /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/; +if (process.argv.length !== 3 || !versionRegex.test(process.argv[2])) { process.stderr.write( [ `USAGE: ${process.argv[1]} `, @@ -35,6 +32,12 @@ if ( "If a package depends on another package from the workspace, the", "dependency version is updated as well.", "", + ...(versionRegex.test(process.argv[2]) + ? [] + : [ + "Version provided is not a valid semver version.", + "Please provide a version in the format MAJOR.MINOR.PATCH[-PRERELEASE+BUILD].", + ]), ].join("\n"), ); process.exit(1); From fbd6ac5696c9c21cd324adbbc8b2fec3fe3df4f2 Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Thu, 23 Oct 2025 10:23:29 -0400 Subject: [PATCH 13/17] Revert file rename Signed-off-by: Paul Sachs --- .github/workflows/{add-to-project.yml => add-to-project.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{add-to-project.yml => add-to-project.yaml} (100%) diff --git a/.github/workflows/add-to-project.yml b/.github/workflows/add-to-project.yaml similarity index 100% rename from .github/workflows/add-to-project.yml rename to .github/workflows/add-to-project.yaml From 1c2cb2785db2ca6ee483617461794d2fc16a8fb1 Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Thu, 23 Oct 2025 10:50:15 -0400 Subject: [PATCH 14/17] Update setup-node action Signed-off-by: Paul Sachs --- .github/workflows/prepare-release.yml | 3 +-- .github/workflows/publish-release.yml | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 6bb2d753..975d2c2e 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -29,10 +29,9 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: ".nvmrc" - cache: "npm" - name: Install dependencies run: npm ci diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index f3e40881..3af5a105 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -35,10 +35,9 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: ".nvmrc" - cache: "npm" - name: Install dependencies run: npm ci From 0cc3e60305fa8a4cafdcf143443978a96e910dee Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Thu, 23 Oct 2025 13:11:37 -0400 Subject: [PATCH 15/17] Review comments addressed - Updated releasing process notes - Used API to get release notes to avoid draft release - Use shared js util to get workspace version Signed-off-by: Paul Sachs --- .github/RELEASING.md | 16 ++----- .github/workflows/prepare-release.yml | 28 +++++++++--- .github/workflows/publish-release.yml | 61 +++++---------------------- package.json | 1 + scripts/find-workspace-version.js | 17 ++++++++ scripts/release.js | 44 ++----------------- scripts/utils.js | 50 ++++++++++++++++++++++ 7 files changed, 107 insertions(+), 110 deletions(-) create mode 100755 scripts/find-workspace-version.js create mode 100644 scripts/utils.js diff --git a/.github/RELEASING.md b/.github/RELEASING.md index ddc79b32..47fc0214 100644 --- a/.github/RELEASING.md +++ b/.github/RELEASING.md @@ -12,18 +12,10 @@ 1. Choose a new version (e.g. 1.2.3), making sure to follow semver. Note that all packages in this repository use the same version number. -2. Make sure you are on the latest main, and create a new git branch. -3. Set the new version across all packages within the monorepo with the following - command: `npm run setversion 1.2.3` -4. Commit, push, and open a pull request with the title "Release 1.2.3". -5. Edit the PR description with release notes. See the section below for details. -6. Make sure CI passed on your PR and ask a maintainer for review. -7. After approval, run the following command to publish to npmjs.com: `npm run release`. -8. Merge your PR. -9. Create a new release in the GitHub UI - - Choose "v1.2.3" as a tag and as the release title. - - Copy and paste the release notes from the PR description. - - Check the checkbox โ€œCreate a discussion for this releaseโ€. +2. Trigger the prepare-release workflow that will create a release PR. +3. Edit the PR description with release notes. See the section below for details. +4. Make sure CI passed on your PR and ask a maintainer for review. +5. After approval, merge your PR. ## Release notes diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 975d2c2e..9b9790ce 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -8,7 +8,12 @@ on: required: true type: string base_branch: - description: "Base branch for release (e.g. release/v1.x, hotfix/v1.2.x)" + description: | + Base branch for release (release/v1.x, or main). Specifying + a base branch other than main will require that release branch to + exist. Select something like release/v1.x to create a release + for a hotfix of a major version that is behind the current main + branch. required: false default: "main" type: string @@ -36,17 +41,18 @@ jobs: - name: Install dependencies run: npm ci - - name: Create draft release - run: gh release create v${{ inputs.version }} --draft --generate-notes - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create release branch run: | git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' git checkout -b "release/prep-release-${{ inputs.version }}" + - name: Get current workspace version + id: workspace_version + run: | + VERSION=$(npm run getversion --silent) + echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Set version and run build run: | npm run setversion ${{ inputs.version }} @@ -60,7 +66,15 @@ jobs: - name: Get release notes id: release_notes run: | - RELEASE_NOTES=$(gh release view v${{ inputs.version }} --json body | jq -r ".body") + RELEASE_NOTES=$( + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/${{ github.repository }}/releases/generate-notes \ + -f 'tag_name=v${{ inputs.version }}' -f 'target_commitish=${{ inputs.base_branch }}' -f 'previous_tag_name=v${{ steps.workspace_version.outputs.version }}' \ + --jq ".body" \ + ) echo "notes<> $GITHUB_OUTPUT echo "$RELEASE_NOTES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 3af5a105..5ffb3048 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -6,7 +6,6 @@ on: branches: - main - "release/**" - - "hotfix/**" jobs: publish-release: @@ -20,13 +19,6 @@ jobs: issues: write steps: - - name: Extract version from branch name - id: extract_version - run: | - BRANCH_NAME="${{ github.event.pull_request.head.ref }}" - VERSION=$(echo "$BRANCH_NAME" | sed 's/release\/prep-release-//') - echo "version=$VERSION" >> $GITHUB_OUTPUT - - name: Checkout base branch uses: actions/checkout@v4 with: @@ -42,6 +34,12 @@ jobs: - name: Install dependencies run: npm ci + - name: Get current workspace version + id: workspace_version + run: | + VERSION=$(npm run getversion --silent) + echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Get updated release notes from PR id: pr_notes run: | @@ -57,48 +55,9 @@ jobs: - name: Publish GitHub release run: | - gh release edit v${{ steps.extract_version.outputs.version }} \ - --notes "${{ steps.pr_notes.outputs.notes }}" \ - --draft=false - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - cleanup-canceled-release: - runs-on: ubuntu-latest - # Only run if PR was closed without merge and branch name starts with release/prep-release- - if: github.event.pull_request.merged == false && startsWith(github.event.pull_request.head.ref, 'release/prep-release-') - permissions: - contents: write - pull-requests: write - - steps: - - name: Checkout base branch - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.base.ref }} - fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - - name: Extract version from branch name - id: extract_version - run: | - BRANCH_NAME="${{ github.event.pull_request.head.ref }}" - VERSION=$(echo "$BRANCH_NAME" | sed 's/release\/prep-release-//') - echo "version=$VERSION" >> $GITHUB_OUTPUT - - - name: Delete draft release - run: | - if gh release view v${{ steps.extract_version.outputs.version }} --json isDraft | jq -r ".isDraft" | grep -q "true"; then - echo "Deleting draft release v${{ steps.extract_version.outputs.version }}" - gh release delete v${{ steps.extract_version.outputs.version }} --yes - else - echo "Release v${{ steps.extract_version.outputs.version }} is not a draft, skipping deletion" - fi - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Delete release branch - continue-on-error: true - run: | - echo "Deleting release branch ${{ github.event.pull_request.head.ref }}" - git push origin --delete ${{ github.event.pull_request.head.ref }} + gh release create v${{ steps.workspace_version.outputs.version }} \ + --title "Release v${{ steps.workspace_version.outputs.version }}" \ + --notes "${{ steps.pr_notes.outputs.notes }}" + # --discussion-category "Announcements" ## Enable if discussions are enabled env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index 09c49c77..5ba91566 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "all": "turbo run --ui tui build format test lint attw license-header", "clean": "git clean -Xdf", "setversion": "node scripts/set-workspace-version.js", + "getversion": "node scripts/find-workspace-version.js", "postsetversion": "npm run all", "release": "node scripts/release.js", "prerelease": "npm run all", diff --git a/scripts/find-workspace-version.js b/scripts/find-workspace-version.js new file mode 100755 index 00000000..121d5742 --- /dev/null +++ b/scripts/find-workspace-version.js @@ -0,0 +1,17 @@ +// Copyright 2021-2023 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { findWorkspaceVersion } from "./utils.js"; + +process.stdout.write(`${findWorkspaceVersion("packages")}\n`); diff --git a/scripts/release.js b/scripts/release.js index 1b136c65..162377e0 100644 --- a/scripts/release.js +++ b/scripts/release.js @@ -12,20 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { readdirSync, readFileSync } from "fs"; -import { join } from "path"; -import { existsSync } from "node:fs"; import { execSync } from "node:child_process"; +import { findWorkspaceVersion } from "./utils.js"; /* * Publish connect-query * * Recommended procedure: - * 1. Set a new version with `npm run setversion 1.2.3` - * 2. Commit and push all changes to a PR, wait for approval. - * 3. Login with `npm login` - * 4. Publish to npmjs.com with `npm run release` - * 5. Merge PR and create a release on GitHub + * 1. Trigger the prepare-release workflow with the version you want to release. + * 2. Reviews release notes in the created PR, wait for approval. + * 3. Merge the PR. */ const tag = determinePublishTag(findWorkspaceVersion("packages")); @@ -79,35 +75,3 @@ function determinePublishTag(version) { throw new Error(`Unable to determine publish tag from version ${version}`); } } - -/** - * @param {string} packagesDir - * @returns {string} - */ -function findWorkspaceVersion(packagesDir) { - let version = undefined; - for (const entry of readdirSync(packagesDir, { withFileTypes: true })) { - if (!entry.isDirectory()) { - continue; - } - const path = join(packagesDir, entry.name, "package.json"); - if (existsSync(path)) { - const pkg = JSON.parse(readFileSync(path, "utf-8")); - if (pkg.private === true) { - continue; - } - if (!pkg.version) { - throw new Error(`${path} is missing "version"`); - } - if (version === undefined) { - version = pkg.version; - } else if (version !== pkg.version) { - throw new Error(`${path} has unexpected version ${pkg.version}`); - } - } - } - if (version === undefined) { - throw new Error(`unable to find workspace version`); - } - return version; -} diff --git a/scripts/utils.js b/scripts/utils.js new file mode 100644 index 00000000..4969090e --- /dev/null +++ b/scripts/utils.js @@ -0,0 +1,50 @@ +// Copyright 2021-2023 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { readdirSync, readFileSync, existsSync } from "node:fs"; +import { join } from "node:path"; + +/** + * Retrieves the workspace version from the package directory. + * + * @param {string} packagesDir + * @returns {string} + */ +export function findWorkspaceVersion(packagesDir) { + let version = undefined; + for (const entry of readdirSync(packagesDir, { withFileTypes: true })) { + if (!entry.isDirectory()) { + continue; + } + const path = join(packagesDir, entry.name, "package.json"); + if (existsSync(path)) { + const pkg = JSON.parse(readFileSync(path, "utf-8")); + if (pkg.private === true) { + continue; + } + if (!pkg.version) { + throw new Error(`${path} is missing "version"`); + } + if (version === undefined) { + version = pkg.version; + } else if (version !== pkg.version) { + throw new Error(`${path} has unexpected version ${pkg.version}`); + } + } + } + if (version === undefined) { + throw new Error(`unable to find workspace version`); + } + return version; +} From 32b8f39c01c9e82f7f89d3fae9f3772ae11d9f65 Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Thu, 23 Oct 2025 13:26:12 -0400 Subject: [PATCH 16/17] Fix lint and add some explanation of base_branch to release.md Signed-off-by: Paul Sachs --- .github/RELEASING.md | 3 +++ cspell.config.json | 1 + 2 files changed, 4 insertions(+) diff --git a/.github/RELEASING.md b/.github/RELEASING.md index 47fc0214..51d94889 100644 --- a/.github/RELEASING.md +++ b/.github/RELEASING.md @@ -13,6 +13,9 @@ 1. Choose a new version (e.g. 1.2.3), making sure to follow semver. Note that all packages in this repository use the same version number. 2. Trigger the prepare-release workflow that will create a release PR. + +- Note: If releasing for a hotfix of a major version that is behind the current main branch, make sure to create an appropriate branch (e.g. release/v1.x) before running the workflow with the branch name set as the base_branch. + 3. Edit the PR description with release notes. See the section below for details. 4. Make sure CI passed on your PR and ask a maintainer for review. 5. After approval, merge your PR. diff --git a/cspell.config.json b/cspell.config.json index 5d030eeb..550e4c1e 100644 --- a/cspell.config.json +++ b/cspell.config.json @@ -40,6 +40,7 @@ "oneof", "typesafe", "setversion", + "getversion", "postsetversion", "postgenerate", "npmjs" From 684233a67e5340231fc893802f2bbb05be0c95ce Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Tue, 18 Nov 2025 12:04:26 -0500 Subject: [PATCH 17/17] Remove tentantive support for hotfix releases Signed-off-by: Paul Sachs --- .github/workflows/prepare-release.yml | 12 +----------- .github/workflows/publish-release.yml | 1 - 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 9b9790ce..86cfd6b0 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -7,16 +7,6 @@ on: description: "Version to release (e.g. 1.2.3)" required: true type: string - base_branch: - description: | - Base branch for release (release/v1.x, or main). Specifying - a base branch other than main will require that release branch to - exist. Select something like release/v1.x to create a release - for a hotfix of a major version that is behind the current main - branch. - required: false - default: "main" - type: string jobs: prepare-release: @@ -29,7 +19,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - ref: ${{ inputs.base_branch }} + ref: main fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 5ffb3048..48dd90ed 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -5,7 +5,6 @@ on: types: [closed] branches: - main - - "release/**" jobs: publish-release: