photoncloud-monorepo/scripts/ci_changed_workspaces.py
centra e1a5d394e5
Some checks failed
Nix CI / filter (push) Successful in 54s
Nix CI / gate (shared crates) (push) Has been skipped
Nix CI / gate () (push) Failing after 6s
Nix CI / build () (push) Has been skipped
Nix CI / ci-status (push) Failing after 1m14s
ci: unify workspace inventory and harden tier0 gating
2026-03-28 00:09:22 +09:00

134 lines
4.1 KiB
Python

#!/usr/bin/env python3
import argparse
import fnmatch
import json
from pathlib import Path
from typing import Any
def load_changed_files(args: argparse.Namespace) -> list[str]:
changed_files: list[str] = []
for path in args.changed_files_file:
for line in Path(path).read_text().splitlines():
candidate = line.strip()
if candidate:
changed_files.append(candidate)
changed_files.extend(path.strip() for path in args.changed_file if path.strip())
return changed_files
def matches_any(path: str, patterns: list[str]) -> bool:
return any(fnmatch.fnmatchcase(path, pattern) for pattern in patterns)
def detect_changes(config: dict[str, Any], changed_files: list[str]) -> dict[str, Any]:
workspaces: list[dict[str, Any]] = config["workspaces"]
all_workspace_names = [workspace["name"] for workspace in workspaces]
global_changed = any(
matches_any(path, config["global_paths"])
for path in changed_files
)
shared_crates_changed = any(
matches_any(path, config["shared_crates_paths"])
for path in changed_files
)
if global_changed:
changed_workspaces = all_workspace_names
else:
changed_workspaces = [
workspace["name"]
for workspace in workspaces
if any(matches_any(path, workspace["paths"]) for path in changed_files)
]
selected_workspaces = set(changed_workspaces)
build_targets: list[dict[str, str]] = []
seen_build_targets: set[tuple[str, str]] = set()
for workspace in workspaces:
if workspace["name"] not in selected_workspaces:
continue
for package in workspace.get("build_packages", []):
key = (workspace["name"], package)
if key in seen_build_targets:
continue
seen_build_targets.add(key)
build_targets.append({
"workspace": workspace["name"],
"package": package,
})
return {
"workspaces": changed_workspaces,
"build_targets": build_targets,
"any_changed": global_changed or bool(changed_workspaces),
"build_changed": bool(build_targets),
"global_changed": global_changed,
"shared_crates_changed": shared_crates_changed,
}
def write_github_output(path: Path, result: dict[str, Any]) -> None:
serialized = {
"workspaces": json.dumps(result["workspaces"], separators=(",", ":")),
"build_targets": json.dumps(result["build_targets"], separators=(",", ":")),
"any_changed": str(result["any_changed"]).lower(),
"build_changed": str(result["build_changed"]).lower(),
"global_changed": str(result["global_changed"]).lower(),
"shared_crates_changed": str(result["shared_crates_changed"]).lower(),
}
with path.open("a", encoding="utf-8") as handle:
for key, value in serialized.items():
handle.write(f"{key}={value}\n")
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Map changed files to PhotonCloud CI workspaces."
)
parser.add_argument(
"--config",
required=True,
help="Path to the JSON CI workspace inventory.",
)
parser.add_argument(
"--changed-files-file",
action="append",
default=[],
help="File containing newline-separated changed paths.",
)
parser.add_argument(
"--changed-file",
action="append",
default=[],
help="Single changed path. Can be repeated.",
)
parser.add_argument(
"--github-output",
help="Optional path to append GitHub Actions step outputs.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
config = json.loads(Path(args.config).read_text())
changed_files = load_changed_files(args)
result = detect_changes(config, changed_files)
if args.github_output:
write_github_output(Path(args.github_output), result)
print(json.dumps(result, indent=2, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())