From 60963c25690cf05883a5027c57aacc4ad3f3fec5 Mon Sep 17 00:00:00 2001 From: thekevinbot <248217931+thekevinbot@users.noreply.github.com> Date: Sat, 5 Sep 2026 07:24:51 -0400 Subject: [PATCH 1/3] refactor(checks): one function per file across attestation_guard and platforms_mirror Splits the last modules in internals/checks/src that carried more than one over-threshold module-scope function, taking the package to zero one-function-per-file violations at max_lines = 1. Each module keeps its named export and the private helpers move out, one function per module with a colocated unit test: - attestation_guard/gate.py keeps run; verdict moves to verdict.py - attestation_guard/git_ops.py splits into deleted_files.py and commit_messages.py - attestation_guard/report.py keeps report; the two annotation builders move to deletion_annotation.py and near_miss_annotation.py - platforms_mirror/gate.py keeps run; read moves to read.py - platforms_mirror/typescript_table.py keeps typescript_table; requoted, without_comments (with its token regex) and as_json move to their own modules Pure refactor -- no behavior change. --- .../attestation_guard/commit_messages.py | 21 ++++++++++ .../attestation_guard/commit_messages_test.py | 15 +++++++ .../{git_ops.py => deleted_files.py} | 16 +------- ...{git_ops_test.py => deleted_files_test.py} | 14 +------ .../attestation_guard/deletion_annotation.py | 11 +++++ .../deletion_annotation_test.py | 9 +++++ .../src/checks/attestation_guard/gate.py | 24 +++-------- .../src/checks/attestation_guard/gate_test.py | 21 +++++----- .../attestation_guard/near_miss_annotation.py | 11 +++++ .../near_miss_annotation_test.py | 10 +++++ .../src/checks/attestation_guard/report.py | 19 ++------- .../checks/attestation_guard/report_test.py | 24 +---------- .../src/checks/attestation_guard/verdict.py | 20 ++++++++++ .../checks/attestation_guard/verdict_test.py | 35 ++++++++++++++++ .../src/checks/platforms_mirror/as_json.py | 15 +++++++ .../checks/platforms_mirror/as_json_test.py | 28 +++++++++++++ .../src/checks/platforms_mirror/gate.py | 6 +-- .../src/checks/platforms_mirror/gate_test.py | 20 +++++----- .../src/checks/platforms_mirror/read.py | 12 ++++++ .../src/checks/platforms_mirror/read_test.py | 17 ++++++++ .../src/checks/platforms_mirror/requoted.py | 16 ++++++++ .../checks/platforms_mirror/requoted_test.py | 28 +++++++++++++ .../platforms_mirror/typescript_table.py | 34 ++-------------- .../platforms_mirror/typescript_table_test.py | 7 ++++ .../platforms_mirror/without_comments.py | 27 +++++++++++++ .../platforms_mirror/without_comments_test.py | 40 +++++++++++++++++++ .../integration/attestation_guard_test.py | 7 ++-- 27 files changed, 362 insertions(+), 145 deletions(-) create mode 100644 internals/checks/src/checks/attestation_guard/commit_messages.py create mode 100644 internals/checks/src/checks/attestation_guard/commit_messages_test.py rename internals/checks/src/checks/attestation_guard/{git_ops.py => deleted_files.py} (55%) rename internals/checks/src/checks/attestation_guard/{git_ops_test.py => deleted_files_test.py} (64%) create mode 100644 internals/checks/src/checks/attestation_guard/deletion_annotation.py create mode 100644 internals/checks/src/checks/attestation_guard/deletion_annotation_test.py create mode 100644 internals/checks/src/checks/attestation_guard/near_miss_annotation.py create mode 100644 internals/checks/src/checks/attestation_guard/near_miss_annotation_test.py create mode 100644 internals/checks/src/checks/attestation_guard/verdict.py create mode 100644 internals/checks/src/checks/attestation_guard/verdict_test.py create mode 100644 internals/checks/src/checks/platforms_mirror/as_json.py create mode 100644 internals/checks/src/checks/platforms_mirror/as_json_test.py create mode 100644 internals/checks/src/checks/platforms_mirror/read.py create mode 100644 internals/checks/src/checks/platforms_mirror/read_test.py create mode 100644 internals/checks/src/checks/platforms_mirror/requoted.py create mode 100644 internals/checks/src/checks/platforms_mirror/requoted_test.py create mode 100644 internals/checks/src/checks/platforms_mirror/without_comments.py create mode 100644 internals/checks/src/checks/platforms_mirror/without_comments_test.py diff --git a/internals/checks/src/checks/attestation_guard/commit_messages.py b/internals/checks/src/checks/attestation_guard/commit_messages.py new file mode 100644 index 000000000..e9d70cdd5 --- /dev/null +++ b/internals/checks/src/checks/attestation_guard/commit_messages.py @@ -0,0 +1,21 @@ +"""The commit bodies over a range, for the attestation-guard check (#1043). + +Shells out for exactly this one piece of data, so the decision logic in +`decide.py` stays subprocess-free and unit-testable. +""" + +from __future__ import annotations + +import subprocess + + +def commit_messages(base_sha: str, head_sha: str, runner=subprocess.run) -> str: + # Raw bodies (`%B`), scanned for the bypass line without git's trailer + # parser, so the bypass works from any line of any commit in the range. + result = runner( + ["git", "log", "--format=%B", f"{base_sha}..{head_sha}"], + capture_output=True, + text=True, + check=True, + ) + return result.stdout diff --git a/internals/checks/src/checks/attestation_guard/commit_messages_test.py b/internals/checks/src/checks/attestation_guard/commit_messages_test.py new file mode 100644 index 000000000..2a442f249 --- /dev/null +++ b/internals/checks/src/checks/attestation_guard/commit_messages_test.py @@ -0,0 +1,15 @@ +from unittest import mock + +from checks.attestation_guard.commit_messages import commit_messages + + +def describe_commit_messages(): + def returns_raw_bodies_via_the_two_dot_range(): + runner = mock.Mock(return_value=mock.Mock(stdout="fix: x\n")) + assert commit_messages("base", "head", runner=runner) == "fix: x\n" + runner.assert_called_once_with( + ["git", "log", "--format=%B", "base..head"], + capture_output=True, + text=True, + check=True, + ) diff --git a/internals/checks/src/checks/attestation_guard/git_ops.py b/internals/checks/src/checks/attestation_guard/deleted_files.py similarity index 55% rename from internals/checks/src/checks/attestation_guard/git_ops.py rename to internals/checks/src/checks/attestation_guard/deleted_files.py index d453de6cd..9a895c4d3 100644 --- a/internals/checks/src/checks/attestation_guard/git_ops.py +++ b/internals/checks/src/checks/attestation_guard/deleted_files.py @@ -1,6 +1,6 @@ -"""Thin git subprocess wrappers for the attestation-guard check (#1043). +"""The paths a range deletes, for the attestation-guard check (#1043). -Each shells out for exactly one piece of data, so the decision logic in +Shells out for exactly this one piece of data, so the decision logic in `decide.py` stays subprocess-free and unit-testable. """ @@ -21,15 +21,3 @@ def deleted_files(base_sha: str, head_sha: str, runner=subprocess.run) -> list[s check=True, ) return [line for line in result.stdout.splitlines() if line] - - -def commit_messages(base_sha: str, head_sha: str, runner=subprocess.run) -> str: - # Raw bodies (`%B`), scanned for the bypass line without git's trailer - # parser, so the bypass works from any line of any commit in the range. - result = runner( - ["git", "log", "--format=%B", f"{base_sha}..{head_sha}"], - capture_output=True, - text=True, - check=True, - ) - return result.stdout diff --git a/internals/checks/src/checks/attestation_guard/git_ops_test.py b/internals/checks/src/checks/attestation_guard/deleted_files_test.py similarity index 64% rename from internals/checks/src/checks/attestation_guard/git_ops_test.py rename to internals/checks/src/checks/attestation_guard/deleted_files_test.py index b621e990e..9689557dc 100644 --- a/internals/checks/src/checks/attestation_guard/git_ops_test.py +++ b/internals/checks/src/checks/attestation_guard/deleted_files_test.py @@ -1,6 +1,6 @@ from unittest import mock -from checks.attestation_guard.git_ops import commit_messages, deleted_files +from checks.attestation_guard.deleted_files import deleted_files def describe_deleted_files(): @@ -22,15 +22,3 @@ def parses_stdout_into_lines(): def drops_blank_lines(): runner = mock.Mock(return_value=mock.Mock(stdout="a.json\n\n")) assert deleted_files("base", "head", runner=runner) == ["a.json"] - - -def describe_commit_messages(): - def returns_raw_bodies_via_the_two_dot_range(): - runner = mock.Mock(return_value=mock.Mock(stdout="fix: x\n")) - assert commit_messages("base", "head", runner=runner) == "fix: x\n" - runner.assert_called_once_with( - ["git", "log", "--format=%B", "base..head"], - capture_output=True, - text=True, - check=True, - ) diff --git a/internals/checks/src/checks/attestation_guard/deletion_annotation.py b/internals/checks/src/checks/attestation_guard/deletion_annotation.py new file mode 100644 index 000000000..36a8fa826 --- /dev/null +++ b/internals/checks/src/checks/attestation_guard/deletion_annotation.py @@ -0,0 +1,11 @@ +"""The per-file annotation for one wrongly-deleted receipt (#1043).""" + +from __future__ import annotations + + +def deletion_annotation(path: str) -> str: + """The per-file annotation naming one wrongly-deleted receipt.""" + return ( + f"::error file={path}::{path} is an e2e attestation receipt. " + "Receipts are append-only; this PR deletes it." + ) diff --git a/internals/checks/src/checks/attestation_guard/deletion_annotation_test.py b/internals/checks/src/checks/attestation_guard/deletion_annotation_test.py new file mode 100644 index 000000000..8595b8ff5 --- /dev/null +++ b/internals/checks/src/checks/attestation_guard/deletion_annotation_test.py @@ -0,0 +1,9 @@ +from checks.attestation_guard.deletion_annotation import deletion_annotation + + +def describe_deletion_annotation(): + def names_the_file_and_the_append_only_rule(): + line = deletion_annotation("packages/ts/e2e-attestations/a.json") + assert line.startswith("::error file=packages/ts/e2e-attestations/a.json::") + assert "packages/ts/e2e-attestations/a.json is an e2e attestation receipt" in line + assert "Receipts are append-only; this PR deletes it." in line diff --git a/internals/checks/src/checks/attestation_guard/gate.py b/internals/checks/src/checks/attestation_guard/gate.py index 7b73f2384..2eedd284f 100644 --- a/internals/checks/src/checks/attestation_guard/gate.py +++ b/internals/checks/src/checks/attestation_guard/gate.py @@ -6,30 +6,18 @@ from __future__ import annotations -from checks.attestation_guard import git_ops -from checks.attestation_guard.decide import ( - deleted_receipts, - has_allow_line, - near_miss_lines, -) -from checks.attestation_guard.report import report - - -def verdict(deleted, messages: str, base_sha: str) -> int: - """0 when a well-formed bypass line is present, else 1 with diagnostics.""" - if has_allow_line(messages): - print("allow-receipt-deletion line present; permitting receipt deletion.") - return 0 - report(deleted, near_miss_lines(messages), base_sha) - return 1 +from checks.attestation_guard.commit_messages import commit_messages +from checks.attestation_guard.decide import deleted_receipts +from checks.attestation_guard.deleted_files import deleted_files +from checks.attestation_guard.verdict import verdict def run( base_sha: str, head_sha: str, *, - deleted_files=git_ops.deleted_files, - commit_messages=git_ops.commit_messages, + deleted_files=deleted_files, + commit_messages=commit_messages, ) -> int: deleted = deleted_receipts(deleted_files(base_sha, head_sha)) if not deleted: diff --git a/internals/checks/src/checks/attestation_guard/gate_test.py b/internals/checks/src/checks/attestation_guard/gate_test.py index d47634219..9c0dde243 100644 --- a/internals/checks/src/checks/attestation_guard/gate_test.py +++ b/internals/checks/src/checks/attestation_guard/gate_test.py @@ -1,8 +1,9 @@ +import inspect from unittest import mock import pytest -from checks.attestation_guard.gate import run, verdict +from checks.attestation_guard.gate import run def _run(deleted, *, messages="fix: a thing\n"): @@ -80,15 +81,11 @@ def reads_commit_messages_over_the_same_range(): messages.assert_called_once_with("b1", "h1") -def describe_verdict(): - def reports_every_deleted_receipt(capsys): - rc = verdict( - ["a/e2e-attestations/x.json", "b/e2e-attestations/y.json"], "fix: x", "sha" + def defaults_to_the_git_wrappers_each_in_its_own_module(): + defaults = inspect.signature(run).parameters + assert defaults["deleted_files"].default.__module__ == ( + "checks.attestation_guard.deleted_files" + ) + assert defaults["commit_messages"].default.__module__ == ( + "checks.attestation_guard.commit_messages" ) - assert rc == 1 - assert capsys.readouterr().out.count("::error file=") == 2 - - def does_not_report_when_bypassed(capsys): - rc = verdict(["a/e2e-attestations/x.json"], "allow-receipt-deletion: why", "sha") - assert rc == 0 - assert "::error file=" not in capsys.readouterr().out diff --git a/internals/checks/src/checks/attestation_guard/near_miss_annotation.py b/internals/checks/src/checks/attestation_guard/near_miss_annotation.py new file mode 100644 index 000000000..f3297aa2c --- /dev/null +++ b/internals/checks/src/checks/attestation_guard/near_miss_annotation.py @@ -0,0 +1,11 @@ +"""The annotation for a bypass line that was attempted but misspelled (#1043).""" + +from __future__ import annotations + + +def near_miss_annotation(line: str) -> str: + """The annotation naming a bypass line that was attempted but misspelled.""" + return ( + f"::error::{line!r} is not the bypass line. The exact form is " + "'allow-receipt-deletion: ', reason required." + ) diff --git a/internals/checks/src/checks/attestation_guard/near_miss_annotation_test.py b/internals/checks/src/checks/attestation_guard/near_miss_annotation_test.py new file mode 100644 index 000000000..72bd371d1 --- /dev/null +++ b/internals/checks/src/checks/attestation_guard/near_miss_annotation_test.py @@ -0,0 +1,10 @@ +from checks.attestation_guard.near_miss_annotation import near_miss_annotation + + +def describe_near_miss_annotation(): + def quotes_the_line_and_gives_the_exact_form(): + line = near_miss_annotation("skip-receipt-deletion: y") + assert line == ( + "::error::'skip-receipt-deletion: y' is not the bypass line. " + "The exact form is 'allow-receipt-deletion: ', reason required." + ) diff --git a/internals/checks/src/checks/attestation_guard/report.py b/internals/checks/src/checks/attestation_guard/report.py index 709147145..86c708ace 100644 --- a/internals/checks/src/checks/attestation_guard/report.py +++ b/internals/checks/src/checks/attestation_guard/report.py @@ -8,6 +8,9 @@ from __future__ import annotations +from checks.attestation_guard.deletion_annotation import deletion_annotation +from checks.attestation_guard.near_miss_annotation import near_miss_annotation + _ADVICE = ( "::error::A receipt records that another branch's e2e suite ran, and " "nothing can regenerate a merged branch's. Restore them and re-attest only " @@ -21,22 +24,6 @@ def restore_command(deleted, base_sha: str) -> str: return f"git checkout {base_sha} -- {' '.join(deleted)}" -def deletion_annotation(path: str) -> str: - """The per-file annotation naming one wrongly-deleted receipt.""" - return ( - f"::error file={path}::{path} is an e2e attestation receipt. " - "Receipts are append-only; this PR deletes it." - ) - - -def near_miss_annotation(line: str) -> str: - """The annotation naming a bypass line that was attempted but misspelled.""" - return ( - f"::error::{line!r} is not the bypass line. The exact form is " - "'allow-receipt-deletion: ', reason required." - ) - - def report(deleted, near_misses, base_sha: str) -> None: """Print one annotation per deleted receipt, then how to undo them.""" for path in deleted: diff --git a/internals/checks/src/checks/attestation_guard/report_test.py b/internals/checks/src/checks/attestation_guard/report_test.py index 0ddf7d4b2..548dc1785 100644 --- a/internals/checks/src/checks/attestation_guard/report_test.py +++ b/internals/checks/src/checks/attestation_guard/report_test.py @@ -1,9 +1,4 @@ -from checks.attestation_guard.report import ( - deletion_annotation, - near_miss_annotation, - report, - restore_command, -) +from checks.attestation_guard.report import report, restore_command def describe_restore_command(): @@ -13,23 +8,6 @@ def names_the_base_sha_and_every_path(): ) -def describe_deletion_annotation(): - def names_the_file_and_the_append_only_rule(): - line = deletion_annotation("packages/ts/e2e-attestations/a.json") - assert line.startswith("::error file=packages/ts/e2e-attestations/a.json::") - assert "packages/ts/e2e-attestations/a.json is an e2e attestation receipt" in line - assert "Receipts are append-only; this PR deletes it." in line - - -def describe_near_miss_annotation(): - def quotes_the_line_and_gives_the_exact_form(): - line = near_miss_annotation("skip-receipt-deletion: y") - assert line == ( - "::error::'skip-receipt-deletion: y' is not the bypass line. " - "The exact form is 'allow-receipt-deletion: ', reason required." - ) - - def describe_report(): def annotates_every_deleted_receipt_with_its_own_file(capsys): report(["packages/ts/e2e-attestations/a.json"], [], "abc123") diff --git a/internals/checks/src/checks/attestation_guard/verdict.py b/internals/checks/src/checks/attestation_guard/verdict.py new file mode 100644 index 000000000..101acfc3a --- /dev/null +++ b/internals/checks/src/checks/attestation_guard/verdict.py @@ -0,0 +1,20 @@ +"""The attestation-guard verdict once a receipt deletion is known (#1043). + +Split from the orchestration so the bypass decision reads on its own: the +deletion is already established here, and all that remains is whether a +well-formed bypass line excuses it. +""" + +from __future__ import annotations + +from checks.attestation_guard.decide import has_allow_line, near_miss_lines +from checks.attestation_guard.report import report + + +def verdict(deleted, messages: str, base_sha: str) -> int: + """0 when a well-formed bypass line is present, else 1 with diagnostics.""" + if has_allow_line(messages): + print("allow-receipt-deletion line present; permitting receipt deletion.") + return 0 + report(deleted, near_miss_lines(messages), base_sha) + return 1 diff --git a/internals/checks/src/checks/attestation_guard/verdict_test.py b/internals/checks/src/checks/attestation_guard/verdict_test.py new file mode 100644 index 000000000..833f50e85 --- /dev/null +++ b/internals/checks/src/checks/attestation_guard/verdict_test.py @@ -0,0 +1,35 @@ +from checks.attestation_guard.verdict import verdict + + +def describe_verdict(): + def reports_every_deleted_receipt(capsys): + rc = verdict( + ["a/e2e-attestations/x.json", "b/e2e-attestations/y.json"], "fix: x", "sha" + ) + assert rc == 1 + assert capsys.readouterr().out.count("::error file=") == 2 + + def names_the_restore_command_with_the_base_sha(capsys): + assert verdict(["a/e2e-attestations/x.json"], "fix: x", "abc123") == 1 + out = capsys.readouterr().out + assert "git checkout abc123 -- a/e2e-attestations/x.json" in out + + def passes_and_says_so_when_a_bypass_line_is_present(capsys): + rc = verdict(["a/e2e-attestations/x.json"], "allow-receipt-deletion: why", "sha") + assert rc == 0 + assert "permitting receipt deletion" in capsys.readouterr().out + + def does_not_report_when_bypassed(capsys): + rc = verdict(["a/e2e-attestations/x.json"], "allow-receipt-deletion: why", "sha") + assert rc == 0 + assert "::error file=" not in capsys.readouterr().out + + def names_a_near_miss_bypass_line(capsys): + rc = verdict(["a/e2e-attestations/x.json"], "skip-receipt-deletion: y", "sha") + assert rc == 1 + out = capsys.readouterr().out + assert "'skip-receipt-deletion: y' is not the bypass line" in out + + def reports_no_near_miss_when_the_messages_hold_none(capsys): + assert verdict(["a/e2e-attestations/x.json"], "fix: x", "sha") == 1 + assert "is not the bypass line" not in capsys.readouterr().out diff --git a/internals/checks/src/checks/platforms_mirror/as_json.py b/internals/checks/src/checks/platforms_mirror/as_json.py new file mode 100644 index 000000000..9b290d016 --- /dev/null +++ b/internals/checks/src/checks/platforms_mirror/as_json.py @@ -0,0 +1,15 @@ +"""A comment-free object literal as JSON. + +TypeScript's bare keys and trailing commas are the only two things left between +a stripped `PLATFORMS` array and something `json` will decode. +""" + +from __future__ import annotations + +import re + + +def as_json(text: str) -> str: + """`text` with bare object keys quoted and trailing commas dropped.""" + keyed = re.sub(r"([{,]\s*)([A-Za-z_$][\w$]*)\s*:", r'\1"\2":', text) + return re.sub(r",(\s*[}\]])", r"\1", keyed) diff --git a/internals/checks/src/checks/platforms_mirror/as_json_test.py b/internals/checks/src/checks/platforms_mirror/as_json_test.py new file mode 100644 index 000000000..3dae05eda --- /dev/null +++ b/internals/checks/src/checks/platforms_mirror/as_json_test.py @@ -0,0 +1,28 @@ +"""Colocated unit tests for the JSON normalizer (isolation -- pure text in, +text out). +""" + +from checks.platforms_mirror.as_json import as_json + + +def describe_as_json(): + def it_quotes_a_bare_object_key(): + assert as_json('{nodePlatform: "linux"}') == '{"nodePlatform": "linux"}' + + def it_quotes_every_key_after_a_comma(): + assert as_json('{a: 1, b: 2}') == '{"a": 1, "b": 2}' + + def it_quotes_a_key_holding_a_dollar_sign_or_underscore(): + assert as_json("{_a$b: 1}") == '{"_a$b": 1}' + + def it_drops_a_trailing_comma_before_a_brace(): + assert as_json('{"a": 1,}') == '{"a": 1}' + + def it_drops_a_trailing_comma_before_a_bracket(): + assert as_json('["a",]') == '["a"]' + + def it_keeps_the_whitespace_that_followed_a_dropped_comma(): + assert as_json('{"a": 1,\n}') == '{"a": 1\n}' + + def it_leaves_an_already_quoted_key_alone(): + assert as_json('{"a": 1}') == '{"a": 1}' diff --git a/internals/checks/src/checks/platforms_mirror/gate.py b/internals/checks/src/checks/platforms_mirror/gate.py index 9da9f18e0..f7c8223e4 100644 --- a/internals/checks/src/checks/platforms_mirror/gate.py +++ b/internals/checks/src/checks/platforms_mirror/gate.py @@ -12,14 +12,10 @@ from .parse import ParseError from .problems import problems from .python_table import python_table +from .read import read from .typescript_table import typescript_table -def read(path: str) -> str: - with open(path, encoding="utf-8") as handle: - return handle.read() - - def run( python_path: str, typescript_path: str, diff --git a/internals/checks/src/checks/platforms_mirror/gate_test.py b/internals/checks/src/checks/platforms_mirror/gate_test.py index ed737a7fc..27a524976 100644 --- a/internals/checks/src/checks/platforms_mirror/gate_test.py +++ b/internals/checks/src/checks/platforms_mirror/gate_test.py @@ -1,12 +1,13 @@ """Colocated unit tests for the platforms-mirror gate (#1004). Isolation: the reader is injected, so nothing here touches the repo's real -platforms.py / platforms.ts. The reader itself is exercised against a scratch -file. The table readers and the verdict in `problems.py` run for real, since -they are pure text-in / messages-out. +platforms.py / platforms.ts. The table readers and the verdict in +`problems.py` run for real, since they are pure text-in / messages-out. """ -from checks.platforms_mirror.gate import read, run +import inspect + +from checks.platforms_mirror.gate import run PYTHON = '''\ from dataclasses import dataclass @@ -55,13 +56,6 @@ def invoke(**kwargs): return code, "\n".join(lines) -def describe_read(): - def it_reads_a_file_as_utf8_text(tmp_path): - path = tmp_path / "platforms.ts" - path.write_text("// caffè\n", encoding="utf-8") - assert read(str(path)) == "// caffè\n" - - def describe_run(): def it_reports_the_target_count_when_the_subset_agrees(): code, out = invoke() @@ -100,3 +94,7 @@ def it_fails_when_the_typescript_table_cannot_be_read(): assert code == 1 assert "could not read a platform table:" in out assert "no `PLATFORMS" in out + + def it_defaults_to_the_reader_in_its_own_module(): + default = inspect.signature(run).parameters["source"].default + assert default.__module__ == "checks.platforms_mirror.read" diff --git a/internals/checks/src/checks/platforms_mirror/read.py b/internals/checks/src/checks/platforms_mirror/read.py new file mode 100644 index 000000000..e9e8c72dc --- /dev/null +++ b/internals/checks/src/checks/platforms_mirror/read.py @@ -0,0 +1,12 @@ +"""The default reader for the platforms-mirror check (#1004). + +Split from the gate so the orchestration holds no I/O: the gate takes this as +an injected default and unit-tests against text rather than the repo's files. +""" + +from __future__ import annotations + + +def read(path: str) -> str: + with open(path, encoding="utf-8") as handle: + return handle.read() diff --git a/internals/checks/src/checks/platforms_mirror/read_test.py b/internals/checks/src/checks/platforms_mirror/read_test.py new file mode 100644 index 000000000..d868fe66f --- /dev/null +++ b/internals/checks/src/checks/platforms_mirror/read_test.py @@ -0,0 +1,17 @@ +"""Colocated unit test for the platforms-mirror reader (isolation -- a scratch +file, never the repo's real platforms.py / platforms.ts). +""" + +from checks.platforms_mirror.read import read + + +def describe_read(): + def it_reads_a_file_as_utf8_text(tmp_path): + path = tmp_path / "platforms.ts" + path.write_text("// caffè\n", encoding="utf-8") + assert read(str(path)) == "// caffè\n" + + def it_reads_a_file_whole(tmp_path): + path = tmp_path / "platforms.ts" + path.write_text("one\ntwo\n", encoding="utf-8") + assert read(str(path)) == "one\ntwo\n" diff --git a/internals/checks/src/checks/platforms_mirror/requoted.py b/internals/checks/src/checks/platforms_mirror/requoted.py new file mode 100644 index 000000000..53e3076b1 --- /dev/null +++ b/internals/checks/src/checks/platforms_mirror/requoted.py @@ -0,0 +1,16 @@ +"""A TypeScript string literal as a JSON one. + +Single-quoted and template forms carry the same characters JSON spells with +double quotes, so re-quoting is what lets `json` read the table as data. +""" + +from __future__ import annotations + +import json + + +def requoted(text: str) -> str: + """A TypeScript string literal as a JSON one.""" + if text.startswith('"'): + return text + return json.dumps(text[1:-1].replace('\\"', '"').replace("\\'", "'")) diff --git a/internals/checks/src/checks/platforms_mirror/requoted_test.py b/internals/checks/src/checks/platforms_mirror/requoted_test.py new file mode 100644 index 000000000..dd9cc4d09 --- /dev/null +++ b/internals/checks/src/checks/platforms_mirror/requoted_test.py @@ -0,0 +1,28 @@ +"""Colocated unit tests for the string re-quoter (isolation -- pure text in, +text out). +""" + +from checks.platforms_mirror.requoted import requoted + + +def describe_requoted(): + def it_keeps_a_double_quoted_literal_verbatim(): + assert requoted('"linux"') == '"linux"' + + def it_rewrites_a_single_quoted_literal_as_json(): + assert requoted("'linux'") == '"linux"' + + def it_rewrites_a_template_literal_as_json(): + assert requoted("`linux`") == '"linux"' + + def it_keeps_the_contents_of_a_single_quoted_literal_whole(): + assert requoted("'aba'") == '"aba"' + + def it_unescapes_an_escaped_single_quote(): + assert requoted(r"'it\'s'") == '"it\'s"' + + def it_unescapes_an_escaped_double_quote_inside_a_template(): + assert requoted(r"`a\"b`") == '"a\\"b"' + + def it_escapes_a_double_quote_that_was_bare_in_a_single_quoted_literal(): + assert requoted("'a\"b'") == '"a\\"b"' diff --git a/internals/checks/src/checks/platforms_mirror/typescript_table.py b/internals/checks/src/checks/platforms_mirror/typescript_table.py index 8ffbb8560..0f804c6b3 100644 --- a/internals/checks/src/checks/platforms_mirror/typescript_table.py +++ b/internals/checks/src/checks/platforms_mirror/typescript_table.py @@ -10,47 +10,21 @@ import json import re +from .as_json import as_json from .parse import ParseError, TABLE_NAME - -# One alternation of "things a naive scan would cut in half": the three string -# forms first, so a `//` inside a string is matched as string rather than as the -# comment that follows it. -_TOKENS = re.compile( - r'"(?:\\.|[^"\\])*"' r"|'(?:\\.|[^'\\])*'" r"|`(?:\\.|[^`\\])*`" r"|//[^\n]*" r"|/\*.*?\*/", - re.S, -) - - -def _requoted(text: str) -> str: - """A TypeScript string literal as a JSON one.""" - if text.startswith('"'): - return text - return json.dumps(text[1:-1].replace('\\"', '"').replace("\\'", "'")) - - -def _without_comments(source: str) -> str: - def keep(match: re.Match) -> str: - text = match.group(0) - return "" if text.startswith(("//", "/*")) else _requoted(text) - - return _TOKENS.sub(keep, source) - - -def _as_json(text: str) -> str: - keyed = re.sub(r"([{,]\s*)([A-Za-z_$][\w$]*)\s*:", r'\1"\2":', text) - return re.sub(r",(\s*[}\]])", r"\1", keyed) +from .without_comments import without_comments def typescript_table(source: str) -> list[dict]: """Rows of the `PLATFORMS` array in a `platforms.ts`-shaped module.""" - cleaned = _without_comments(source) + cleaned = without_comments(source) marker = re.search(rf"\b{TABLE_NAME}\b[^=;]*=\s*(\[)", cleaned) if marker is None: raise ParseError(f"no `{TABLE_NAME} = [...]` assignment in the TypeScript source.") try: # `raw_decode` stops at the end of the array, so nothing here has to find # the closing bracket or care what follows it. - rows, _ = json.JSONDecoder().raw_decode(_as_json(cleaned[marker.start(1) :])) + rows, _ = json.JSONDecoder().raw_decode(as_json(cleaned[marker.start(1) :])) except json.JSONDecodeError as error: raise ParseError( f"`{TABLE_NAME}` is not a plain array of object literals ({error}); this check " diff --git a/internals/checks/src/checks/platforms_mirror/typescript_table_test.py b/internals/checks/src/checks/platforms_mirror/typescript_table_test.py index 64f4b61cc..ef05fd393 100644 --- a/internals/checks/src/checks/platforms_mirror/typescript_table_test.py +++ b/internals/checks/src/checks/platforms_mirror/typescript_table_test.py @@ -4,6 +4,7 @@ import pytest +from checks.platforms_mirror import typescript_table as module from checks.platforms_mirror.typescript_table import ParseError, typescript_table TYPESCRIPT = """\ @@ -74,3 +75,9 @@ def it_rejects_an_entry_that_is_not_an_object_literal(): def it_rejects_a_table_it_cannot_read_as_data(): with pytest.raises(ParseError, match="not a plain array"): typescript_table("const PLATFORMS = [{ ...spread }];") + + def it_reads_the_source_through_the_helpers_in_their_own_modules(): + assert module.without_comments.__module__ == ( + "checks.platforms_mirror.without_comments" + ) + assert module.as_json.__module__ == "checks.platforms_mirror.as_json" diff --git a/internals/checks/src/checks/platforms_mirror/without_comments.py b/internals/checks/src/checks/platforms_mirror/without_comments.py new file mode 100644 index 000000000..debd8d4b6 --- /dev/null +++ b/internals/checks/src/checks/platforms_mirror/without_comments.py @@ -0,0 +1,27 @@ +"""A `platforms.ts`-shaped source with its comments stripped and strings requoted. + +One alternation of "things a naive scan would cut in half": the three string +forms come first, so a `//` inside a string is matched as string rather than as +the comment that follows it. +""" + +from __future__ import annotations + +import re + +from .requoted import requoted + +_TOKENS = re.compile( + r'"(?:\\.|[^"\\])*"' r"|'(?:\\.|[^'\\])*'" r"|`(?:\\.|[^`\\])*`" r"|//[^\n]*" r"|/\*.*?\*/", + re.S, +) + + +def without_comments(source: str) -> str: + """`source` with comments removed and every string literal JSON-quoted.""" + + def keep(match: re.Match) -> str: + text = match.group(0) + return "" if text.startswith(("//", "/*")) else requoted(text) + + return _TOKENS.sub(keep, source) diff --git a/internals/checks/src/checks/platforms_mirror/without_comments_test.py b/internals/checks/src/checks/platforms_mirror/without_comments_test.py new file mode 100644 index 000000000..dfe905fc8 --- /dev/null +++ b/internals/checks/src/checks/platforms_mirror/without_comments_test.py @@ -0,0 +1,40 @@ +"""Colocated unit tests for the comment stripper (isolation -- pure text in, +text out; nothing here touches the repo's real platforms.ts). +""" + +from checks.platforms_mirror.without_comments import without_comments + + +def describe_without_comments(): + def it_strips_a_line_comment(): + assert without_comments("a // gone\nb") == "a \nb" + + def it_strips_a_line_comment_that_runs_to_end_of_file(): + assert without_comments("a // gone") == "a " + + def it_strips_a_block_comment(): + assert without_comments("a /* gone */ b") == "a b" + + def it_strips_a_block_comment_spanning_lines(): + assert without_comments("a /* one\ntwo */ b") == "a b" + + def it_keeps_a_double_quoted_string_verbatim(): + assert without_comments('x = "linux"') == 'x = "linux"' + + def it_requotes_a_single_quoted_string(): + assert without_comments("x = 'linux'") == 'x = "linux"' + + def it_requotes_a_template_literal(): + assert without_comments("x = `linux`") == 'x = "linux"' + + def it_does_not_read_a_line_comment_marker_inside_a_string(): + assert without_comments('x = "a // b"') == 'x = "a // b"' + + def it_does_not_read_a_block_comment_marker_inside_a_string(): + assert without_comments("x = 'a /* b'") == 'x = "a /* b"' + + def it_keeps_an_escaped_quote_inside_a_string(): + assert without_comments(r'x = "a\"b"') == r'x = "a\"b"' + + def it_leaves_source_with_neither_comment_nor_string_alone(): + assert without_comments("[1, 2]") == "[1, 2]" diff --git a/internals/checks/tests/integration/attestation_guard_test.py b/internals/checks/tests/integration/attestation_guard_test.py index 7e886ed2f..6d176c88b 100644 --- a/internals/checks/tests/integration/attestation_guard_test.py +++ b/internals/checks/tests/integration/attestation_guard_test.py @@ -1,8 +1,9 @@ """Integration tests for the attestation-guard check against a real git repo. -Exercises `gate.run` with its default collaborators (the real `git_ops` -functions, real `git` subprocess calls) over a scratch repo fixture -- never -the packaged `dirsql-checks` CLI (that's the e2e tier). +Exercises `gate.run` with its default collaborators (the real +`deleted_files` / `commit_messages` wrappers, real `git` subprocess calls) +over a scratch repo fixture -- never the packaged `dirsql-checks` CLI (that's +the e2e tier). """ from __future__ import annotations From 92a7561ad5b5c4c189052411c929f3555e87f722 Mon Sep 17 00:00:00 2001 From: thekevinbot <248217931+thekevinbot@users.noreply.github.com> Date: Sat, 5 Sep 2026 07:26:44 -0400 Subject: [PATCH 2/3] e2e attestation for claude/ofpf-attestation-guard-split-r7m2 --- .../claude-ofpf-attestation-guard-split-r7m2.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 internals/checks/e2e-attestations/claude-ofpf-attestation-guard-split-r7m2.json diff --git a/internals/checks/e2e-attestations/claude-ofpf-attestation-guard-split-r7m2.json b/internals/checks/e2e-attestations/claude-ofpf-attestation-guard-split-r7m2.json new file mode 100644 index 000000000..d274e9c91 --- /dev/null +++ b/internals/checks/e2e-attestations/claude-ofpf-attestation-guard-split-r7m2.json @@ -0,0 +1,7 @@ +{ + "command": "uv run python -m pytest tests/e2e -x -q", + "ran_at": 1788607604, + "exit_code": 0, + "commit": "827aa4770e160cb96d1bf4864d61c14f8abc31ba", + "branch": "claude/ofpf-attestation-guard-split-r7m2" +} From 2a4ba167314a233a5c70a789c5622e5d2a6ac7b2 Mon Sep 17 00:00:00 2001 From: thekevinbot <248217931+thekevinbot@users.noreply.github.com> Date: Sat, 5 Sep 2026 07:27:00 -0400 Subject: [PATCH 3/3] test(checks): pin typescript_table's helpers by patching them at its namespace The two extracted helpers are patched by string rather than imported, so the unit test keeps its isolation while asserting that typescript_table resolves without_comments and as_json in its own module namespace. --- .../platforms_mirror/typescript_table_test.py | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/internals/checks/src/checks/platforms_mirror/typescript_table_test.py b/internals/checks/src/checks/platforms_mirror/typescript_table_test.py index ef05fd393..78dd277cd 100644 --- a/internals/checks/src/checks/platforms_mirror/typescript_table_test.py +++ b/internals/checks/src/checks/platforms_mirror/typescript_table_test.py @@ -2,9 +2,10 @@ in, data out; nothing here touches the repo's real platforms.ts). """ +from unittest import mock + import pytest -from checks.platforms_mirror import typescript_table as module from checks.platforms_mirror.typescript_table import ParseError, typescript_table TYPESCRIPT = """\ @@ -76,8 +77,18 @@ def it_rejects_a_table_it_cannot_read_as_data(): with pytest.raises(ParseError, match="not a plain array"): typescript_table("const PLATFORMS = [{ ...spread }];") - def it_reads_the_source_through_the_helpers_in_their_own_modules(): - assert module.without_comments.__module__ == ( - "checks.platforms_mirror.without_comments" - ) - assert module.as_json.__module__ == "checks.platforms_mirror.as_json" + def it_strips_comments_through_the_helper_in_its_own_module(): + with mock.patch( + "checks.platforms_mirror.typescript_table.without_comments", + return_value='PLATFORMS = [{"os": ["a"]}]', + ) as stripper: + assert typescript_table("// only the stripper sees this") == [{"os": ["a"]}] + stripper.assert_called_once_with("// only the stripper sees this") + + def it_normalizes_to_json_through_the_helper_in_its_own_module(): + with mock.patch( + "checks.platforms_mirror.typescript_table.as_json", + return_value='[{"os": ["a"]}]', + ) as normalizer: + assert typescript_table("const PLATFORMS = [{ os: ['b'] }];") == [{"os": ["a"]}] + normalizer.assert_called_once_with("""[{ os: ["b"] }];""")