Skip to content

Commit 77b5ce0

Browse files
anth-volkclaude
andcommitted
Attribute ci-scripts findings to enforced rules
Sync scripts/rules with policyengine-api. Once a CI-script rule is enforced it has no baseline, so its findings were reported as [registry]; fall back to the rule whose id ends in the check's name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 7350a56 commit 77b5ce0

3 files changed

Lines changed: 38 additions & 5 deletions

File tree

‎docs/generated/rules.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Rules
22

3-
Generated by `python -m scripts.rules render` (scripts/rules v0.1.0, tool sha256 `afc55ba219f1`) from `rules/registry.toml`. Do not edit by hand.
3+
Generated by `python -m scripts.rules render` (scripts/rules v0.1.0, tool sha256 `86b191f2ebb3`) from `rules/registry.toml`. Do not edit by hand.
44

55
Every rule that governs `policyengine-sim-api` is listed here with how it is enforced. Blocking rules fail CI. The Gaps section lists what is written down but not yet enforced or proven. To add or change a rule, see `docs/engineering/skills/rules-registry.md`.
66

‎scripts/rules/ci_scripts.py‎

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@
2525

2626
REPO_ROOT = Path(__file__).resolve().parents[2]
2727
SEPARATOR = " :: "
28+
# Rule ids end with these names (``<repo>.ci.<name>``). Used to attribute a
29+
# finding to its rule once the rule is enforced and has no baseline left.
30+
RULE_NAMES = {
31+
"multiline": "multiline-run-is-script",
32+
"untested": "workflow-scripts-tested",
33+
"shellcheck": "shell-scripts-pass-shellcheck",
34+
}
2835

2936

3037
@dataclass
@@ -118,10 +125,15 @@ def _baseline(root: Path, path: str) -> set[str]:
118125
}
119126

120127

121-
def _rule_for(registry: Registry, baseline: str) -> str | None:
128+
def _rule_for(registry: Registry, baseline: str, check: str) -> str | None:
122129
for rule in registry.rules:
123130
if rule.baseline is not None and rule.baseline.file == baseline:
124131
return rule.id
132+
for rule in registry.rules:
133+
if rule.enforcer.get("name") == "ci-scripts" and rule.id.endswith(
134+
f".ci.{RULE_NAMES[check]}"
135+
):
136+
return rule.id
125137
return None
126138

127139

@@ -130,30 +142,33 @@ def compare(registry: Registry, findings: Findings, root: Path) -> list[Violatio
130142
assert config is not None
131143
checks = (
132144
(
145+
"multiline",
133146
"multi-line run: block",
134147
findings.multiline,
135148
config.multiline_baseline,
136149
"move the commands into a tested script under "
137150
f"{config.scripts_dir} and call it from a single-line run:",
138151
),
139152
(
153+
"untested",
140154
"untested workflow script",
141155
findings.untested,
142156
config.untested_baseline,
143157
"add a test that runs the script (see docs/engineering/skills/ci-workflows.md)",
144158
),
145159
(
160+
"shellcheck",
146161
"shellcheck finding in",
147162
findings.shellcheck,
148163
config.shellcheck_baseline,
149164
"fix what `shellcheck <script>` reports",
150165
),
151166
)
152167
violations = []
153-
for label, found, baseline_path, fix in checks:
168+
for check, label, found, baseline_path, fix in checks:
154169
if found is None:
155170
continue
156-
rule_id = _rule_for(registry, baseline_path)
171+
rule_id = _rule_for(registry, baseline_path, check)
157172
baseline = _baseline(root, baseline_path)
158173
for entry in sorted(found - baseline):
159174
violations.append(Violation(rule_id, "ci-scripts", f"{label} {entry}", fix))
@@ -199,7 +214,9 @@ def run_check(root: Path = REPO_ROOT) -> tuple[list[Violation], list[str]]:
199214
if findings.shellcheck is None and os.environ.get("GITHUB_ACTIONS"):
200215
violations.append(
201216
Violation(
202-
_rule_for(registry, registry.ci_scripts.shellcheck_baseline),
217+
_rule_for(
218+
registry, registry.ci_scripts.shellcheck_baseline, "shellcheck"
219+
),
203220
"ci-scripts",
204221
"shellcheck is not installed in this CI job",
205222
"install it in the job (pip install shellcheck-py==<version>)",

‎scripts/rules/tests/test_ci_scripts.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,22 @@ def test_new_finding_fails_with_its_rule_id(self) -> None:
189189
messages[0],
190190
)
191191

192+
def test_enforced_rule_without_baseline_keeps_its_id(self) -> None:
193+
findings = Findings(multiline=set(), untested={"x.sh"}, shellcheck=set())
194+
registry, _ = load_text(
195+
REGISTRY.replace("ex.ci.multiline", "ex.ci.workflow-scripts-tested")
196+
.replace('status = "ratcheted"', 'status = "enforced"')
197+
.replace(
198+
'[rule.baseline]\nfile = "rules/baselines/ci-multiline-run.txt"\ncount = 1\n',
199+
"",
200+
)
201+
)
202+
assert registry is not None
203+
messages = [str(v) for v in compare(registry, findings, self.root)]
204+
self.assertTrue(
205+
messages[-1].startswith("[ex.ci.workflow-scripts-tested]"), messages
206+
)
207+
192208
def test_fixed_finding_must_leave_the_baseline(self) -> None:
193209
messages = self.messages(set())
194210
self.assertIn(

0 commit comments

Comments
 (0)