Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions host-setup/agent-safety/gh-write-guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,17 @@
| (?:label|secret|variable|ruleset)\s+(?:create|delete|edit|set)
| gist\s+(?:create|edit|delete)
)\b""",
re.X,
re.VERBOSE,
)
_GH_API = re.compile(r"\bgh\s+api\b")
_EXPLICIT_WRITE_METHOD = re.compile(r"(?:--method|-X)\s+(?:POST|PUT|PATCH|DELETE)\b", re.I)
_EXPLICIT_WRITE_METHOD = re.compile(r"(?:--method|-X)\s+(?:POST|PUT|PATCH|DELETE)\b", re.IGNORECASE)
# A gh api call with a field flag defaults to POST even without -X, so it is a write.
_API_FIELD_FLAG = re.compile(r"(?:^|\s)(?:-f|-F|--field|--raw-field|--input)\b")
_GRAPHQL = re.compile(r"\bgh\s+api\b.*\bgraphql\b", re.S)
_GRAPHQL = re.compile(r"\bgh\s+api\b.*\bgraphql\b", re.DOTALL)
_MUTATION = re.compile(r"\bmutation\b")
# Loose pre-filter only: matches `git` before `push` even with global options between them
# (git -C <dir> push). _push_arg_lists is the accurate arbiter that confirms an executable push.
_GIT_PUSH = re.compile(r"\bgit\b.*?\bpush\b", re.S)
_GIT_PUSH = re.compile(r"\bgit\b.*?\bpush\b", re.DOTALL)

# --- Bypass-of-branch-rule detectors (Rule 4) --------------------------------------------------------
# A git operation is denied when it would only succeed by bypassing an active branch rule.
Expand Down
2 changes: 1 addition & 1 deletion host-setup/agent-safety/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def reject(where, held, want):
newline, existing = "\n", ""
for marker, filename in blocks:
snippet = (HERE / filename).read_text(encoding="utf-8").strip()
block_re = re.compile(rf"<!-- {marker} v\d+ start -->.*?<!-- {marker} v\d+ end -->", re.S)
block_re = re.compile(rf"<!-- {marker} v\d+ start -->.*?<!-- {marker} v\d+ end -->", re.DOTALL)
if block_re.search(existing):
existing, action = block_re.sub(lambda _: snippet, existing), "updated"
else:
Expand Down
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ line-length = 100

[tool.ruff.lint]
extend-select = ["I"] # isort import ordering, on top of the default rules
# UP017 rewrites `timezone.utc` to `datetime.UTC`, which needs Python 3.11.
# `spec/host-tools.json` declares no python3 floor and records why.
# Every script here is standard library only, so a bare interpreter is enough.
# Taking the rewrite would create that floor silently, as a lint fix rather than a decision.
ignore = ["UP017"]

[tool.mypy]
python_version = "3.13"
Expand Down
7 changes: 6 additions & 1 deletion scripts/host_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@
below. 2 = the declaration itself could not be read, which is a defect here rather than on the host.
"""
from __future__ import annotations
import argparse, json, re, subprocess, sys

import argparse
import json
import re
import subprocess
import sys
from pathlib import Path

SPEC = Path(__file__).resolve().parent.parent / 'spec' / 'host-tools.json'
Expand Down
12 changes: 10 additions & 2 deletions scripts/pr_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,15 @@
GOVERNANCE.md "Repository Boundaries and Write Safety" for the rules `reply` enforces.
"""
from __future__ import annotations
import argparse, io, json, re, subprocess, sys, tarfile, time

import argparse
import io
import json
import re
import subprocess
import sys
import tarfile
import time
from datetime import datetime, timezone
from pathlib import Path

Expand Down Expand Up @@ -978,7 +986,7 @@ def checks_unreadable(pr: dict) -> bool:
than as this reading having failed. A silent narrowing is the failure mode this whole script
is built against, and it does not get an exception for its own newest field.
"""
return bool(((pr.get('commits') or {}).get('nodes') or [])) and not head_commit(pr)
return bool((pr.get('commits') or {}).get('nodes') or []) and not head_commit(pr)


def checks_tally(nodes: list[dict]) -> tuple[int, int]:
Expand Down
9 changes: 8 additions & 1 deletion scripts/prose_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@
Exit 1 if any violation is found. Read-only, never edits.
"""
from __future__ import annotations
import argparse, io, re, subprocess, sys, tokenize, unicodedata

import argparse
import io
import re
import subprocess
import sys
import tokenize
import unicodedata
from pathlib import Path
from typing import NamedTuple, TypedDict

Expand Down
10 changes: 7 additions & 3 deletions scripts/repo_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@
actionlint, editorconfig-checker, spec/validate.py).
"""
from __future__ import annotations
import argparse, re, subprocess, sys
from pathlib import Path, PurePosixPath

