-
Notifications
You must be signed in to change notification settings - Fork 1
chore(ci): fail CI when defaults/main.yml drifts from argument_specs.yml #186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dd5070d
6ffecfc
13fd6e7
81ef686
e4d75fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -95,9 +95,14 @@ jobs: | |
| # the shared incus-ci host. The memory-capacity gate in shared/create.yml | ||
| # eventually admits every job, but without a matrix-level cap the full | ||
| # 16-combo PR matrix would swamp the 131 GB host and starve the biggest | ||
| # scenarios (elasticstack_default, cert_renewal). 6 concurrent slots keep | ||
| # committed memory around 90-100 GB with head-room to spare. | ||
| max-parallel: 6 | ||
| # scenarios (elasticstack_default, cert_renewal). 6 concurrent slots was | ||
| # the previous target, sized for the intended 90-100 GB steady state. | ||
| # Observed reality after landing several PRs concurrently: with all six | ||
| # slots filled, the 20 GB + 13.8 GB scenarios still lose the memory | ||
| # race and time out at the 45-min gate deadline. Halving to 3 keeps | ||
| # committed memory well under host capacity and lets the heavy jobs | ||
| # actually acquire without starvation. Wall clock roughly doubles. | ||
| max-parallel: 3 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift Use the reusable Molecule workflow for this matrix. This job defines the matrix and invokes Molecule inline, while As per path instructions, “Molecule test workflows should use the reusable molecule.yml workflow.” 🤖 Prompt for AI AgentsSource: Path instructions |
||
| matrix: | ||
| # Standardise on rockylinux10 (not 9) for PR runs to match the rest | ||
| # of the workflows. Rocky 10 exercises the EL≥9 branch in | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| #!/usr/bin/env python3 | ||
| """ | ||
| Verify that every role's meta/argument_specs.yml lists the same variables | ||
| its defaults/main.yml defines. Fails CI when the two drift. | ||
|
|
||
| Runs across every role that has a meta/argument_specs.yml. Roles without | ||
| one (e.g. `repos`, which uses variables from `elasticstack` instead) are | ||
| skipped intentionally. | ||
|
|
||
| Exit codes: | ||
| 0 All roles in sync | ||
| 1 At least one role has drift (details on stderr) | ||
| 2 Usage / setup error | ||
|
|
||
| The parser only looks at the top-level `varname: value` lines in | ||
| defaults/main.yml. Internal-only vars (leading underscore) are ignored | ||
| so vars/main.yml-style facts don't need arg-spec entries. | ||
| """ | ||
| import sys | ||
| import re | ||
| from pathlib import Path | ||
|
|
||
| try: | ||
| import yaml | ||
| except ImportError: | ||
| print("check_argspecs: PyYAML is required (pip install pyyaml)", file=sys.stderr) | ||
| sys.exit(2) | ||
|
|
||
|
|
||
| VAR_LINE = re.compile(r"^([A-Za-z][\w]*)\s*:") | ||
|
|
||
|
|
||
| def defaults_vars(path): | ||
| """Top-level variable names in a defaults/main.yml, minus internal underscore vars.""" | ||
| out = set() | ||
| with open(path) as f: | ||
| for line in f: | ||
| m = VAR_LINE.match(line) | ||
| if m: | ||
| name = m.group(1) | ||
| if not name.startswith("_"): | ||
| out.add(name) | ||
| return out | ||
|
|
||
|
|
||
| def argspec_options(path): | ||
| """Option names declared under the role's `main` entry point. | ||
|
|
||
| Task-file entry points (e.g. node_maintenance_start / _end) are | ||
| invoked with per-call parameters that don't need to appear in | ||
| defaults/main.yml — they're inputs to a specific action, not | ||
| role-wide defaults. Only `main`'s options are role-wide vars. | ||
| """ | ||
| with open(path) as f: | ||
| spec = yaml.safe_load(f) or {} | ||
| entries = spec.get("argument_specs") or {} | ||
| main = entries.get("main") or {} | ||
| return set((main.get("options") or {}).keys()) | ||
|
|
||
|
|
||
| def main(): | ||
| repo = Path(__file__).resolve().parent.parent | ||
| roles = sorted(p for p in (repo / "roles").iterdir() if p.is_dir()) | ||
| exit_code = 0 | ||
|
|
||
| for role in roles: | ||
| defaults = role / "defaults" / "main.yml" | ||
| specs = role / "meta" / "argument_specs.yml" | ||
| if not specs.exists(): | ||
| continue # role opts out | ||
| if not defaults.exists(): | ||
| print( | ||
| f"[{role.name}] has meta/argument_specs.yml but no defaults/main.yml", | ||
| file=sys.stderr, | ||
| ) | ||
| exit_code = 1 | ||
| continue | ||
|
|
||
| vars_ = defaults_vars(defaults) | ||
| opts = argspec_options(specs) | ||
| missing_in_spec = vars_ - opts | ||
| extra_in_spec = opts - vars_ | ||
|
|
||
| if missing_in_spec or extra_in_spec: | ||
| exit_code = 1 | ||
| print(f"\n[{role.name}] argument_specs drift:", file=sys.stderr) | ||
| for v in sorted(missing_in_spec): | ||
| print(f" - in defaults but missing from spec: {v}", file=sys.stderr) | ||
| for v in sorted(extra_in_spec): | ||
| print(f" - in spec but no longer in defaults: {v}", file=sys.stderr) | ||
| else: | ||
| print(f"[{role.name}] ok ({len(vars_)} vars)") | ||
|
|
||
| if exit_code: | ||
| print( | ||
| "\nFix by editing meta/argument_specs.yml or regenerating with " | ||
| "`scripts/gen_argspecs.py <role_path>`.", | ||
| file=sys.stderr, | ||
| ) | ||
| sys.exit(exit_code) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Correct the pull-request wall-clock estimate.
For
pull_requestandmerge_group, Line 111 selects two distributions and Line 120 selects one release. With the four scenarios on Lines 113-116, the matrix has eight jobs. Reducingmax-parallelfrom 6 to 3 changes the ideal wave count from two to three, or about 1.5×, not roughly 2×. Scope this estimate to the default 48-job matrix or state the event-specific impact.Proposed wording
📝 Committable suggestion
🤖 Prompt for AI Agents