From 1918b118791cdaf75c2b62ea64302b398b379eb8 Mon Sep 17 00:00:00 2001 From: Ryan Anderson Date: Tue, 23 Dec 2025 12:08:18 -0600 Subject: [PATCH 1/3] feat: add auto-merge workflow for version PRs to enable full automation --- .github/workflows/auto-merge-version.yml | 57 ++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 .github/workflows/auto-merge-version.yml diff --git a/.github/workflows/auto-merge-version.yml b/.github/workflows/auto-merge-version.yml new file mode 100644 index 0000000..e5f3c40 --- /dev/null +++ b/.github/workflows/auto-merge-version.yml @@ -0,0 +1,57 @@ +name: Auto-merge Version PR + +on: + pull_request: + types: [opened, synchronize, labeled] + +permissions: + contents: write + pull-requests: write + +jobs: + auto-merge: + runs-on: ubuntu-latest + if: | + github.event.pull_request.title == 'chore: version packages' && + github.event.pull_request.head.repo.full_name == github.repository && + (github.event.action == 'opened' || github.event.action == 'synchronize' || (github.event.action == 'labeled' && github.event.label.name == 'auto-merge')) + steps: + - name: Wait for status checks + uses: actions/github-script@v7 + id: wait + with: + script: | + const maxWait = 300; // 5 minutes + const checkInterval = 10; // 10 seconds + let waited = 0; + + while (waited < maxWait) { + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + }); + + if (pr.mergeable === true && pr.mergeable_state === 'clean') { + console.log('PR is ready to merge'); + return; + } + + console.log(`Waiting for checks... (${waited}s)`); + await new Promise(resolve => setTimeout(resolve, checkInterval * 1000)); + waited += checkInterval; + } + + throw new Error('Timeout waiting for PR to be mergeable'); + + - name: Auto-merge version PR + uses: actions/github-script@v7 + with: + script: | + await github.rest.pulls.merge({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + merge_method: 'squash' + }); + From 67c055efe44e1817d78654f17106c5c797b82397 Mon Sep 17 00:00:00 2001 From: Ryan Anderson Date: Tue, 23 Dec 2025 12:10:09 -0600 Subject: [PATCH 2/3] refactor: consolidate all workflows into single ci.yml workflow --- .github/workflows/auto-merge-version.yml | 57 -------- .github/workflows/changesets-version.yml | 35 ----- .github/workflows/ci.yml | 176 +++++++++++++++++++++++ .github/workflows/main.yml | 38 ----- .github/workflows/tag-on-version.yml | 50 ------- 5 files changed, 176 insertions(+), 180 deletions(-) delete mode 100644 .github/workflows/auto-merge-version.yml delete mode 100644 .github/workflows/changesets-version.yml create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/main.yml delete mode 100644 .github/workflows/tag-on-version.yml diff --git a/.github/workflows/auto-merge-version.yml b/.github/workflows/auto-merge-version.yml deleted file mode 100644 index e5f3c40..0000000 --- a/.github/workflows/auto-merge-version.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Auto-merge Version PR - -on: - pull_request: - types: [opened, synchronize, labeled] - -permissions: - contents: write - pull-requests: write - -jobs: - auto-merge: - runs-on: ubuntu-latest - if: | - github.event.pull_request.title == 'chore: version packages' && - github.event.pull_request.head.repo.full_name == github.repository && - (github.event.action == 'opened' || github.event.action == 'synchronize' || (github.event.action == 'labeled' && github.event.label.name == 'auto-merge')) - steps: - - name: Wait for status checks - uses: actions/github-script@v7 - id: wait - with: - script: | - const maxWait = 300; // 5 minutes - const checkInterval = 10; // 10 seconds - let waited = 0; - - while (waited < maxWait) { - const { data: pr } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.pull_request.number, - }); - - if (pr.mergeable === true && pr.mergeable_state === 'clean') { - console.log('PR is ready to merge'); - return; - } - - console.log(`Waiting for checks... (${waited}s)`); - await new Promise(resolve => setTimeout(resolve, checkInterval * 1000)); - waited += checkInterval; - } - - throw new Error('Timeout waiting for PR to be mergeable'); - - - name: Auto-merge version PR - uses: actions/github-script@v7 - with: - script: | - await github.rest.pulls.merge({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.pull_request.number, - merge_method: 'squash' - }); - diff --git a/.github/workflows/changesets-version.yml b/.github/workflows/changesets-version.yml deleted file mode 100644 index f9ba8bd..0000000 --- a/.github/workflows/changesets-version.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Version Packages - -on: - push: - branches: [ main ] - -permissions: - contents: write - pull-requests: write - -jobs: - version: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/setup-node@v4 - with: - node-version: '24' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Create or update version PR - uses: changesets/action@v1 - with: - version: npm run version-packages - commit: "chore: version packages" - title: "chore: version packages" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ead8ac7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,176 @@ +name: CI + +on: + push: + branches: [ main ] + tags: + - 'v*' + pull_request: + branches: [ main, master ] + types: [opened, synchronize, labeled] + +permissions: + contents: write + packages: write + pull-requests: write + id-token: write + +jobs: + build-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: '24' + cache: 'npm' + registry-url: 'https://registry.npmjs.org' + + - name: Install npm dependencies + run: npm ci + + - name: Build and Test + run: npm run build + + - name: Publish to NPM + if: startsWith(github.ref, 'refs/tags/v') + run: npm publish + + version-packages: + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: '24' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Create or update version PR + uses: changesets/action@v1 + with: + version: npm run version-packages + commit: "chore: version packages" + title: "chore: version packages" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + auto-merge-version: + runs-on: ubuntu-latest + if: | + github.event_name == 'pull_request' && + github.event.pull_request.title == 'chore: version packages' && + github.event.pull_request.head.repo.full_name == github.repository && + (github.event.action == 'opened' || github.event.action == 'synchronize' || (github.event.action == 'labeled' && github.event.label.name == 'auto-merge')) + steps: + - name: Wait for status checks + uses: actions/github-script@v7 + id: wait + with: + script: | + const maxWait = 300; // 5 minutes + const checkInterval = 10; // 10 seconds + let waited = 0; + + while (waited < maxWait) { + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + }); + + if (pr.mergeable === true && pr.mergeable_state === 'clean') { + console.log('PR is ready to merge'); + return; + } + + console.log(`Waiting for checks... (${waited}s)`); + await new Promise(resolve => setTimeout(resolve, checkInterval * 1000)); + waited += checkInterval; + } + + throw new Error('Timeout waiting for PR to be mergeable'); + + - name: Auto-merge version PR + uses: actions/github-script@v7 + with: + script: | + await github.rest.pulls.merge({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + merge_method: 'squash' + }); + + tag-on-version: + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + steps: + - name: Check if version commit + id: check + run: | + if [[ "${{ github.event.head_commit.message }}" == *"chore: version packages"* ]]; then + echo "is_version=true" >> $GITHUB_OUTPUT + else + echo "is_version=false" >> $GITHUB_OUTPUT + fi + + - uses: actions/checkout@v4 + if: steps.check.outputs.is_version == 'true' + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + if: steps.check.outputs.is_version == 'true' + with: + node-version: '24' + cache: 'npm' + + - name: Install dependencies + if: steps.check.outputs.is_version == 'true' + run: npm ci + + - name: Create tags from Changesets + if: steps.check.outputs.is_version == 'true' + run: npx changeset tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Push tags + if: steps.check.outputs.is_version == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git push --follow-tags + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: '24' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Create tags from Changesets + run: npx changeset tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Push tags + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git push --follow-tags + diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 5007f53..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Build and Test - -on: - push: - branches: [ main ] - tags: - - 'v*' - pull_request: - branches: [ main, master ] - -permissions: - contents: read - packages: write - id-token: write - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/setup-node@v4 - with: - node-version: '24' - cache: 'npm' - registry-url: 'https://registry.npmjs.org' - - - name: Install npm dependencies - run: npm ci - - - name: Build and Test - run: npm run build - - - name: Publish to NPM - if: startsWith(github.ref, 'refs/tags/v') - run: npm publish diff --git a/.github/workflows/tag-on-version.yml b/.github/workflows/tag-on-version.yml deleted file mode 100644 index 7976fc9..0000000 --- a/.github/workflows/tag-on-version.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Tag on Version Commit - -on: - push: - branches: [ main ] - -permissions: - contents: write - -jobs: - tag: - runs-on: ubuntu-latest - steps: - - name: Check if version commit - id: check - run: | - if [[ "${{ github.event.head_commit.message }}" == *"chore: version packages"* ]]; then - echo "is_version=true" >> $GITHUB_OUTPUT - else - echo "is_version=false" >> $GITHUB_OUTPUT - fi - - - uses: actions/checkout@v4 - if: steps.check.outputs.is_version == 'true' - with: - fetch-depth: 0 - - - uses: actions/setup-node@v4 - if: steps.check.outputs.is_version == 'true' - with: - node-version: '24' - cache: 'npm' - - - name: Install dependencies - if: steps.check.outputs.is_version == 'true' - run: npm ci - - - name: Create tags from Changesets - if: steps.check.outputs.is_version == 'true' - run: npx changeset tag - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Push tags - if: steps.check.outputs.is_version == 'true' - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git push --follow-tags - From 0b246a1a0fa4a460e3bdce81202ec25ebdb8e796 Mon Sep 17 00:00:00 2001 From: Ryan Anderson Date: Tue, 23 Dec 2025 12:14:53 -0600 Subject: [PATCH 3/3] refactor: split workflow into ci.yml and release.yml for better separation of concerns --- .github/workflows/ci.yml | 140 +--------------------------------- .github/workflows/release.yml | 126 ++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 139 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ead8ac7..f61299d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,12 +7,10 @@ on: - 'v*' pull_request: branches: [ main, master ] - types: [opened, synchronize, labeled] permissions: - contents: write + contents: read packages: write - pull-requests: write id-token: write jobs: @@ -38,139 +36,3 @@ jobs: - name: Publish to NPM if: startsWith(github.ref, 'refs/tags/v') run: npm publish - - version-packages: - runs-on: ubuntu-latest - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/setup-node@v4 - with: - node-version: '24' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Create or update version PR - uses: changesets/action@v1 - with: - version: npm run version-packages - commit: "chore: version packages" - title: "chore: version packages" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - auto-merge-version: - runs-on: ubuntu-latest - if: | - github.event_name == 'pull_request' && - github.event.pull_request.title == 'chore: version packages' && - github.event.pull_request.head.repo.full_name == github.repository && - (github.event.action == 'opened' || github.event.action == 'synchronize' || (github.event.action == 'labeled' && github.event.label.name == 'auto-merge')) - steps: - - name: Wait for status checks - uses: actions/github-script@v7 - id: wait - with: - script: | - const maxWait = 300; // 5 minutes - const checkInterval = 10; // 10 seconds - let waited = 0; - - while (waited < maxWait) { - const { data: pr } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.pull_request.number, - }); - - if (pr.mergeable === true && pr.mergeable_state === 'clean') { - console.log('PR is ready to merge'); - return; - } - - console.log(`Waiting for checks... (${waited}s)`); - await new Promise(resolve => setTimeout(resolve, checkInterval * 1000)); - waited += checkInterval; - } - - throw new Error('Timeout waiting for PR to be mergeable'); - - - name: Auto-merge version PR - uses: actions/github-script@v7 - with: - script: | - await github.rest.pulls.merge({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.pull_request.number, - merge_method: 'squash' - }); - - tag-on-version: - runs-on: ubuntu-latest - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - steps: - - name: Check if version commit - id: check - run: | - if [[ "${{ github.event.head_commit.message }}" == *"chore: version packages"* ]]; then - echo "is_version=true" >> $GITHUB_OUTPUT - else - echo "is_version=false" >> $GITHUB_OUTPUT - fi - - - uses: actions/checkout@v4 - if: steps.check.outputs.is_version == 'true' - with: - fetch-depth: 0 - - - uses: actions/setup-node@v4 - if: steps.check.outputs.is_version == 'true' - with: - node-version: '24' - cache: 'npm' - - - name: Install dependencies - if: steps.check.outputs.is_version == 'true' - run: npm ci - - - name: Create tags from Changesets - if: steps.check.outputs.is_version == 'true' - run: npx changeset tag - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Push tags - if: steps.check.outputs.is_version == 'true' - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git push --follow-tags - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/setup-node@v4 - with: - node-version: '24' - cache: 'npm' - - - name: Install dependencies - run: npm ci - - - name: Create tags from Changesets - run: npx changeset tag - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Push tags - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git push --follow-tags - diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9159a55 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,126 @@ +name: Release + +on: + push: + branches: [ main ] + pull_request: + types: [opened, synchronize, labeled] + +permissions: + contents: write + pull-requests: write + +jobs: + version-packages: + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: '24' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Create or update version PR + uses: changesets/action@v1 + with: + version: npm run version-packages + commit: "chore: version packages" + title: "chore: version packages" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + auto-merge-version: + runs-on: ubuntu-latest + if: | + github.event_name == 'pull_request' && + github.event.pull_request.title == 'chore: version packages' && + github.event.pull_request.head.repo.full_name == github.repository && + (github.event.action == 'opened' || github.event.action == 'synchronize' || (github.event.action == 'labeled' && github.event.label.name == 'auto-merge')) + steps: + - name: Wait for status checks + uses: actions/github-script@v7 + id: wait + with: + script: | + const maxWait = 300; // 5 minutes + const checkInterval = 10; // 10 seconds + let waited = 0; + + while (waited < maxWait) { + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + }); + + if (pr.mergeable === true && pr.mergeable_state === 'clean') { + console.log('PR is ready to merge'); + return; + } + + console.log(`Waiting for checks... (${waited}s)`); + await new Promise(resolve => setTimeout(resolve, checkInterval * 1000)); + waited += checkInterval; + } + + throw new Error('Timeout waiting for PR to be mergeable'); + + - name: Auto-merge version PR + uses: actions/github-script@v7 + with: + script: | + await github.rest.pulls.merge({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + merge_method: 'squash' + }); + + tag-on-version: + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + steps: + - name: Check if version commit + id: check + run: | + if [[ "${{ github.event.head_commit.message }}" == *"chore: version packages"* ]]; then + echo "is_version=true" >> $GITHUB_OUTPUT + else + echo "is_version=false" >> $GITHUB_OUTPUT + fi + + - uses: actions/checkout@v4 + if: steps.check.outputs.is_version == 'true' + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + if: steps.check.outputs.is_version == 'true' + with: + node-version: '24' + cache: 'npm' + + - name: Install dependencies + if: steps.check.outputs.is_version == 'true' + run: npm ci + + - name: Create tags from Changesets + if: steps.check.outputs.is_version == 'true' + run: npx changeset tag + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Push tags + if: steps.check.outputs.is_version == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git push --follow-tags +