From 8d06d0ec5520a3dab426bd40747762a1cc915a82 Mon Sep 17 00:00:00 2001 From: Julien Goux Date: Tue, 3 Nov 2026 10:43:21 +0100 Subject: [PATCH 1/3] ci: prepare for merge queue and approve Dependabot PRs as a code owner Run CI, E2E and Licensed on merge_group so the required checks report for queued entries, and detect changed license inputs from the merge group range. Rework the Dependabot workflow to pull_request_target in the auto-approve environment so the supabase-oss token approves non-major updates. An app approval cannot satisfy the code-owner review, so supabase-oss is now a co-owner of the files Dependabot updates. --- .github/CODEOWNERS | 11 ++++++ .github/workflows/ci.yml | 3 +- .github/workflows/dependabot.yml | 62 +++++++++++++++++++++----------- .github/workflows/e2e.yml | 3 +- .github/workflows/licensed.yml | 11 ++++-- 5 files changed, 65 insertions(+), 25 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e58bd32..d0de5dc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,12 @@ +# Default all repository changes to the CLI team. * @supabase/cli + +# Files that Dependabot updates. Either owner satisfies code-owner review; `supabase-oss` +# approves only Dependabot PRs, through dependabot.yml. +/.github/workflows/*.yml @supabase/cli @supabase-oss +/package.json @supabase/cli @supabase-oss +/bun.lock @supabase/cli @supabase-oss + +# Dependabot PRs touching this workflow still need a maintainer: it runs with secrets. +# Last matching pattern wins. +/.github/workflows/dependabot.yml @supabase/cli diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40fb228..959db5a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,7 @@ on: - reopened - ready_for_review - converted_to_draft + merge_group: push: branches: - main @@ -85,7 +86,7 @@ jobs: supabase -h ci: - if: ${{ always() && github.event_name == 'pull_request' }} + if: ${{ always() && (github.event_name == 'pull_request' || github.event_name == 'merge_group') }} name: CI runs-on: ubuntu-latest needs: [validate, test, github-release-fallback] diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index c5e213c..d7626b6 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -1,47 +1,67 @@ # Adapted from https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions#enable-auto-merge-on-a-pull-request name: Dependabot auto-merge -on: pull_request +on: + # Dependabot-triggered pull_request runs get no Actions or environment secrets. Only this + # event lets the job use the auto-approve environment's secret. The job never checks out or + # runs code from the pull request. + # jactionlint ignore=dangerous-triggers + pull_request_target: + types: [opened, reopened, synchronize, ready_for_review] + branches: [main] concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} cancel-in-progress: true -permissions: - pull-requests: write - contents: write +permissions: {} jobs: dependabot: + name: Approve and enable auto-merge runs-on: ubuntu-latest timeout-minutes: 10 - # Only act on PRs opened by Dependabot from branches in this repository. - if: github.actor == 'dependabot[bot]' && github.repository == github.event.pull_request.head.repo.full_name + # The first condition checks who opened the pull request. The second checks who caused this + # event, so a person pushing to a Dependabot pull request does not get it approved. Only + # branches in this repository qualify. + # jactionlint ignore=bot-conditions + if: >- + github.event.pull_request.user.login == 'dependabot[bot]' && + github.event.sender.login == 'dependabot[bot]' && + github.event.pull_request.head.repo.full_name == github.repository && + !github.event.pull_request.draft + # Holds `AUTO_APPROVE_PR_PAT`; the environment only deploys from the default branch. + environment: auto-approve + permissions: + pull-requests: write + contents: write steps: + # pull_request_target is safe here because this job never checks out PR code. # Metadata drives the non-major gating used for approval and auto-merge. - id: meta uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - - name: Generate token - id: app-token - if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor' }} - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 - with: - app-id: ${{ secrets.APP_ID }} - private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - + # The org ruleset requires a code-owner review, which an app approval cannot satisfy. + # `supabase-oss` is a co-owner of the files Dependabot updates (see CODEOWNERS). A rewritten + # PR keeps its approval; skipping it avoids stacking a review per push. - name: Approve a PR if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor' }} - run: gh pr review --approve "$PR_URL" + run: | + set -euo pipefail + decision="$(GH_TOKEN="$READ_TOKEN" gh pr view --repo "$GITHUB_REPOSITORY" "$PR_NUMBER" --json reviewDecision --jq .reviewDecision)" + if [ "$decision" != "APPROVED" ]; then + gh pr review --approve --repo "$GITHUB_REPOSITORY" "$PR_NUMBER" + fi env: - PR_URL: ${{ github.event.pull_request.html_url }} - GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + GH_TOKEN: ${{ secrets.AUTO_APPROVE_PR_PAT }} + READ_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} - name: Enable auto-merge for Dependabot PRs if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor' }} - run: gh pr merge --auto --squash "$PR_URL" + run: gh pr merge --auto --squash --repo "$GITHUB_REPOSITORY" "$PR_NUMBER" env: - PR_URL: ${{ github.event.pull_request.html_url }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index aa5cec0..aa4c946 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -7,6 +7,7 @@ on: - reopened - ready_for_review - converted_to_draft + merge_group: push: branches: - main @@ -69,7 +70,7 @@ jobs: - run: supabase start e2e-check: - if: ${{ always() && github.event_name == 'pull_request' }} + if: ${{ always() && (github.event_name == 'pull_request' || github.event_name == 'merge_group') }} name: E2E runs-on: ubuntu-latest needs: [e2e] diff --git a/.github/workflows/licensed.yml b/.github/workflows/licensed.yml index 423de7a..b3e8f12 100644 --- a/.github/workflows/licensed.yml +++ b/.github/workflows/licensed.yml @@ -6,6 +6,7 @@ name: Licensed on: pull_request: + merge_group: push: branches: - main @@ -38,15 +39,21 @@ jobs: id: license-inputs env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + EVENT_NAME: ${{ github.event_name }} PR_NUMBER: ${{ github.event.pull_request.number }} + MERGE_BASE_SHA: ${{ github.event.merge_group.base_sha }} + MERGE_HEAD_SHA: ${{ github.event.merge_group.head_sha }} REPOSITORY: ${{ github.repository }} run: | - if [[ "${{ github.event_name }}" != "pull_request" ]]; then + if [[ "$EVENT_NAME" == "pull_request" ]]; then + gh api "repos/${REPOSITORY}/pulls/${PR_NUMBER}/files" --paginate --jq '.[].filename' > changed-files.txt + elif [[ "$EVENT_NAME" == "merge_group" ]]; then + gh api "repos/${REPOSITORY}/compare/${MERGE_BASE_SHA}...${MERGE_HEAD_SHA}" --paginate --jq '.files[].filename' > changed-files.txt + else echo "changed=true" >> "$GITHUB_OUTPUT" exit 0 fi - gh api "repos/${REPOSITORY}/pulls/${PR_NUMBER}/files" --paginate --jq '.[].filename' > changed-files.txt if grep -Eq '^(\.github/workflows/licensed\.yml|\.licensed\.yml|\.licenses/.*|bun\.lock|package\.json)$' changed-files.txt; then echo "changed=true" >> "$GITHUB_OUTPUT" else From 8366ba649a5c0a1b31c0e5d639a409b3d54f8d8d Mon Sep 17 00:00:00 2001 From: Julien Goux Date: Tue, 3 Nov 2026 11:10:52 +0100 Subject: [PATCH 2/3] ci: enable Dependabot auto-merge with the app token --- .github/workflows/dependabot.yml | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml index d7626b6..9032b3f 100644 --- a/.github/workflows/dependabot.yml +++ b/.github/workflows/dependabot.yml @@ -32,9 +32,10 @@ jobs: !github.event.pull_request.draft # Holds `AUTO_APPROVE_PR_PAT`; the environment only deploys from the default branch. environment: auto-approve + # The built-in token only reads; the PAT approves and the app token enables auto-merge. permissions: - pull-requests: write - contents: write + pull-requests: read + contents: read steps: # pull_request_target is safe here because this job never checks out PR code. # Metadata drives the non-major gating used for approval and auto-merge. @@ -59,9 +60,21 @@ jobs: READ_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} + # The built-in GITHUB_TOKEN cannot add pull requests to a merge queue, and the merge group + # it would create would not trigger the required checks. + - name: Generate token + id: app-token + if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor' }} + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + permission-contents: write + permission-pull-requests: write + - name: Enable auto-merge for Dependabot PRs if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor' }} run: gh pr merge --auto --squash --repo "$GITHUB_REPOSITORY" "$PR_NUMBER" env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ steps.app-token.outputs.token }} PR_NUMBER: ${{ github.event.pull_request.number }} From 892887aa3c07227171fc590bab9eead8330b60b2 Mon Sep 17 00:00:00 2001 From: Julien Goux Date: Tue, 3 Nov 2026 11:58:57 +0100 Subject: [PATCH 3/3] ci: co-own action.yml for Dependabot and avoid the compare file cap --- .github/CODEOWNERS | 1 + .github/workflows/licensed.yml | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d0de5dc..8e07e54 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,6 +4,7 @@ # Files that Dependabot updates. Either owner satisfies code-owner review; `supabase-oss` # approves only Dependabot PRs, through dependabot.yml. /.github/workflows/*.yml @supabase/cli @supabase-oss +/action.yml @supabase/cli @supabase-oss /package.json @supabase/cli @supabase-oss /bun.lock @supabase/cli @supabase-oss diff --git a/.github/workflows/licensed.yml b/.github/workflows/licensed.yml index b3e8f12..688a4cd 100644 --- a/.github/workflows/licensed.yml +++ b/.github/workflows/licensed.yml @@ -48,7 +48,12 @@ jobs: if [[ "$EVENT_NAME" == "pull_request" ]]; then gh api "repos/${REPOSITORY}/pulls/${PR_NUMBER}/files" --paginate --jq '.[].filename' > changed-files.txt elif [[ "$EVENT_NAME" == "merge_group" ]]; then - gh api "repos/${REPOSITORY}/compare/${MERGE_BASE_SHA}...${MERGE_HEAD_SHA}" --paginate --jq '.files[].filename' > changed-files.txt + gh api "repos/${REPOSITORY}/compare/${MERGE_BASE_SHA}...${MERGE_HEAD_SHA}" --jq '.files[].filename' > changed-files.txt + # The compare API lists at most 300 files; check everything when the list may be cut off. + if (( $(wc -l < changed-files.txt) >= 300 )); then + echo "changed=true" >> "$GITHUB_OUTPUT" + exit 0 + fi else echo "changed=true" >> "$GITHUB_OUTPUT" exit 0