From 065167fd8ab0a5ec57b1377c06e53b33b9f78f6c Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 28 Oct 2025 12:16:32 -0400 Subject: [PATCH 1/9] Automate release branching --- .github/workflows/check-release-pr.yaml | 55 +++++++++++++++ .github/workflows/create-release-pr.yaml | 67 +++++++++++++++++++ .../create-release-tag-on-merge.yaml | 51 ++++++++++++++ .github/workflows/minor-release.yaml | 16 +++++ .github/workflows/patch-release.yaml | 20 ++++++ Justfile | 6 ++ 6 files changed, 215 insertions(+) create mode 100644 .github/workflows/check-release-pr.yaml create mode 100644 .github/workflows/create-release-pr.yaml create mode 100644 .github/workflows/create-release-tag-on-merge.yaml create mode 100644 .github/workflows/minor-release.yaml create mode 100644 .github/workflows/patch-release.yaml diff --git a/.github/workflows/check-release-pr.yaml b/.github/workflows/check-release-pr.yaml new file mode 100644 index 000000000000..10eec4ec97dc --- /dev/null +++ b/.github/workflows/check-release-pr.yaml @@ -0,0 +1,55 @@ +name: Check Release PR + +on: + pull_request: + types: [opened, synchronize] + branches: + - main + +jobs: + check-commits: + runs-on: ubuntu-latest + if: startsWith(github.head_ref, 'release/') + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + fetch-depth: 0 + + - name: Check all PR commits exist in main + run: | + git fetch origin main + + CHERRY_OUTPUT=$(git cherry origin/main HEAD) + + if [ -z "$CHERRY_OUTPUT" ]; then + echo "✅ All commits already exist in main" + exit 0 + fi + + echo "Cherry check results:" + echo "$CHERRY_OUTPUT" + + MISSING_COMMITS=$(echo "$CHERRY_OUTPUT" | grep '^+' | cut -d' ' -f2) + + if [ -z "$MISSING_COMMITS" ]; then + echo "✅ All commits exist in main" + exit 0 + fi + + COMMIT_COUNT=$(echo "$MISSING_COMMITS" | wc -l) + FIRST_COMMIT=$(git rev-list --reverse HEAD ^origin/main | head -1) + + if [ "$COMMIT_COUNT" -eq 1 ] && echo "$MISSING_COMMITS" | grep -q "$FIRST_COMMIT"; then + echo "✅ Only version bump commit is unique" + git log --oneline -1 "$FIRST_COMMIT" + else + echo "❌ Found commits that should exist in main:" + for commit in $MISSING_COMMITS; do + if [ "$commit" != "$FIRST_COMMIT" ]; then + git log --oneline -1 "$commit" + fi + done + echo "Make sure commits have equivalents in main. If you've since updated main, re-run this job" + exit 1 + fi diff --git a/.github/workflows/create-release-pr.yaml b/.github/workflows/create-release-pr.yaml new file mode 100644 index 000000000000..3eab7e3c60d8 --- /dev/null +++ b/.github/workflows/create-release-pr.yaml @@ -0,0 +1,67 @@ +name: Release Workflow + +on: + workflow_call: + inputs: + bump_type: + description: 'Type of version bump (minor or patch)' + required: true + type: string + target_branch: + description: 'Target branch for the pull request' + required: false + type: string + default: 'main' + +permissions: + contents: write + pull-requests: write + +jobs: + create-release: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: cashapp/activate-hermit@v1 + + - name: Validate bump type + run: | + if [[ "${{ inputs.bump_type }}" != "minor" && "${{ inputs.bump_type }}" != "patch" ]]; then + echo "Error: bump_type must be 'minor' or 'patch'" + exit 1 + fi + + - name: create release branch + run: | + if [[ "${{ inputs.bump_type }}" == "minor" ]]; then + VERSION=$(just get-next-minor-version) + else + VERSION=$(just get-next-patch-version) + fi + + echo "version=$VERSION" >> $GITHUB_ENV + echo "Version: $VERSION" + + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + + just prepare-release $VERSION + BRANCH_NAME=$(git branch --show-current) + echo "branch_name=$BRANCH_NAME" >> $GITHUB_ENV + echo "Branch: $BRANCH_NAME" + + - name: push release branch + run: | + git push origin "${{ env.branch_name }}" + + - name: Create Pull Request + run: | + gh pr create \ + -B "${{ inputs.target_branch }}" \ + -H "${{ env.branch_name }}" \ + --title 'chore(release): release version ${{ env.version }} (${{ inputs.bump_type }})' \ + --body '_Created automatically by ${{ github.workflow_ref }}_' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ISSUE: ${{ github.event.issue.html_url }} diff --git a/.github/workflows/create-release-tag-on-merge.yaml b/.github/workflows/create-release-tag-on-merge.yaml new file mode 100644 index 000000000000..83e902fa7bf8 --- /dev/null +++ b/.github/workflows/create-release-tag-on-merge.yaml @@ -0,0 +1,51 @@ +name: Create release tag and patch release PR on merge + +on: + pull_request: + types: [closed] + branches: + - main + - release/* + +permissions: + actions: write + contents: write + pull-requests: write + +jobs: + trigger-patch-release: + runs-on: ubuntu-latest + if: > + github.event.pull_request.merged == true && + startsWith(github.event.pull_request.head.ref, 'release/') + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Restore release branch and create tag + run: | + BRANCH_NAME="${{ github.event.pull_request.head.ref }}" + PR_HEAD_SHA="${{ github.event.pull_request.head.sha }}" + + git checkout -b "$BRANCH_NAME" "$PR_HEAD_SHA" + git push origin "$BRANCH_NAME" + + echo "✅ Restored branch: $BRANCH_NAME at $PR_HEAD_SHA" + + RELEASE_TAG=${BRANCH_NAME#release/} + + echo "creating release tag: $RELEASE_TAG" + git tag "$RELEASE_TAG" + git push origin tag "$RELEASE_TAG" + + - name: Trigger patch release + run: | + gh workflow run patch-release.yaml \ + --field target_branch=${{ github.event.pull_request.head.ref }} + + echo "✅ Triggered patch release workflow" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/minor-release.yaml b/.github/workflows/minor-release.yaml new file mode 100644 index 000000000000..99ce40a43932 --- /dev/null +++ b/.github/workflows/minor-release.yaml @@ -0,0 +1,16 @@ +name: Create Minor Release PR + +permissions: + contents: write + pull-requests: write + +on: + schedule: + - cron: '0 0 * * 2' + workflow_dispatch: + +jobs: + release: + uses: ./.github/workflows/create-release-pr.yaml + with: + bump_type: "minor" diff --git a/.github/workflows/patch-release.yaml b/.github/workflows/patch-release.yaml new file mode 100644 index 000000000000..29ed3c076093 --- /dev/null +++ b/.github/workflows/patch-release.yaml @@ -0,0 +1,20 @@ +name: Create Patch Release PR + +permissions: + contents: write + pull-requests: write + +on: + workflow_dispatch: + inputs: + target_branch: + description: 'Target branch for hotfix' + required: true + type: string + +jobs: + hotfix: + uses: ./.github/workflows/create-release-pr.yaml + with: + bump_type: "patch" + target_branch: ${{ inputs.target_branch }} diff --git a/Justfile b/Justfile index 6f0ac07a6f72..888cf3f7a1b6 100644 --- a/Justfile +++ b/Justfile @@ -363,6 +363,12 @@ validate version: exit 1 fi +get-next-minor-version: + @python -c "import sys; v=sys.argv[1].split('.'); print(f'{v[0]}.{int(v[1])+1}.0')" $(just get-tag-version) + +get-next-patch-version: + @python -c "import sys; v=sys.argv[1].split('.'); print(f'{v[0]}.{v[1]}.{int(v[2])+1}')" $(just get-tag-version) + # set cargo and app versions, must be semver prepare-release version: @just validate {{ version }} || exit 1 From c3fee613dc00ff0b369cd3ac5c6950cc2e77c421 Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 28 Oct 2025 12:42:45 -0400 Subject: [PATCH 2/9] pin actions --- .github/workflows/check-release-pr.yaml | 2 +- .github/workflows/create-release-pr.yaml | 4 ++-- .github/workflows/create-release-tag-on-merge.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/check-release-pr.yaml b/.github/workflows/check-release-pr.yaml index 10eec4ec97dc..caa58d5d192b 100644 --- a/.github/workflows/check-release-pr.yaml +++ b/.github/workflows/check-release-pr.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest if: startsWith(github.head_ref, 'release/') steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 with: ref: ${{ github.head_ref }} fetch-depth: 0 diff --git a/.github/workflows/create-release-pr.yaml b/.github/workflows/create-release-pr.yaml index 3eab7e3c60d8..444cbbb36318 100644 --- a/.github/workflows/create-release-pr.yaml +++ b/.github/workflows/create-release-pr.yaml @@ -22,8 +22,8 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: cashapp/activate-hermit@v1 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 + - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 - name: Validate bump type run: | diff --git a/.github/workflows/create-release-tag-on-merge.yaml b/.github/workflows/create-release-tag-on-merge.yaml index 83e902fa7bf8..4fae246ed7f3 100644 --- a/.github/workflows/create-release-tag-on-merge.yaml +++ b/.github/workflows/create-release-tag-on-merge.yaml @@ -20,7 +20,7 @@ jobs: startsWith(github.event.pull_request.head.ref, 'release/') steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} From 786008fe0cb9583e420884eaf4009fc0bf6ac2ef Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 28 Oct 2025 13:54:14 -0400 Subject: [PATCH 3/9] some fixes --- .github/workflows/check-release-pr.yaml | 4 ++- .github/workflows/create-release-pr.yaml | 32 +++++++++++++++---- .../create-release-tag-on-merge.yaml | 14 ++++---- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/.github/workflows/check-release-pr.yaml b/.github/workflows/check-release-pr.yaml index caa58d5d192b..d5462ee41749 100644 --- a/.github/workflows/check-release-pr.yaml +++ b/.github/workflows/check-release-pr.yaml @@ -2,7 +2,9 @@ name: Check Release PR on: pull_request: - types: [opened, synchronize] + types: + - opened + - synchronize branches: - main diff --git a/.github/workflows/create-release-pr.yaml b/.github/workflows/create-release-pr.yaml index 444cbbb36318..f9294c25ce6a 100644 --- a/.github/workflows/create-release-pr.yaml +++ b/.github/workflows/create-release-pr.yaml @@ -20,26 +20,34 @@ permissions: jobs: create-release: runs-on: ubuntu-latest + env: + BUMP_TYPE: ${{ inputs.bump_type }} + TARGET_BRANCH: ${{ inputs.target_branch }} steps: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 + with: + ref: ${{ inputs.target_branch }} + fetch-depth: 0 # to generate complete release log - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 - - name: Validate bump type + - name: Validate input and set old version run: | - if [[ "${{ inputs.bump_type }}" != "minor" && "${{ inputs.bump_type }}" != "patch" ]]; then + if [[ "$BUMP_TYPE" != "minor" && "$BUMP_TYPE" != "patch" ]]; then echo "Error: bump_type must be 'minor' or 'patch'" exit 1 fi - name: create release branch run: | - if [[ "${{ inputs.bump_type }}" == "minor" ]]; then + PRIOR_VERSION=$(just get-tag-version) + if [[ "$BUMP_TYPE" == "minor" ]]; then VERSION=$(just get-next-minor-version) else VERSION=$(just get-next-patch-version) fi + echo "prior_version=$PRIOR_VERSION" >> $GITHUB_ENV echo "version=$VERSION" >> $GITHUB_ENV echo "Version: $VERSION" @@ -57,11 +65,23 @@ jobs: - name: Create Pull Request run: | + cat > pr_body.txt <> pr_body.txt + gh pr create \ - -B "${{ inputs.target_branch }}" \ + -B "$TARGET_BRANCH" \ -H "${{ env.branch_name }}" \ - --title 'chore(release): release version ${{ env.version }} (${{ inputs.bump_type }})' \ - --body '_Created automatically by ${{ github.workflow_ref }}_' + --title "chore(release): release version ${{ env.version }} ($BUMP_TYPE)" \ + --body-file pr_body.txt env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} ISSUE: ${{ github.event.issue.html_url }} diff --git a/.github/workflows/create-release-tag-on-merge.yaml b/.github/workflows/create-release-tag-on-merge.yaml index 4fae246ed7f3..bece6b237e80 100644 --- a/.github/workflows/create-release-tag-on-merge.yaml +++ b/.github/workflows/create-release-tag-on-merge.yaml @@ -2,7 +2,8 @@ name: Create release tag and patch release PR on merge on: pull_request: - types: [closed] + types: + - closed branches: - main - release/* @@ -15,6 +16,9 @@ permissions: jobs: trigger-patch-release: runs-on: ubuntu-latest + env: + BRANCH_NAME: ${{ github.event.pull_request.head.ref }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} if: > github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/') @@ -23,19 +27,15 @@ jobs: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 with: fetch-depth: 0 - token: ${{ secrets.GITHUB_TOKEN }} - name: Restore release branch and create tag run: | - BRANCH_NAME="${{ github.event.pull_request.head.ref }}" - PR_HEAD_SHA="${{ github.event.pull_request.head.sha }}" - git checkout -b "$BRANCH_NAME" "$PR_HEAD_SHA" git push origin "$BRANCH_NAME" echo "✅ Restored branch: $BRANCH_NAME at $PR_HEAD_SHA" - RELEASE_TAG=${BRANCH_NAME#release/} + RELEASE_TAG=v${BRANCH_NAME#release/} echo "creating release tag: $RELEASE_TAG" git tag "$RELEASE_TAG" @@ -44,7 +44,7 @@ jobs: - name: Trigger patch release run: | gh workflow run patch-release.yaml \ - --field target_branch=${{ github.event.pull_request.head.ref }} + --field target_branch=$BRANCH_NAME echo "✅ Triggered patch release workflow" env: From 73cb915eae86692ec683b85563f73d2b9c9a3de3 Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 4 Nov 2025 16:55:25 -0500 Subject: [PATCH 4/9] Merge on tag, not the other way --- .github/workflows/create-release-pr.yaml | 2 +- .../create-release-tag-on-merge.yaml | 148 +++++++++++++++--- 2 files changed, 125 insertions(+), 25 deletions(-) diff --git a/.github/workflows/create-release-pr.yaml b/.github/workflows/create-release-pr.yaml index f9294c25ce6a..18244760ad08 100644 --- a/.github/workflows/create-release-pr.yaml +++ b/.github/workflows/create-release-pr.yaml @@ -68,7 +68,7 @@ jobs: cat > pr_body.txt < - github.event.pull_request.merged == true && - startsWith(github.event.pull_request.head.ref, 'release/') steps: - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 - with: - fetch-depth: 0 - - name: Restore release branch and create tag + - name: Extract version from tag + id: version run: | - git checkout -b "$BRANCH_NAME" "$PR_HEAD_SHA" - git push origin "$BRANCH_NAME" + TAG=${{ github.ref_name }} + VERSION=${TAG#v} + BRANCH="release/${VERSION}" - echo "✅ Restored branch: $BRANCH_NAME at $PR_HEAD_SHA" + echo "tag=${TAG}" >> $GITHUB_OUTPUT + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "branch=${BRANCH}" >> $GITHUB_OUTPUT - RELEASE_TAG=v${BRANCH_NAME#release/} + echo "Tag: ${TAG}" + echo "Version: ${VERSION}" + echo "Expected branch: ${BRANCH}" - echo "creating release tag: $RELEASE_TAG" - git tag "$RELEASE_TAG" - git push origin tag "$RELEASE_TAG" + - name: Find matching PR + id: find_pr + run: | + BRANCH="${{ steps.version.outputs.branch }}" + + PR_NUMBER=$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number // empty') + + if [ -z "$PR_NUMBER" ]; then + echo "❌ No open PR found for branch: $BRANCH" + echo "pr_found=false" >> $GITHUB_OUTPUT + exit 1 + else + echo "✅ Found PR #$PR_NUMBER for branch: $BRANCH" + echo "pr_found=true" >> $GITHUB_OUTPUT + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + fi + env: + GH_TOKEN: ${{ github.token }} + + - name: Get PR details and check status + if: steps.find_pr.outputs.pr_found == 'true' + id: pr_status + run: | + PR_NUMBER="${{ steps.find_pr.outputs.pr_number }}" + + PR_DATA=$(gh pr view $PR_NUMBER --json title,headRefName,baseRefName,mergeable,statusCheckRollup) + + TITLE=$(echo "$PR_DATA" | jq -r '.title') + HEAD_BRANCH=$(echo "$PR_DATA" | jq -r '.headRefName') + BASE_BRANCH=$(echo "$PR_DATA" | jq -r '.baseRefName') + MERGEABLE=$(echo "$PR_DATA" | jq -r '.mergeable') + + echo "PR Title: $TITLE" + echo "Head Branch: $HEAD_BRANCH" + echo "Base Branch: $BASE_BRANCH" + echo "Mergeable: $MERGEABLE" + + if [ "$MERGEABLE" != "MERGEABLE" ]; then + echo "❌ PR is not in a mergeable state: $MERGEABLE" + echo "can_merge=false" >> $GITHUB_OUTPUT + echo "merge_reason=PR is not mergeable (state: $MERGEABLE)" >> $GITHUB_OUTPUT + exit 0 + fi + + STATUS_CHECKS=$(echo "$PR_DATA" | jq -r '.statusCheckRollup[]? | select(.conclusion != null) | "\(.context): \(.conclusion)"') + FAILED_CHECKS=$(echo "$PR_DATA" | jq -r '.statusCheckRollup[]? | select(.conclusion == "FAILURE" or .conclusion == "CANCELLED" or .conclusion == "TIMED_OUT") | .context') + PENDING_CHECKS=$(echo "$PR_DATA" | jq -r '.statusCheckRollup[]? | select(.conclusion == null) | .context') + + echo "Status checks:" + if [ -n "$STATUS_CHECKS" ]; then + echo "$STATUS_CHECKS" + else + echo "No status checks found" + fi + + if [ -n "$FAILED_CHECKS" ]; then + echo "❌ Failed checks found:" + echo "$FAILED_CHECKS" + echo "can_merge=false" >> $GITHUB_OUTPUT + echo "merge_reason=Some checks are failing" >> $GITHUB_OUTPUT + exit 0 + fi + + if [ -n "$PENDING_CHECKS" ]; then + echo "⏳ Pending checks found:" + echo "$PENDING_CHECKS" + echo "can_merge=false" >> $GITHUB_OUTPUT + echo "merge_reason=Some checks are still pending" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "✅ All checks are passing and PR is ready to merge" + echo "can_merge=true" >> $GITHUB_OUTPUT + env: + GH_TOKEN: ${{ github.token }} + + - name: Get branch SHA before merge + if: steps.find_pr.outputs.pr_found == 'true' && steps.pr_status.outputs.can_merge == 'true' + id: branch_info + run: | + BRANCH="${{ steps.version.outputs.branch }}" + + git fetch origin + + BRANCH_SHA=$(git rev-parse "origin/$BRANCH") + echo "branch_sha=$BRANCH_SHA" >> $GITHUB_OUTPUT + echo "Branch SHA: $BRANCH_SHA" + + - name: Merge PR + if: steps.find_pr.outputs.pr_found == 'true' && steps.pr_status.outputs.can_merge == 'true' + run: | + PR_NUMBER="${{ steps.find_pr.outputs.pr_number }}" + VERSION="${{ steps.version.outputs.version }}" + + gh pr merge $PR_NUMBER --squash --delete-branch --subject "Release $VERSION" --body "Auto-merged release PR after tag ${{ steps.version.outputs.tag }} was pushed" + env: + GH_TOKEN: ${{ github.token }} + + - name: Restore branch + if: steps.find_pr.outputs.pr_found == 'true' && steps.pr_status.outputs.can_merge == 'true' + run: | + BRANCH="${{ steps.version.outputs.branch }}" + BRANCH_SHA="${{ steps.branch_info.outputs.branch_sha }}" + + git checkout -b "$BRANCH" "$BRANCH_SHA" + git push origin "$BRANCH" - name: Trigger patch release run: | + BRANCH="${{ steps.version.outputs.branch }}" gh workflow run patch-release.yaml \ - --field target_branch=$BRANCH_NAME + --field target_branch=$BRANCH echo "✅ Triggered patch release workflow" env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ github.token }} From 899afadac35859da02a62eb830edf85ecda7abed Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 4 Nov 2025 17:11:51 -0500 Subject: [PATCH 5/9] rename --- ...erge.yaml => merge-release-pr-on-tag.yaml} | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) rename .github/workflows/{create-release-tag-on-merge.yaml => merge-release-pr-on-tag.yaml} (86%) diff --git a/.github/workflows/create-release-tag-on-merge.yaml b/.github/workflows/merge-release-pr-on-tag.yaml similarity index 86% rename from .github/workflows/create-release-tag-on-merge.yaml rename to .github/workflows/merge-release-pr-on-tag.yaml index e7e155b7c9b9..44cb92b4f162 100644 --- a/.github/workflows/create-release-tag-on-merge.yaml +++ b/.github/workflows/merge-release-pr-on-tag.yaml @@ -1,4 +1,4 @@ -name: Create release tag and patch release PR on merge +name: Merge release PR on tag push on: push: @@ -20,8 +20,9 @@ jobs: - name: Extract version from tag id: version + env: + TAG: ${{ github.ref_name }} run: | - TAG=${{ github.ref_name }} VERSION=${TAG#v} BRANCH="release/${VERSION}" @@ -35,9 +36,10 @@ jobs: - name: Find matching PR id: find_pr + env: + GH_TOKEN: ${{ github.token }} + BRANCH: ${{ steps.version.outputs.branch }} run: | - BRANCH="${{ steps.version.outputs.branch }}" - PR_NUMBER=$(gh pr list --head "$BRANCH" --state open --json number --jq '.[0].number // empty') if [ -z "$PR_NUMBER" ]; then @@ -49,15 +51,14 @@ jobs: echo "pr_found=true" >> $GITHUB_OUTPUT echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT fi - env: - GH_TOKEN: ${{ github.token }} - name: Get PR details and check status if: steps.find_pr.outputs.pr_found == 'true' id: pr_status + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ steps.find_pr.outputs.pr_number }} run: | - PR_NUMBER="${{ steps.find_pr.outputs.pr_number }}" - PR_DATA=$(gh pr view $PR_NUMBER --json title,headRefName,baseRefName,mergeable,statusCheckRollup) TITLE=$(echo "$PR_DATA" | jq -r '.title') @@ -106,15 +107,13 @@ jobs: echo "✅ All checks are passing and PR is ready to merge" echo "can_merge=true" >> $GITHUB_OUTPUT - env: - GH_TOKEN: ${{ github.token }} - name: Get branch SHA before merge if: steps.find_pr.outputs.pr_found == 'true' && steps.pr_status.outputs.can_merge == 'true' id: branch_info + env: + BRANCH: ${{ steps.version.outputs.branch }} run: | - BRANCH="${{ steps.version.outputs.branch }}" - git fetch origin BRANCH_SHA=$(git rev-parse "origin/$BRANCH") @@ -123,29 +122,27 @@ jobs: - name: Merge PR if: steps.find_pr.outputs.pr_found == 'true' && steps.pr_status.outputs.can_merge == 'true' - run: | - PR_NUMBER="${{ steps.find_pr.outputs.pr_number }}" - VERSION="${{ steps.version.outputs.version }}" - - gh pr merge $PR_NUMBER --squash --delete-branch --subject "Release $VERSION" --body "Auto-merged release PR after tag ${{ steps.version.outputs.tag }} was pushed" env: GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ steps.find_pr.outputs.pr_number }} + VERSION: ${{ steps.version.outputs.version }} + TAG: ${{ steps.version.outputs.tag }} + run: | + gh pr merge $PR_NUMBER --squash --delete-branch --subject "Release $VERSION" --body "Auto-merged release PR after tag $TAG was pushed" - name: Restore branch if: steps.find_pr.outputs.pr_found == 'true' && steps.pr_status.outputs.can_merge == 'true' + env: + BRANCH: ${{ steps.version.outputs.branch }} + BRANCH_SHA: ${{ steps.branch_info.outputs.branch_sha }} run: | - BRANCH="${{ steps.version.outputs.branch }}" - BRANCH_SHA="${{ steps.branch_info.outputs.branch_sha }}" - git checkout -b "$BRANCH" "$BRANCH_SHA" git push origin "$BRANCH" - name: Trigger patch release + env: + BRANCH: ${{ steps.version.outputs.branch }} + GH_TOKEN: ${{ github.token }} run: | - BRANCH="${{ steps.version.outputs.branch }}" gh workflow run patch-release.yaml \ --field target_branch=$BRANCH - - echo "✅ Triggered patch release workflow" - env: - GH_TOKEN: ${{ github.token }} From c78f4fda3bc86de8f155358bf8dba72eb00de1f6 Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 4 Nov 2025 22:00:22 -0500 Subject: [PATCH 6/9] update release notes --- .../generate-release-pr-body/action.yaml | 44 +++++++++++++ .../pr_body_template.txt | 19 ++++++ .github/workflows/create-release-pr.yaml | 22 +++---- .../workflows/merge-release-pr-on-tag.yaml | 2 +- .github/workflows/update-release-pr.yaml | 64 +++++++++++++++++++ 5 files changed, 137 insertions(+), 14 deletions(-) create mode 100644 .github/actions/generate-release-pr-body/action.yaml create mode 100644 .github/actions/generate-release-pr-body/pr_body_template.txt create mode 100644 .github/workflows/update-release-pr.yaml diff --git a/.github/actions/generate-release-pr-body/action.yaml b/.github/actions/generate-release-pr-body/action.yaml new file mode 100644 index 000000000000..cc7def3d2d1c --- /dev/null +++ b/.github/actions/generate-release-pr-body/action.yaml @@ -0,0 +1,44 @@ +name: 'Generate Release Notes' +description: 'Generate release notes for a given version' + +inputs: + version: + description: 'The version being released' + required: true + head_ref: + description: 'The version being released' + required: true + prior_ref: + description: 'The previous version to compare against' + required: true + +outputs: + pr_body_file: + description: 'Path to the generated PR body file' + value: ${{ steps.generate.outputs.pr_body_file }} + +runs: + using: 'composite' + steps: + - name: Generate release notes + id: generate + shell: bash + env: + VERSION: ${{ inputs.version }} + HEAD_REF: ${{ inputs.head_ref }} + PRIOR_REF: ${{ inputs.prior_ref }} + TEMPLATE_FILE: "${{ github.action_path }}/pr_body_template.txt" + run: | + git fetch origin --tags + + { + sed -e "s/{{VERSION}}/${VERSION}/g" \ + -e "s/{{PRIOR_VERSION}}/${PRIOR_REF}/g" \ + "$TEMPLATE_FILE" + git log --pretty=format:"- %s (%h)" --reverse ${PRIOR_REF}..${HEAD_REF} + echo "" + echo "---" + echo "*This release PR was generated automatically.*" + } > pr_body.txt + + echo "pr_body_file=pr_body.txt" >> $GITHUB_OUTPUT diff --git a/.github/actions/generate-release-pr-body/pr_body_template.txt b/.github/actions/generate-release-pr-body/pr_body_template.txt new file mode 100644 index 000000000000..e63e7c2041f7 --- /dev/null +++ b/.github/actions/generate-release-pr-body/pr_body_template.txt @@ -0,0 +1,19 @@ +# Release v{{VERSION}} + +## How to Release + +Push the release tag to trigger the release: + ```bash + git fetch && git tag v{{VERSION}} origin/release/{{VERSION}} + git push origin v{{VERSION}} + ``` +This PR will auto-merge once the tag is pushed. + +## Important Notes + +- All commits in this release should have corresponding cherry-picks in `main` +- This PR can be closed if the release is not needed. + +## Changes in This Release + +**Comparing:** `{{PRIOR_VERSION}}...v{{VERSION}}` diff --git a/.github/workflows/create-release-pr.yaml b/.github/workflows/create-release-pr.yaml index 18244760ad08..9269172fa3e5 100644 --- a/.github/workflows/create-release-pr.yaml +++ b/.github/workflows/create-release-pr.yaml @@ -29,6 +29,7 @@ jobs: with: ref: ${{ inputs.target_branch }} fetch-depth: 0 # to generate complete release log + - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 - name: Validate input and set old version @@ -47,7 +48,7 @@ jobs: VERSION=$(just get-next-patch-version) fi - echo "prior_version=$PRIOR_VERSION" >> $GITHUB_ENV + echo "prior_ref=v$PRIOR_VERSION" >> $GITHUB_ENV echo "version=$VERSION" >> $GITHUB_ENV echo "Version: $VERSION" @@ -63,20 +64,15 @@ jobs: run: | git push origin "${{ env.branch_name }}" + - name: Generate release notes + uses: ./.github/actions/generate-release-pr-body + with: + version: ${{ env.version }} + head_ref: ${{ github.event.pull_request.head.sha }} + prior_ref: ${{ env.prior_ref }} + - name: Create Pull Request run: | - cat > pr_body.txt <> pr_body.txt - gh pr create \ -B "$TARGET_BRANCH" \ -H "${{ env.branch_name }}" \ diff --git a/.github/workflows/merge-release-pr-on-tag.yaml b/.github/workflows/merge-release-pr-on-tag.yaml index 44cb92b4f162..cbf122f7c55c 100644 --- a/.github/workflows/merge-release-pr-on-tag.yaml +++ b/.github/workflows/merge-release-pr-on-tag.yaml @@ -3,7 +3,7 @@ name: Merge release PR on tag push on: push: tags: - - 'v[0-9]+.[0-9]+.[0-9]+' # Matches v1.2.3, v10.20.30, etc. + - 'v[0-9]+.[0-9]+.[0-9]+' permissions: actions: write diff --git a/.github/workflows/update-release-pr.yaml b/.github/workflows/update-release-pr.yaml new file mode 100644 index 000000000000..3ffe5e300905 --- /dev/null +++ b/.github/workflows/update-release-pr.yaml @@ -0,0 +1,64 @@ +name: Update Release Notes + +on: + pull_request: + types: + - synchronize + branches: + - main + +permissions: + contents: read + pull-requests: write + +jobs: + update-release-notes: + runs-on: ubuntu-latest + if: startsWith(github.event.pull_request.head.ref, 'release/') && github.event.pull_request.user.login == 'github-actions[bot]' + steps: + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 + with: + fetch-depth: 0 # to generate complete release log + + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 + with: + ref: ${{ github.base_ref }} + path: './prior-version' + + - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 + + - name: Extract version from branch name + env: + REF_NAME: ${{ github.head_ref }} + run: | + BRANCH_NAME="$REF_NAME" + VERSION=$(echo "$BRANCH_NAME" | sed 's/release\///') + echo "version=$VERSION" >> $GITHUB_ENV + echo "Version: $VERSION" + + - name: Get prior version + working-directory: './prior-version' + run: | + PRIOR_VERSION=$(just get-tag-version) + echo "prior_ref=v$PRIOR_VERSION" >> $GITHUB_ENV + + - name: Generate release notes + uses: ./.github/actions/generate-release-pr-body + with: + version: ${{ env.version }} + head_ref: ${{ github.event.pull_request.head.sha }} + prior_ref: ${{ env.prior_ref }} + + - name: Update Pull Request + env: + REF_NAME: ${{ github.head_ref }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_NUMBER=$(gh pr list --head "$REF_NAME" --json number --jq '.[0].number') + + if [[ -z "$PR_NUMBER" || "$PR_NUMBER" == "null" ]]; then + echo "No PR found for branch $REF_NAME" + exit 1 + fi + + gh pr edit "$PR_NUMBER" --body-file pr_body.txt From 1872e4248eda976beaf626725c8cc0f2b791e562 Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 4 Nov 2025 22:03:54 -0500 Subject: [PATCH 7/9] Also on patch PRs --- .github/workflows/update-release-pr.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update-release-pr.yaml b/.github/workflows/update-release-pr.yaml index 3ffe5e300905..83a962212d61 100644 --- a/.github/workflows/update-release-pr.yaml +++ b/.github/workflows/update-release-pr.yaml @@ -6,6 +6,7 @@ on: - synchronize branches: - main + - release/** permissions: contents: read From 8bf2b1aec56d145d5581747548bc81b0ebe3c961 Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 4 Nov 2025 22:06:02 -0500 Subject: [PATCH 8/9] unused --- .github/workflows/create-release-pr.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/create-release-pr.yaml b/.github/workflows/create-release-pr.yaml index 9269172fa3e5..dcdb1b523754 100644 --- a/.github/workflows/create-release-pr.yaml +++ b/.github/workflows/create-release-pr.yaml @@ -80,4 +80,3 @@ jobs: --body-file pr_body.txt env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ISSUE: ${{ github.event.issue.html_url }} From 54267869c3354fa910718a9391da821b6f617880 Mon Sep 17 00:00:00 2001 From: Jack Amadeo Date: Tue, 4 Nov 2025 22:16:17 -0500 Subject: [PATCH 9/9] fixes --- .github/actions/generate-release-pr-body/action.yaml | 2 +- .github/workflows/create-release-pr.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/generate-release-pr-body/action.yaml b/.github/actions/generate-release-pr-body/action.yaml index cc7def3d2d1c..72ccd754e382 100644 --- a/.github/actions/generate-release-pr-body/action.yaml +++ b/.github/actions/generate-release-pr-body/action.yaml @@ -6,7 +6,7 @@ inputs: description: 'The version being released' required: true head_ref: - description: 'The version being released' + description: 'The commit SHA or reference of the head of the release branch' required: true prior_ref: description: 'The previous version to compare against' diff --git a/.github/workflows/create-release-pr.yaml b/.github/workflows/create-release-pr.yaml index dcdb1b523754..7208f4e2280a 100644 --- a/.github/workflows/create-release-pr.yaml +++ b/.github/workflows/create-release-pr.yaml @@ -68,7 +68,7 @@ jobs: uses: ./.github/actions/generate-release-pr-body with: version: ${{ env.version }} - head_ref: ${{ github.event.pull_request.head.sha }} + head_ref: ${{ github.event.pull_request.head.sha || github.sha }} prior_ref: ${{ env.prior_ref }} - name: Create Pull Request