Files
plezy/scripts/checks/check_workflow_action_pins.py
T
edde746 f622ba8efe chore(scripts): group scripts into checks, codegen, maestro and release subdirectories
scripts/ had ~80 flat files. Entry points (ci_*.sh, codegen.sh, run_tests.sh, format_native.sh, setup_hooks.sh, upload-symbols.*) and the shared pubspec_version.py stay at the root; checkers, generators, maestro tooling and release tooling move into subdirectories with their tests. Updated every reference: workflow steps, guard-test glob, Docker COPY paths and .dockerignore whitelist, website audit path, dart test imports, and regenerated the five outputs whose headers embed generator paths.
2026-08-17 01:40:54 +02:00

63 lines
2.0 KiB
Python
Executable File

#!/usr/bin/env python3
"""Require immutable commit pins for remote GitHub Actions dependencies."""
from __future__ import annotations
import re
import sys
from pathlib import Path
import workflow_yaml
ROOT = Path(__file__).resolve().parents[2]
WORKFLOWS = ROOT / ".github" / "workflows"
ACTIONS = ROOT / ".github" / "actions"
REMOTE_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_./-]+)?@[0-9a-fA-F]{40}$")
def iter_action_files(directory: Path = ACTIONS):
"""Local composite actions run in the same trust boundary as the workflows."""
yield from sorted((*directory.glob("*/action.yml"), *directory.glob("*/action.yaml")))
def iter_uses_references(path: Path):
return workflow_yaml.iter_uses_references(path.read_text(encoding="utf-8"))
def validate_reference(reference: str) -> str | None:
if reference.startswith("./"):
return None
if REMOTE_RE.fullmatch(reference):
return None
return "remote actions must use a full 40-character commit SHA"
def main(argv: list[str] | None = None) -> int:
args = list(sys.argv[1:] if argv is None else argv)
paths = (
[Path(value) for value in args]
if args
else [*workflow_yaml.iter_workflow_files(WORKFLOWS), *iter_action_files()]
)
violations = []
for path in paths:
for line_number, reference in iter_uses_references(path):
reason = validate_reference(reference)
if reason:
try:
display_path = path.resolve().relative_to(ROOT)
except ValueError:
display_path = path
violations.append(f"{display_path}:{line_number}: {reference!r}: {reason}")
if violations:
print("Mutable or malformed GitHub Actions references:", file=sys.stderr)
for violation in violations:
print(f" {violation}", file=sys.stderr)
return 1
print(f"Workflow action pins verified ({len(paths)} files).")
return 0
if __name__ == "__main__":
raise SystemExit(main())