import argparse
import re
import subprocess
import sys
from fnmatch import fnmatch
from pathlib import Path, PurePosixPath

# GOVERNANCE.md documents exactly one floating-ref exception.
SHA_EXCEPTIONS = {'dotnet/nbgv'}
USES = re.compile(r'^\s*-?\s*uses:\s*(?P<ref>[^\s#]+)', re.M)
USES = re.compile(r'^\s*-?\s*uses:\s*(?P<ref>[^\s#]+)', re.MULTILINE)
PIN = re.compile(r'^[0-9a-f]{40}$')
WORKFLOW = re.compile(r'workflows/.*\.ya?ml$')
# What `gh` prints when GitHub answered, as opposed to when nothing was reached at all.
Expand Down
3 changes: 2 additions & 1 deletion scripts/test_host_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
neither was visible from reading the code.
"""
from __future__ import annotations

import json
import sys
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))
import host_gate # noqa: E402
import host_gate
Comment thread
ptr727 marked this conversation as resolved.


def tool(name, minimum=None, required=True, probes=None, pattern=r'v(\d+(?:\.\d+)*)', **extra):
Expand Down
9 changes: 8 additions & 1 deletion scripts/test_pr_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@
Run as `python3 scripts/test_pr_review.py`, or under `python3 -m unittest discover -s scripts`.
"""
from __future__ import annotations
import contextlib, io, json, re, subprocess, sys, unittest

