From 9d18459df3761ed6d2743da9669e7ebc29f50eb1 Mon Sep 17 00:00:00 2001 From: Douglas Baggett Date: Fri, 18 Sep 2026 21:18:01 -0400 Subject: [PATCH] fix: gate GitHub Actions minor and patch updates Require human review for GitHub Actions minor and patch updates while preserving digest and pin automerge. Keep first-party actions behind the same review gate because the issue targets the github-actions manager and no approved exception exists. Closes #1074.\n\nAssisted-by: GPT-5.6-Luna via GitHub Copilot\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Justfile | 2 +- .../references/automerge-and-rulesets.md | 21 ++++++++--- renovate.json | 11 ++++++ tests/test_renovate_config.py | 36 +++++++++++++++++++ 4 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 tests/test_renovate_config.py diff --git a/Justfile b/Justfile index f5fcdb4d1..95e79be5d 100644 --- a/Justfile +++ b/Justfile @@ -3,7 +3,7 @@ just := just_executable() # Run unit tests (pytest for hooks.py, bats for shell scripts) # test_libvirt_helper.bats is excluded — requires a running libvirtd session test: - python3 -m pytest tests/test_hooks.py tests/test_check_oci_refs.py tests/test_bazaar_hook.py tests/test_curated_config.py tests/test_skill_docs.py tests/test_chairlift_config.py -v --cov=tests --cov-report=term-missing + python3 -m pytest tests/test_hooks.py tests/test_check_oci_refs.py tests/test_bazaar_hook.py tests/test_curated_config.py tests/test_skill_docs.py tests/test_chairlift_config.py tests/test_renovate_config.py -v --cov=tests --cov-report=term-missing bats tests/test_libsetup.bats bats tests/test_setup_scripts.bats bats tests/test_privileged_setup.bats diff --git a/docs/skills/ci-pitfalls/references/automerge-and-rulesets.md b/docs/skills/ci-pitfalls/references/automerge-and-rulesets.md index 1abc77070..12e62e49a 100644 --- a/docs/skills/ci-pitfalls/references/automerge-and-rulesets.md +++ b/docs/skills/ci-pitfalls/references/automerge-and-rulesets.md @@ -6,11 +6,14 @@ Part of [ci-pitfalls](../SKILL.md) — Renovate automerge mechanics, the merge-q ## Renovate automerge — how it works in `common` - +Renovate applies every matching `packageRules` entry in order; later entries override +earlier values for the same option. Source: [Renovate packageRules +documentation](https://docs.renovatebot.com/configuration-options/#packagerules). -`common` uses `platformAutomerge: true` in `renovate.json`. Renovate calls GitHub's native -auto-merge API when it opens an eligible PR (digest/pin/patch/minor). GitHub's auto-merge -enqueues the PR into the merge queue once all required checks pass — no separate workflow needed. +`common` uses `platformAutomerge: true` in `renovate.json`. For updates whose package +rules enable automerge, Renovate calls GitHub's native auto-merge API. GitHub's +auto-merge enqueues the PR into the merge queue once all required checks pass — no +separate workflow needed. **Why `platformAutomerge` instead of a workflow:** `common/main` has a merge queue ruleset. `github-actions[bot]` cannot bypass the merge queue, so any workflow attempting a direct @@ -18,7 +21,15 @@ enqueues the PR into the merge queue once all required checks pass — no separa PR review ruleset (actor_id 2740, bypass_mode: pull_request) and uses GitHub's own auto-merge API, which the merge queue respects natively. -**Eligible update types:** `digest`, `pin`, `patch`, `minor`. Major bumps require human review. +**Eligible update types:** `digest` and `pin` updates automerge for all managers. `patch` +and `minor` updates automerge for non-`github-actions` managers; GitHub Actions +`patch`/`minor` updates require human review, including `projectbluefin/actions`. +Major bumps require human review. + +The inherited `github-actions (non-major)` group remains intact. Renovate only enables +automerge for a grouped branch when every upgrade in that branch is automerge-eligible; +therefore a mixed digest plus minor/patch group waits for human review as a whole. +Digest-only groups continue to automerge. **Bypass actors in the PR review ruleset:** - OrganizationAdmin — `bypass_mode: always` diff --git a/renovate.json b/renovate.json index 97d76eebf..7f56a9b3e 100644 --- a/renovate.json +++ b/renovate.json @@ -14,6 +14,17 @@ ], "automerge": true, "platformAutomerge": true + }, + { + "description": "Require human review for GitHub Actions minor/patch updates", + "matchManagers": [ + "github-actions" + ], + "matchUpdateTypes": [ + "minor", + "patch" + ], + "automerge": false } ] } diff --git a/tests/test_renovate_config.py b/tests/test_renovate_config.py new file mode 100644 index 000000000..2873ae52f --- /dev/null +++ b/tests/test_renovate_config.py @@ -0,0 +1,36 @@ +"""Regression checks for the repository Renovate policy.""" + +import json +from pathlib import Path + + +ROOT = Path(__file__).parent.parent +RENOVATE = ROOT / "renovate.json" + + +def _load_config(): + return json.loads(RENOVATE.read_text(encoding="utf-8")) + + +def test_github_actions_minor_patch_updates_require_review(): + config = _load_config() + rules = config["packageRules"] + + blanket_rule = next( + rule + for rule in rules + if set(rule.get("matchUpdateTypes", [])) + == {"digest", "pin", "patch", "minor"} + and "matchManagers" not in rule + ) + review_rule = next( + rule + for rule in rules + if rule.get("matchManagers") == ["github-actions"] + and set(rule.get("matchUpdateTypes", [])) == {"minor", "patch"} + ) + + assert rules.index(review_rule) > rules.index(blanket_rule) + assert blanket_rule["automerge"] is True + assert blanket_rule["platformAutomerge"] is True + assert review_rule["automerge"] is False