This repository has been archived on 2026-11-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
platform-ci/tests/test_script_host_platforms.py
forge-recoveryandClaude Sonnet 5.5 a36f17b857
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 6s
feat(ci): advance shared pipeline capabilities
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 10:30:25 +00:00

147 lines
8.7 KiB
Python

"""The `script` tool on host platforms, the PowerShell launcher and Maven-format script artifacts.
NOT RUN: written statically while test execution was suspended.
"""
import tempfile
import unittest
from collections.abc import Mapping, Sequence
from pathlib import Path
from platform_ci.build_tools import BUILD_TOOLS, PLATFORMS, POWERSHELL_LAUNCHER
from platform_ci.configuration import ConfigurationError, parse_project_configuration
from platform_ci.execution import launch_command, run_target_commands
from platform_ci.runtimes import RuntimeCatalogError
from tests.support import NATIVE_RUNTIME_CATALOG, event, plan_for
def configuration(platforms: list[str], toolchains: dict[str, str], program: str, **component: object) -> dict[str, object]:
"""A one-component `script` configuration."""
body: dict[str, object] = {"platforms": platforms, "toolchains": toolchains, "build": {"tool": "script", "commands": [[program, "argument"]]}, **component}
return {"schema": 1, "components": {"native": body}}
class Recorder:
"""A command stub that records invocations."""
def __init__(self) -> None:
"""Start empty."""
self.commands: list[list[str]] = []
def __call__(self, command: Sequence[str], working_directory: Path, environment: Mapping[str, str]) -> int:
"""Record and succeed."""
del working_directory, environment
self.commands.append(list(command))
return 0
class ScriptHostConfigurationTest(unittest.TestCase):
"""Platforms and toolchains are accepted by declaration; the catalog decides availability."""
def test_the_script_tool_is_offered_on_every_platform_with_linux_first(self) -> None:
self.assertEqual(BUILD_TOOLS["script"].platforms, tuple(PLATFORMS))
self.assertEqual(BUILD_TOOLS["script"].platforms[0], "linux-amd64")
self.assertIn("xcode", BUILD_TOOLS["script"].toolchain_choices)
self.assertIn("dotnet", BUILD_TOOLS["script"].toolchain_choices)
def test_the_default_platform_stays_linux(self) -> None:
mapping: dict[str, object] = {"schema": 1, "components": {"native": {"toolchains": {"java": "21"}, "build": {"tool": "script", "commands": [["./scripts/ci/check.sh"]]}}}}
self.assertEqual(parse_project_configuration(mapping).components[0].platforms, ("linux-amd64",))
def test_any_program_kind_is_accepted_on_any_declared_platform(self) -> None:
for platforms, toolchains, program in (
(["macos-arm64"], {"xcode": "27"}, "./scripts/ci/prebuild.sh"),
(["macos-arm64"], {"xcode": "27"}, "./scripts/ci/prebuild.ps1"),
(["windows-amd64"], {"dotnet": "10.0.403"}, "./build/Build-Installer.ps1"),
(["windows-amd64"], {"dotnet": "10.0.403"}, "./build/build.cmd"),
(["linux-amd64"], {"java": "21"}, "./build/Build.ps1"),
(["linux-amd64", "windows-amd64"], {"control": "1"}, "./scripts/ci/check.ps1"),
):
with self.subTest(platforms=platforms, program=program):
self.assertEqual(parse_project_configuration(configuration(platforms, toolchains, program)).components[0].platforms, tuple(sorted(platforms)))
def test_runtime_profiles_are_refused_on_hosts_and_kept_on_linux(self) -> None:
for platform, toolchains in (("macos-arm64", {"xcode": "27"}), ("windows-amd64", {"dotnet": "10.0.403"})):
with self.subTest(platform), self.assertRaisesRegex(ConfigurationError, "belong to container platforms"):
parse_project_configuration(configuration([platform], toolchains, "./scripts/ci/prebuild.sh", requires=["docker"]))
linux = parse_project_configuration(configuration(["linux-amd64"], {"java": "21"}, "./scripts/ci/check.sh", requires=["docker"]))
self.assertEqual(linux.components[0].requires, ("docker",))
def test_a_program_outside_the_component_is_still_rejected_on_a_host(self) -> None:
with self.assertRaises(ConfigurationError):
parse_project_configuration(configuration(["macos-arm64"], {"xcode": "27"}, "../escape.sh"))
class ScriptMavenArtifactFormatTest(unittest.TestCase):
"""A script producer may declare Maven-format artifacts; every other format rule is unchanged."""
def _artifacts(self, artifact_format: str, tool: str = "script") -> dict[str, object]:
mapping: dict[str, object] = configuration(["linux-amd64"], {"java": "21"}, "./scripts/ci/build.sh", artifacts={"packages": {"paths": ["build/repo/**"], "format": artifact_format}})
if tool != "script":
mapping = {"schema": 1, "components": {"native": {"toolchains": {"go": "1"}, "build": {"tool": tool, "arguments": ["build"]}, "artifacts": {"packages": {"paths": ["out/*"], "format": artifact_format}}}}}
return mapping
def test_a_script_component_may_declare_a_maven_artifact(self) -> None:
component = parse_project_configuration(self._artifacts("maven")).components[0]
self.assertEqual(component.artifact_formats, {"packages": "maven"})
def test_a_script_component_still_cannot_declare_an_npm_artifact(self) -> None:
with self.assertRaisesRegex(ConfigurationError, "needs build tool 'node', not 'script'"):
parse_project_configuration(self._artifacts("npm"))
def test_another_tool_still_cannot_declare_a_maven_artifact_and_unknown_formats_fail(self) -> None:
with self.assertRaisesRegex(ConfigurationError, "needs build tool 'maven' or 'script', not 'go'"):
parse_project_configuration(self._artifacts("maven", "go"))
with self.assertRaisesRegex(ConfigurationError, "unsupported artifact format 'rpm'"):
parse_project_configuration(self._artifacts("rpm"))
class ScriptHostPlanTest(unittest.TestCase):
"""Full host plans against the native test catalog; no real runner label or runtime is implied."""
def test_a_macos_script_plans_as_a_host_target_with_the_declared_arguments(self) -> None:
target = plan_for(parse_project_configuration(configuration(["macos-arm64"], {"xcode": "27"}, "./scripts/ci/prebuild.sh")), event(), NATIVE_RUNTIME_CATALOG)["targets"][0]
self.assertEqual((target["execution"], target["runner"], target["image"], target["toolchain"]), ("host", "test-macos-pool", "", "xcode"))
self.assertEqual(target["commands"], [["./scripts/ci/prebuild.sh", "argument"]])
def test_a_windows_ps1_plans_as_a_host_target_and_keeps_the_program_unwrapped(self) -> None:
target = plan_for(parse_project_configuration(configuration(["windows-amd64"], {"dotnet": "10.0.403"}, "./build/Build-Installer.ps1")), event(), NATIVE_RUNTIME_CATALOG)["targets"][0]
self.assertEqual((target["execution"], target["runner"], target["platform"]), ("host", "test-windows-pool", "windows-amd64"))
self.assertEqual(target["commands"], [["./build/Build-Installer.ps1", "argument"]])
def test_an_unattested_host_toolchain_stays_a_catalog_error(self) -> None:
"""`control` has no host attestation in the native catalog, so planning fails naming the platform; nothing is substituted."""
with self.assertRaisesRegex(RuntimeCatalogError, "no qualified windows-amd64 host runtime"):
plan_for(parse_project_configuration(configuration(["windows-amd64"], {"control": "1"}, "./scripts/ci/sign.ps1")), event(), NATIVE_RUNTIME_CATALOG)
class LaunchCommandTest(unittest.TestCase):
"""The fixed PowerShell launcher, on any platform."""
def test_a_plain_program_is_started_as_declared(self) -> None:
self.assertEqual(launch_command(["./scripts/ci/check.sh", "a"]), ["./scripts/ci/check.sh", "a"])
def test_a_ps1_program_gets_the_fixed_launcher(self) -> None:
command = ["./build/Build-Installer.ps1", "-Version", "1.0"]
self.assertEqual(launch_command(command), [*POWERSHELL_LAUNCHER, *command])
self.assertEqual(POWERSHELL_LAUNCHER, ("pwsh", "-NoProfile", "-NonInteractive", "-File"))
self.assertEqual(launch_command(["./Sign.PS1"])[:4], list(POWERSHELL_LAUNCHER))
def test_execution_runs_the_launcher_for_ps1_and_the_program_directly_otherwise(self) -> None:
with tempfile.TemporaryDirectory() as directory:
source = Path(directory)
(source / "scripts").mkdir()
(source / "scripts" / "a.ps1").write_text("", encoding="utf-8")
(source / "scripts" / "b.sh").write_text("", encoding="utf-8")
for program, expected in (("./scripts/a.ps1", [*POWERSHELL_LAUNCHER, "./scripts/a.ps1", "x"]), ("./scripts/b.sh", ["./scripts/b.sh", "x"])):
with self.subTest(program):
target = plan_for(parse_project_configuration(configuration(["macos-arm64"], {"xcode": "27"}, program)), event(), NATIVE_RUNTIME_CATALOG)["targets"][0]
target["commands"] = [[program, "x"]]
recorder = Recorder()
self.assertTrue(run_target_commands(target, source, recorder, {"PATH": "/usr/bin"}))
self.assertEqual(recorder.commands, [expected])
if __name__ == "__main__":
unittest.main()