Archived
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
38 lines
1.5 KiB
Python
38 lines
1.5 KiB
Python
"""Checks shared by every stage about the checked-out source and the commit hashes that identify a run."""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
PLATFORM_CHECKOUT_DIRECTORY_NAME = ".platform-ci"
|
|
GIT_METADATA_DIRECTORY_NAME = ".git"
|
|
|
|
_COMMIT_HASH_PATTERN = re.compile(r"[0-9a-f]{40}|[0-9a-f]{64}")
|
|
|
|
|
|
class PipelineError(Exception):
|
|
"""Base for every failure the pipeline reports to the user as a message instead of a traceback."""
|
|
|
|
|
|
def require_commit_hash(value: str, label: str) -> str:
|
|
"""Return the value when it is a full lowercase git commit hash, otherwise fail loudly.
|
|
|
|
A branch name or a placeholder here would make the recorded revision meaningless,
|
|
so nothing shorter or looser than a full hash is accepted.
|
|
"""
|
|
if _COMMIT_HASH_PATTERN.fullmatch(value) is None:
|
|
raise PipelineError(f"{label} must be a full commit hash, got {value!r}")
|
|
return value
|
|
|
|
|
|
def resolve_component_directory(source_directory: Path, relative_directory: str) -> Path:
|
|
"""Resolve a component directory and prove it is a real directory inside the checked-out source.
|
|
|
|
Resolving symbolic links here means a link in the repository cannot point a build or an
|
|
output glob at files outside the checkout.
|
|
"""
|
|
source_root = source_directory.resolve()
|
|
component_directory = (source_root / relative_directory).resolve()
|
|
if not component_directory.is_relative_to(source_root) or not component_directory.is_dir():
|
|
raise PipelineError(f"component directory {relative_directory!r} is not a directory inside the source checkout")
|
|
return component_directory
|