From 01b464e4cb7ffbe164e5128d62aae0256ecd4956 Mon Sep 17 00:00:00 2001 From: Cursor Date: Thu, 3 Sep 2026 23:39:35 +0000 Subject: [PATCH 1/3] Allow Dependabot PRs with empty update-type when non-major Pip requirement-range bumps often leave fetch-metadata update-type empty, so the minor/patch allowlist skipped approve on those PRs. Keep blocking explicit majors, and for empty update-type fall back to non-major dependency groups or from/to versions in the PR title. Co-authored-by: ProxyMesh AI --- .github/workflows/dependabot-auto-merge.yml | 61 +++++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 490e982..1b46d3b 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -32,11 +32,62 @@ jobs: # Branch updates add non-Dependabot merge commits; still parse Dependabot metadata. skip-commit-verification: true - # Fail closed: only allowlisted minor/patch types. Empty/unknown update-type must not - # approve or merge (e.g. if metadata is missing after an unusual branch history). - # Does not check out or execute PR code — only calls the GitHub API. + # Decide eligibility without checking out PR code (GitHub API only). + # fetch-metadata often leaves update-type empty for pip requirement-range bumps + # ("Update X from >=a to >=b in /dir"), so fall back to title/group heuristics. + - name: Determine eligibility + id: eligible + env: + UPDATE_TYPE: ${{ steps.meta.outputs.update-type }} + DEPENDENCY_GROUP: ${{ steps.meta.outputs.dependency-group }} + PR: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + + allow=false + reason="blocked" + + case "$UPDATE_TYPE" in + version-update:semver-major) + reason="major-update-type" + ;; + version-update:semver-minor|version-update:semver-patch) + allow=true + reason="update-type:$UPDATE_TYPE" + ;; + *) + # Empty/unknown: allow known non-major Dependabot groups. + case "$DEPENDENCY_GROUP" in + prod-minor-patch|dev-all) + allow=true + reason="dependency-group:$DEPENDENCY_GROUP" + ;; + *) + title="$(gh pr view "$PR" --json title -q .title)" + # Match "from [>=~^]*X.Y.Z to [>=~^]*A.B.C", ignoring trailing " in /dir". + if [[ "$title" =~ from[[:space:]]+[^0-9v]*([0-9]+)\.([0-9]+)[^[:space:]]*[[:space:]]+to[[:space:]]+[^0-9v]*([0-9]+)\.([0-9]+) ]]; then + from_major="${BASH_REMATCH[1]}" + to_major="${BASH_REMATCH[3]}" + if [[ "$from_major" == "$to_major" ]]; then + allow=true + reason="title-non-major:$from_major->$to_major" + else + reason="title-major:$from_major->$to_major" + fi + else + reason="unclassified-update-type" + fi + ;; + esac + ;; + esac + + echo "eligibility allow=$allow reason=$reason update_type=${UPDATE_TYPE:-} group=${DEPENDENCY_GROUP:-}" + echo "allow=$allow" >> "$GITHUB_OUTPUT" + - name: Approve pull request - if: contains(fromJSON('["version-update:semver-minor","version-update:semver-patch"]'), steps.meta.outputs.update-type) + if: steps.eligible.outputs.allow == 'true' run: | set -euo pipefail if [ "$(gh pr view "$PR" --json reviewDecision -q .reviewDecision)" != "APPROVED" ]; then @@ -47,7 +98,7 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Enable auto-merge - if: contains(fromJSON('["version-update:semver-minor","version-update:semver-patch"]'), steps.meta.outputs.update-type) + if: steps.eligible.outputs.allow == 'true' run: | set -euo pipefail if [ "$(gh pr view "$PR" --json autoMergeRequest -q '.autoMergeRequest != null')" != "true" ]; then From 886bdcfd12df9a39f72a3b4e7222bb0c074e22cd Mon Sep 17 00:00:00 2001 From: Cursor Date: Fri, 4 Sep 2026 12:43:10 +0000 Subject: [PATCH 2/3] Fail closed on empty update-type for dev-all group dev-all includes majors, so do not auto-approve from the group name alone when update-type is missing. Only prod-minor-patch is trusted as a group shortcut; everything else requires same-major title parsing. Co-authored-by: ProxyMesh AI --- .github/workflows/dependabot-auto-merge.yml | 45 +++++++++++---------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 1b46d3b..b46a55a 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -34,7 +34,9 @@ jobs: # Decide eligibility without checking out PR code (GitHub API only). # fetch-metadata often leaves update-type empty for pip requirement-range bumps - # ("Update X from >=a to >=b in /dir"), so fall back to title/group heuristics. + # ("Update X from >=a to >=b in /dir"). Fall back to prod-minor-patch group + # membership, otherwise same-major title parsing. Never short-circuit on + # dev-all (that group includes majors). - name: Determine eligibility id: eligible env: @@ -57,29 +59,28 @@ jobs: reason="update-type:$UPDATE_TYPE" ;; *) - # Empty/unknown: allow known non-major Dependabot groups. - case "$DEPENDENCY_GROUP" in - prod-minor-patch|dev-all) - allow=true - reason="dependency-group:$DEPENDENCY_GROUP" - ;; - *) - title="$(gh pr view "$PR" --json title -q .title)" - # Match "from [>=~^]*X.Y.Z to [>=~^]*A.B.C", ignoring trailing " in /dir". - if [[ "$title" =~ from[[:space:]]+[^0-9v]*([0-9]+)\.([0-9]+)[^[:space:]]*[[:space:]]+to[[:space:]]+[^0-9v]*([0-9]+)\.([0-9]+) ]]; then - from_major="${BASH_REMATCH[1]}" - to_major="${BASH_REMATCH[3]}" - if [[ "$from_major" == "$to_major" ]]; then - allow=true - reason="title-non-major:$from_major->$to_major" - else - reason="title-major:$from_major->$to_major" - fi + # Empty/unknown: only trust groups that exclude majors. `dev-all` + # includes majors, so never short-circuit on that group name — + # fall through to title parsing instead. + if [[ "$DEPENDENCY_GROUP" == "prod-minor-patch" ]]; then + allow=true + reason="dependency-group:$DEPENDENCY_GROUP" + else + title="$(gh pr view "$PR" --json title -q .title)" + # Match "from [>=~^]*X.Y.Z to [>=~^]*A.B.C", ignoring trailing " in /dir". + if [[ "$title" =~ from[[:space:]]+[^0-9v]*([0-9]+)\.([0-9]+)[^[:space:]]*[[:space:]]+to[[:space:]]+[^0-9v]*([0-9]+)\.([0-9]+) ]]; then + from_major="${BASH_REMATCH[1]}" + to_major="${BASH_REMATCH[3]}" + if [[ "$from_major" == "$to_major" ]]; then + allow=true + reason="title-non-major:$from_major->$to_major" else - reason="unclassified-update-type" + reason="title-major:$from_major->$to_major" fi - ;; - esac + else + reason="unclassified-update-type" + fi + fi ;; esac From 5d2ff003627870334f2df5ba1ca6757d8574ef35 Mon Sep 17 00:00:00 2001 From: Cursor Date: Fri, 4 Sep 2026 12:53:49 +0000 Subject: [PATCH 3/3] Simplify Dependabot auto-merge without title parsing Bump dependabot/fetch-metadata to v3.1.0 so pip/composer requirement bumps populate update-type again. Split dev-all into dev-minor-patch (auto) and dev-major (manual) so empty update-type can trust group membership. Drop custom title parsing from the workflow. Co-authored-by: ProxyMesh AI --- .github/dependabot.yml | 24 +++++++++-- .github/workflows/dependabot-auto-merge.yml | 44 +++++++-------------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 027a933..a83f641 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -17,11 +17,15 @@ updates: update-types: - "minor" - "patch" - dev-all: + # Majors stay out of auto-merge groups so group membership alone is a safe signal. + dev-minor-patch: dependency-type: "development" update-types: - "minor" - "patch" + dev-major: + dependency-type: "development" + update-types: - "major" - package-ecosystem: "pip" @@ -41,11 +45,15 @@ updates: update-types: - "minor" - "patch" - dev-all: + # Majors stay out of auto-merge groups so group membership alone is a safe signal. + dev-minor-patch: dependency-type: "development" update-types: - "minor" - "patch" + dev-major: + dependency-type: "development" + update-types: - "major" - package-ecosystem: "bundler" @@ -65,11 +73,15 @@ updates: update-types: - "minor" - "patch" - dev-all: + # Majors stay out of auto-merge groups so group membership alone is a safe signal. + dev-minor-patch: dependency-type: "development" update-types: - "minor" - "patch" + dev-major: + dependency-type: "development" + update-types: - "major" - package-ecosystem: "composer" @@ -89,11 +101,15 @@ updates: update-types: - "minor" - "patch" - dev-all: + # Majors stay out of auto-merge groups so group membership alone is a safe signal. + dev-minor-patch: dependency-type: "development" update-types: - "minor" - "patch" + dev-major: + dependency-type: "development" + update-types: - "major" - package-ecosystem: "github-actions" diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index b46a55a..2326a7e 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -26,24 +26,23 @@ jobs: github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest steps: - - uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0 + # v3.1.0+ restores update-type for pip/composer requirement bumps (no custom title parsing). + - uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 id: meta with: # Branch updates add non-Dependabot merge commits; still parse Dependabot metadata. skip-commit-verification: true - # Decide eligibility without checking out PR code (GitHub API only). - # fetch-metadata often leaves update-type empty for pip requirement-range bumps - # ("Update X from >=a to >=b in /dir"). Fall back to prod-minor-patch group - # membership, otherwise same-major title parsing. Never short-circuit on - # dev-all (that group includes majors). + # Eligibility uses only fetch-metadata outputs (no PR code checkout, no title parsing): + # 1. Block explicit majors + # 2. Allow explicit minor/patch + # 3. If update-type is empty: allow only groups that exclude majors + # 4. Otherwise fail closed - name: Determine eligibility id: eligible env: UPDATE_TYPE: ${{ steps.meta.outputs.update-type }} DEPENDENCY_GROUP: ${{ steps.meta.outputs.dependency-group }} - PR: ${{ github.event.pull_request.html_url }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail @@ -59,28 +58,15 @@ jobs: reason="update-type:$UPDATE_TYPE" ;; *) - # Empty/unknown: only trust groups that exclude majors. `dev-all` - # includes majors, so never short-circuit on that group name — - # fall through to title parsing instead. - if [[ "$DEPENDENCY_GROUP" == "prod-minor-patch" ]]; then - allow=true - reason="dependency-group:$DEPENDENCY_GROUP" - else - title="$(gh pr view "$PR" --json title -q .title)" - # Match "from [>=~^]*X.Y.Z to [>=~^]*A.B.C", ignoring trailing " in /dir". - if [[ "$title" =~ from[[:space:]]+[^0-9v]*([0-9]+)\.([0-9]+)[^[:space:]]*[[:space:]]+to[[:space:]]+[^0-9v]*([0-9]+)\.([0-9]+) ]]; then - from_major="${BASH_REMATCH[1]}" - to_major="${BASH_REMATCH[3]}" - if [[ "$from_major" == "$to_major" ]]; then - allow=true - reason="title-non-major:$from_major->$to_major" - else - reason="title-major:$from_major->$to_major" - fi - else + case "$DEPENDENCY_GROUP" in + prod-minor-patch|dev-minor-patch) + allow=true + reason="dependency-group:$DEPENDENCY_GROUP" + ;; + *) reason="unclassified-update-type" - fi - fi + ;; + esac ;; esac