"""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()