import contextlib
import io
import json
import re
import subprocess
import sys
import unittest
from datetime import datetime, timedelta, timezone
from itertools import count
from pathlib import Path
Expand Down
14 changes: 11 additions & 3 deletions scripts/test_prose_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@
Run as `python3 scripts/test_prose_lint.py`, or under `python3 -m unittest discover -s scripts`.
"""
from __future__ import annotations
import contextlib, io, json, re, subprocess, sys, tempfile, unittest

import contextlib
import io
import json
import re
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
from unittest import mock

Expand Down Expand Up @@ -129,14 +137,14 @@ class TestGovernanceCoupling(unittest.TestCase):

def setUp(self) -> None:
self.doc = GOVERNANCE.read_text(encoding='utf-8')
section = re.search(r'^### Character Set$(.*?)^### ', self.doc, re.M | re.S)
section = re.search(r'^### Character Set$(.*?)^### ', self.doc, re.MULTILINE | re.DOTALL)
if section is None:
self.fail('the Character Set heading moved, so the parse is blind')
self.section = section.group(1)

def tier_codepoints(self, label: str) -> set[int]:
"""Codepoints named in one tier's bullet, read out of the rule text itself."""
m = re.search(rf'^- \*\*Tier {label},(.*?)(?=^- \*\*)', self.section, re.M | re.S)
m = re.search(rf'^- \*\*Tier {label},(.*?)(?=^- \*\*)', self.section, re.MULTILINE | re.DOTALL)
if m is None:
self.fail(f'the Tier {label} bullet moved, so the parse is blind')
return {int(h, 16) for h in re.findall(r'U\+([0-9A-Fa-f]{4})', m.group(1))}
Expand Down
10 changes: 9 additions & 1 deletion scripts/test_repo_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@
Run as `python3 scripts/test_repo_gate.py`, or under `python3 -m unittest discover -s scripts`.
"""
from __future__ import annotations
import contextlib, io, re, shutil, subprocess, sys, tempfile, unittest

import contextlib
import io
import re
import shutil
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
from unittest import mock

Expand Down
20 changes: 9 additions & 11 deletions spec/audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def driftnote_findings(entry, spec, open_count):
out.append(("DRIFT", f"registry: driftNote names check '{cid}', whose type '{owner}' this repo does not declare: {quoted}"))
else:
out.append(("DRIFT", f"registry: driftNote names check '{cid}', which this audit does not evaluate by id (AUDIT.md section 4) - judge it by hand and delete the note once it passes: {quoted}"))
marker = next((w for w in PENDING_MARKERS if re.search(rf"\b{re.escape(w)}\b", note, re.I)), None)
marker = next((w for w in PENDING_MARKERS if re.search(rf"\b{re.escape(w)}\b", note, re.IGNORECASE)), None)
if marker and not open_count:
out.append(("DRIFT", f"registry: driftNote says '{marker}' but the audit is clean - verify and reconcile: {quoted}"))
elif marker:
Expand Down Expand Up @@ -310,7 +310,7 @@ def heading_texts(markdown):
return {m.group(1).strip().lower() for line in markdown.splitlines() for m in (_HEADING.match(line),) if m}


_HTML_COMMENT = re.compile(r"<!--.*?-->", re.S)
_HTML_COMMENT = re.compile(r"<!--.*?-->", re.DOTALL)
_MD_LINK_INLINE = re.compile(r"\[([^\]]*)\]\((?:[^()]|\([^()]*\))*\)") # URL may hold one level of ()
_MD_LINK_REF = re.compile(r"\[([^\]]*)\]\[[^\]]*\]")

Expand Down Expand Up @@ -363,10 +363,10 @@ def tagline(intro):

_MD_IMAGE_REF = re.compile(r"!\[[^\]]*\]\[([^\]]+)\]")
_MD_IMAGE_INLINE = re.compile(r"!\[[^\]]*\]\((\S+?)\)")
_LINK_DEF = re.compile(r"^\[([^\]]+)\]:\s*(\S+)", re.M)
_LINK_DEF = re.compile(r"^\[([^\]]+)\]:\s*(\S+)", re.MULTILINE)
# A URI scheme, requiring two or more characters so a `C:` drive letter is not read as one.
# Every scheme a README actually carries (mailto, ftp, ssh, git, tel, data) is longer than that.
_URI_SCHEME = re.compile(r"[a-z][a-z0-9+.\-]+:", re.I)
_URI_SCHEME = re.compile(r"[a-z][a-z0-9+.\-]+:", re.IGNORECASE)


def unfenced_text(text):
Expand Down Expand Up @@ -711,10 +711,8 @@ def table_cells(line):
s = line.strip()
if "|" not in s:
return None
if s.startswith("|"):
s = s[1:]
if s.endswith("|"):
s = s[:-1]
s = s.removeprefix("|")
s = s.removesuffix("|")
return [c.strip() for c in s.split("|")]


Expand Down Expand Up @@ -1210,7 +1208,7 @@ def audit_repo(entry, spec, branch=None):
# Language ecosystems such as nuget, uv and npm are directory-scoped and not yet cross-checked here.
db = gh(f"repos/{slug}/contents/.github/dependabot.yml?ref={ground}", ok404=True)
if db and db.get("content"):
declared = set(re.findall(r'^[ \t]*-?[ \t]*package-ecosystem:[ \t]*["\']?([\w-]+)', base64.b64decode(db["content"]).decode("utf-8", "replace"), re.M))
declared = set(re.findall(r'^[ \t]*-?[ \t]*package-ecosystem:[ \t]*["\']?([\w-]+)', base64.b64decode(db["content"]).decode("utf-8", "replace"), re.MULTILINE))
implied = {}
workflows = gh(f"repos/{slug}/contents/.github/workflows?ref={ground}", ok404=True)
if isinstance(workflows, list) and any(e["name"].endswith((".yml", ".yaml")) for e in workflows):
Expand Down Expand Up @@ -1475,7 +1473,7 @@ def _selftest():
got = classify_verbatim(down, canon_t, history)
if got != want:
ok = False
print(f" {'ok ' if got == want else 'FAIL'} want={str(want):>8} got={str(got):>8} verbatim: {label}")
print(f" {'ok ' if got == want else 'FAIL'} want={want!s:>8} got={got!s:>8} verbatim: {label}")
# Action-pin neutralization, where a Dependabot uses:@<sha> bump, meaning both the 40-hex sha and its ` # vN` comment, must not count as verbatim drift, while a changed action name must.
# This is what lets a verbatim workflow region survive routine action bumps while still catching a real fork.
pin_a = " - uses: actions/checkout@" + "a" * 40 + " # v7.0.0\n"
Expand Down Expand Up @@ -1713,7 +1711,7 @@ def _selftest():
"[license-shield]: https://img.shields.io/github/license/o/r\n"
)
# The four repos that suffix every heading for the ToC extension must read identically to the plain form.
omit_toc = re.sub(r"^(#{2,3} .*)$", r"\1 <!-- omit from toc -->", conformant, flags=re.M)
omit_toc = re.sub(r"^(#{2,3} .*)$", r"\1 <!-- omit from toc -->", conformant, flags=re.MULTILINE)
readme_cases = [
("a conformant README, repo-specific section included", conformant, set(), True, 0),
("a second intro paragraph is not a finding", conformant.replace("A fixture repository.\n", "A fixture repository.\n\nAnd a clarifying paragraph about it.\n"), set(), True, 0),
Expand Down
2 changes: 1 addition & 1 deletion spec/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ def check_selector(where, applies_to):
# This is Markdown only, and only where the hub ships the file.
if path.endswith(".md") and (ROOT / path).exists():
hub_text = (ROOT / path).read_text(encoding="utf-8", errors="replace")
headings = {m.group(1).strip() for m in re.finditer(r"^## (.+?)\s*$", hub_text, re.M)}
headings = {m.group(1).strip() for m in re.finditer(r"^## (.+?)\s*$", hub_text, re.MULTILINE)}
for elt in sections:
name = elt.get("name") if isinstance(elt, dict) else elt
if isinstance(name, str) and name and name not in headings:
Expand Down