Quellcode durchsuchen

Automerge minor bumps for newly covered roles

master
Slavi Pantaleev vor 1 Tag
Ursprung
Commit
b8145c2806
4 geänderte Dateien mit 92 neuen und 32 gelöschten Zeilen
  1. +14
    -1
      .github/renovate.json
  2. +2
    -2
      .pre-commit-config.yaml
  3. +72
    -23
      bin/check-molecule-automerge-list.py
  4. +4
    -6
      docs/molecule-testing.md

+ 14
- 1
.github/renovate.json Datei anzeigen

@@ -119,7 +119,7 @@
"automergeType": "branch"
},
{
"description": "Automerge patch bumps of components whose role has a Molecule scenario (via branch push - no PR). A bump touches that role's defaults/main.yml, which makes the Molecule workflow run that role's scenario, so the update only merges once the component has been started on the configuration the role renders. Patch only: a minor carries behaviour changes no static rule can judge, and reading the release notes to decide is a job for a human or an agent. Keep this list in step with the roles that have a scenario - bin/check-molecule-automerge-list.py enforces that.",
"description": "Automerge patch bumps of components whose role has a Molecule scenario (via branch push - no PR). A bump touches that role's defaults/main.yml, which makes the Molecule workflow run that role's scenario, so the update only merges once the component has been started on the configuration the role renders. Patch is the baseline for every covered role; explicitly approved roles may also appear in the narrower minor rule below. Keep this list in step with the roles that have a scenario - bin/check-molecule-automerge-list.py (patch rule) enforces that.",
"matchFileNames": [
"roles/custom/matrix-alertmanager-receiver/defaults/main.yml",
"roles/custom/matrix-authentication-service/defaults/main.yml",
@@ -147,6 +147,19 @@
],
"automerge": true,
"automergeType": "branch"
},
{
"description": "Automerge minor bumps of explicitly approved components with a weight-bearing Molecule scenario (via branch push - no PR). This list is intentionally narrower than the patch rule: the scenario must exercise enough real behavior to be a useful compatibility gate across a minor release. A failure surfaces as a PR instead. Keep every entry backed by a scenario and the patch rule - bin/check-molecule-automerge-list.py (minor rule) enforces that.",
"matchFileNames": [
"roles/custom/matrix-authentication-service/defaults/main.yml",
"roles/custom/matrix-static-files/defaults/main.yml",
"roles/custom/matrix-synapse-auto-compressor/defaults/main.yml"
],
"matchUpdateTypes": [
"minor"
],
"automerge": true,
"automergeType": "branch"
}
],
"pre-commit": {


+ 2
- 2
.pre-commit-config.yaml Datei anzeigen

@@ -39,8 +39,8 @@ repos:
files: '(examples/vars\.yml|roles/custom/matrix_playbook_migration/defaults/main\.yml)'
pass_filenames: false
- id: check-molecule-automerge-list
name: Check the Molecule automerge list matches the roles that have a scenario
name: Check the Molecule automerge rules match the roles that have a scenario
entry: bin/check-molecule-automerge-list.py
language: script
files: '(\.github/renovate\.json|roles/custom/[^/]+/molecule/default/molecule\.yml)'
files: '(\.github/renovate\.json|bin/check-molecule-automerge-list\.py|roles/custom/[^/]+/molecule/default/molecule\.yml)'
pass_filenames: false

+ 72
- 23
bin/check-molecule-automerge-list.py Datei anzeigen

@@ -1,14 +1,12 @@
#!/usr/bin/env python3
"""Keeps the Molecule patch-automerge rule in step with the roles that have a scenario.
"""Keeps Molecule-backed automerge rules in step with the available scenarios.

.github/renovate.json automerges patch bumps for roles listed by file name, on the grounds that
the bump runs that role's Molecule scenario before merging. That reasoning only holds while the
role actually has one.
.github/renovate.json automerges patch bumps for every role with a scenario. A narrower list of
explicitly approved roles also automerges minor bumps. In both cases, the bump runs that role's
Molecule scenario before merging, so the reasoning only holds while the role actually has one.

The dangerous direction is a role keeping automerge after losing its scenario: bumps would then
merge with nothing exercising them. The harmless direction - a role gaining a scenario without
being added - only means a missed opportunity, but it is reported too, since it is usually an
oversight rather than a decision.
The patch list must exactly match the scenarios. The minor list must be a subset of it: omission is
an explicit policy choice, while an extra entry would merge a minor bump without the required gate.
"""

import json
@@ -17,7 +15,28 @@ import sys

REPO = pathlib.Path(__file__).resolve().parent.parent
RENOVATE = REPO / ".github" / "renovate.json"
MARKER = "bin/check-molecule-automerge-list.py"
PATCH_RULE_MARKER = "bin/check-molecule-automerge-list.py (patch rule)"
MINOR_RULE_MARKER = "bin/check-molecule-automerge-list.py (minor rule)"


def find_rule(config: dict, marker: str) -> dict | None:
rules = [r for r in config.get("packageRules", []) if marker in r.get("description", "")]
if len(rules) != 1:
print(f"Expected exactly one rule mentioning {marker}, found {len(rules)}.", file=sys.stderr)
return None
return rules[0]


def listed_roles(rule: dict, label: str) -> set[str] | None:
listed = set()
for name in rule.get("matchFileNames", []):
parts = pathlib.PurePosixPath(name).parts
if parts[:2] == ("roles", "custom") and parts[3:] == ("defaults", "main.yml"):
listed.add(parts[2])
else:
print(f"Unexpected entry in the Molecule {label} automerge rule: {name}", file=sys.stderr)
return None
return listed


def main() -> int:
@@ -26,23 +45,26 @@ def main() -> int:
}

config = json.loads(RENOVATE.read_text())
rules = [r for r in config.get("packageRules", []) if MARKER in r.get("description", "")]
patch_rule = find_rule(config, PATCH_RULE_MARKER)
minor_rule = find_rule(config, MINOR_RULE_MARKER)
if patch_rule is None or minor_rule is None:
return 1

if len(rules) != 1:
print(f"Expected exactly one rule mentioning {MARKER}, found {len(rules)}.", file=sys.stderr)
patch_roles = listed_roles(patch_rule, "patch")
minor_roles = listed_roles(minor_rule, "minor")
if patch_roles is None or minor_roles is None:
return 1

listed = set()
for name in rules[0].get("matchFileNames", []):
parts = pathlib.PurePosixPath(name).parts
if parts[:2] == ("roles", "custom") and parts[3:] == ("defaults", "main.yml"):
listed.add(parts[2])
else:
print(f"Unexpected entry in the automerge rule: {name}", file=sys.stderr)
return 1
errors = False
if set(patch_rule.get("matchUpdateTypes", [])) != {"patch"}:
print("The Molecule patch automerge rule must match only patch updates.", file=sys.stderr)
errors = True
if set(minor_rule.get("matchUpdateTypes", [])) != {"minor"}:
print("The Molecule minor automerge rule must match only minor updates.", file=sys.stderr)
errors = True

automerged_without_scenario = sorted(listed - with_scenario)
scenario_without_automerge = sorted(with_scenario - listed)
automerged_without_scenario = sorted(patch_roles - with_scenario)
scenario_without_automerge = sorted(with_scenario - patch_roles)

if automerged_without_scenario:
print(
@@ -63,7 +85,34 @@ def main() -> int:
for role in scenario_without_automerge:
print(f" {role}", file=sys.stderr)

return 1 if (automerged_without_scenario or scenario_without_automerge) else 0
minor_without_scenario = sorted(minor_roles - with_scenario)
minor_without_patch = sorted(minor_roles - patch_roles)

if minor_without_scenario:
print(
"These roles automerge minor bumps but have no Molecule scenario:",
file=sys.stderr,
)
for role in minor_without_scenario:
print(f" {role}", file=sys.stderr)

if minor_without_patch:
print(
"These roles automerge minor bumps but are missing from the patch rule:",
file=sys.stderr,
)
for role in minor_without_patch:
print(f" {role}", file=sys.stderr)

return 1 if any(
[
errors,
automerged_without_scenario,
scenario_without_automerge,
minor_without_scenario,
minor_without_patch,
]
) else 0


if __name__ == "__main__":


+ 4
- 6
docs/molecule-testing.md Datei anzeigen

@@ -32,13 +32,11 @@ When the diff base cannot be determined (a new branch, a force push), it falls b

## Automerge

A role that has a scenario is listed in the Molecule automerge rule in `.github/renovate.json`, so
patch bumps of its component merge on their own once the scenario has passed on them.
A role that has a scenario is listed in the Molecule patch-automerge rule in `.github/renovate.json`, so patch bumps of its component merge on their own once the scenario has passed on them. Some roles with a sufficiently weight-bearing compatibility gate are explicitly approved in a narrower minor-automerge rule too. Major updates are never included in these rules.

**Add your role to that list when you add its scenario.** `bin/check-molecule-automerge-list.py`
runs from prek and fails the commit if the list and the scenarios have drifted apart. The direction
that matters is a role staying in the list after losing its scenario, since its bumps would then
merge with nothing exercising them.
Both rules use branch automerge: an update that passes its scenario merges without opening a pull request, while a failure surfaces as a pull request instead.

**Add your role to the patch list when you add its scenario.** Add it to the minor list only when that broader policy has been explicitly approved and the scenario exercises enough real behavior to serve as a minor-release compatibility gate. `bin/check-molecule-automerge-list.py` runs from prek and fails the commit if the patch list and scenarios drift apart, or if a minor-automerge entry is not backed by both a scenario and the patch rule.

## Writing a scenario



Laden…
Abbrechen
Speichern