Archived
KofTwentyTwo/ci-policy: ci / fork-guard (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / go-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / go-nix-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / gradle-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / gradle-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / java-docker-4 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / java-maven-5 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / manifests-6 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / nix-7 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / node-8 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / python-9 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-10 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-11 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / rust-12 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / source-config-13 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / source-config-14 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-4 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-5 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-6 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / image-build-7 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / not-enrolled (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / content-validate-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / standards-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / node-matrix-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: ci / source-config-15 (pull_request) Blocked by required conditions
KofTwentyTwo/ci-policy: Shared source and config CI / verify (pull_request) Blocked by required conditions
KofTwentyTwo/ci-policy: Shared source and config CI / secrets (pull_request) Blocked by required conditions
KofTwentyTwo/ci-policy: Shared source and config CI / gate (pull_request) Waiting for needed jobs
KofTwentyTwo/ci-policy: ci / ci-result (pull_request) Waiting for needed jobs
KofTwentyTwo/ci-policy: native / fork-deny (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-0 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-1 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-2 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-macos-3 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-4 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-5 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-6 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / build-windows-7 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-8 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-9 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-10 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / dotnet-11 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-12 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-13 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-14 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-15 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-16 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-17 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-18 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / swift-19 (pull_request) Skipped
KofTwentyTwo/ci-policy: native / native-result (pull_request) Canceled after 4s
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
87 lines
4.6 KiB
Python
87 lines
4.6 KiB
Python
"""Validate and start the receipted native helpers of the `source-tools` build tool.
|
|
|
|
A `source-tools` command is a subcommand name (`actionlint`, `shellcheck-orb`, `make-bats`, `kustomize-render`, `blueprint-lint`, `policy`) and,
|
|
for the two that read directories, validated relative paths. The plan carries exactly that list. At run time this module requires the
|
|
vendored helpers in the platform checkout to match `source_tools/receipt.json`, then starts the helper unchanged with `bash`. The only
|
|
inputs the helpers read are variables derived here from validated data: the path list, the fixed check name and `INSTALL_TOOL`, which
|
|
points at `require-installed-tool.sh`, so no pinned tool is ever downloaded: a missing tool fails the run.
|
|
"""
|
|
|
|
import re
|
|
from collections.abc import Sequence
|
|
from pathlib import Path, PurePosixPath
|
|
from typing import Final
|
|
|
|
from platform_ci.build_tools import BUILD_TOOLS, HelperSubcommand
|
|
from platform_ci.source_helpers import SourceHelperError, verify_source_helpers
|
|
|
|
SOURCE_TOOLS_TOOL: Final = "source-tools"
|
|
RECEIPT_PATH: Final = Path(__file__).resolve().parent.parent / "source_tools" / "receipt.json"
|
|
HELPER_DIRECTORY: Final = ".platform-ci/source_tools"
|
|
"""Where the platform checkout's `source_tools/` sits relative to the repository root, which is the component directory."""
|
|
RESOLVER_SCRIPT: Final = "require-installed-tool.sh"
|
|
# The helpers' own rule (manifest-verify.sh, blueprint-lint.sh): only these characters, relative, inside the workspace.
|
|
_PATH_PATTERN: Final = re.compile(r"[A-Za-z0-9._/-]+")
|
|
|
|
|
|
def subcommand_named(name: str) -> HelperSubcommand | None:
|
|
"""Return the `source-tools` subcommand with this name, or None when it is not in the closed vocabulary."""
|
|
return next((subcommand for subcommand in BUILD_TOOLS[SOURCE_TOOLS_TOOL].helper_subcommands if subcommand.name == name), None)
|
|
|
|
|
|
def validate_helper_command(command: Sequence[str], location: str) -> str | None:
|
|
"""Return a description of what is wrong with a declared `source-tools` command, or None when it is acceptable."""
|
|
if not command:
|
|
return f"{location}: expected a subcommand name"
|
|
subcommand = subcommand_named(command[0])
|
|
known = ", ".join(entry.name for entry in BUILD_TOOLS[SOURCE_TOOLS_TOOL].helper_subcommands)
|
|
if subcommand is None:
|
|
return f"{location}: expected one subcommand of {SOURCE_TOOLS_TOOL!r} ({known}), got {command[0]!r}"
|
|
paths = list(command[1:])
|
|
if not subcommand.path_variable:
|
|
return f"{location}: {subcommand.name!r} takes no arguments, got {paths!r}" if paths else None
|
|
if not paths:
|
|
return f"{location}: {subcommand.name!r} needs at least one relative directory"
|
|
for path in paths:
|
|
problem = _path_problem(path)
|
|
if problem:
|
|
return f"{location}: {path!r} {problem}"
|
|
return None
|
|
|
|
|
|
def _path_problem(path: str) -> str:
|
|
"""Return why a path argument is unsafe, or an empty string; the same rule the helpers enforce on their own input."""
|
|
if _PATH_PATTERN.fullmatch(path) is None:
|
|
return "may only use letters, digits, '.', '_', '-' and '/'"
|
|
if path.startswith("/"):
|
|
return "must be relative to the repository root"
|
|
parts = PurePosixPath(path).parts
|
|
if ".." in parts:
|
|
return "must stay inside the repository"
|
|
if all(part == "." for part in parts):
|
|
return "must name a directory below the repository root"
|
|
return ""
|
|
|
|
|
|
def helper_invocation(command: Sequence[str], working_directory: Path) -> tuple[list[str], dict[str, str]]:
|
|
"""Verify the receipted helpers, then return the helper's argument list and the validated variables it reads.
|
|
|
|
Raises `SourceHelperError` when the helpers differ from the receipt or the command is not in the closed vocabulary.
|
|
"""
|
|
subcommand = subcommand_named(command[0]) if command else None
|
|
if subcommand is None:
|
|
raise SourceHelperError(f"{list(command)!r} is not a source-tools subcommand")
|
|
problem = validate_helper_command(command, "command")
|
|
if problem:
|
|
raise SourceHelperError(problem)
|
|
root = working_directory.resolve()
|
|
helpers = (root / HELPER_DIRECTORY).resolve()
|
|
if not helpers.is_relative_to(root) or not helpers.is_dir():
|
|
raise SourceHelperError(f"{HELPER_DIRECTORY} is not a directory inside the repository root")
|
|
verify_source_helpers(helpers, RECEIPT_PATH)
|
|
scripts = helpers / "scripts"
|
|
environment = {"INSTALL_TOOL": str(scripts / RESOLVER_SCRIPT), **dict(subcommand.fixed_environment)}
|
|
if subcommand.path_variable:
|
|
environment[subcommand.path_variable] = ",".join(command[1:])
|
|
return [BUILD_TOOLS[SOURCE_TOOLS_TOOL].executable, str(scripts / subcommand.script)], environment
|