mirror of
https://github.com/spantaleev/matrix-docker-ansible-deploy.git
synced 2026-09-02 22:13:13 +00:00
Automerge minor bumps for newly covered roles
This commit is contained in:
@@ -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", "")]
|
||||
|
||||
if len(rules) != 1:
|
||||
print(f"Expected exactly one rule mentioning {MARKER}, found {len(rules)}.", file=sys.stderr)
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
automerged_without_scenario = sorted(listed - with_scenario)
|
||||
scenario_without_automerge = sorted(with_scenario - listed)
|
||||
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(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__":
|
||||
|
||||
Reference in New Issue
Block a user