mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-25 21:20:00 +00:00
Give every native installer check its own process budget
Compile and discover one exact test binary per native workflow phase, then execute every discovered root check independently. This removes inventory growth from another check's ten-minute Go alarm while preserving fixture, installer and overall CI job deadlines. Retain exhaustive phase coverage and reject missing completion, failed checks, unexpected exit zero and timeouts using real compiled Go execution controls. Register the runner with deployment installability and retain the failed Intel and advisory benchmark evidence in the existing reliability record.
This commit is contained in:
+4
@@ -37,6 +37,10 @@ The [original Intel timeout](https://github.com/rcourtman/Pulse/actions/runs/386
|
||||
|
||||
The integrated source's [backend partition 0](https://github.com/rcourtman/Pulse/actions/runs/39135739571/job/117701554206) and [partition 1](https://github.com/rcourtman/Pulse/actions/runs/39135739571/job/117701554230) failures were reproduced on the worker at `0b3261e1aa1356f291030622a91dca244324cd0b`. Constructor cancellation returned promptly but left its password request active. The integrated constructor had joined the detached login intended for concurrent readers renewing an accepted session. Initial authentication now owns a synchronous bounded JSON/form exchange. Shared renewal retains its separate caller-independent lifetime, generation coalescing and timeout. Existing constructor, startup and cluster controls must pass alongside shared-renewal cancellation and new client-timeout controls before source qualification closes.
|
||||
|
||||
At `6bf9e818e8c261012902da79ac2fc2e4a5b57acc`, all nine protected required checks passed, including both backend partitions, and five native platforms passed. The [Intel job](https://github.com/rcourtman/Pulse/actions/runs/39138980077/job/117709536519) passed the NAS preflight but exhausted the general-1 ten-minute Go process alarm while its active log-sink recovery control had run only 17 seconds. The remaining source blocker is aggregate native inventory execution. The canonical Unix runner now compiles and discovers one exact binary per phase, then executes every root check independently under the same ten-minute Go bound. Individual fixture and operation deadlines, full inventory acceptance and overall CI job deadlines remain unchanged. Exact-source native qualification is still required before landing.
|
||||
|
||||
The [advisory benchmark comparison](https://github.com/rcourtman/Pulse/actions/runs/39138980114/job/117709718583) completed execution but reported three regressions above 10% in test-only legacy history-filter references. Their corresponding production variants were unchanged or below 4%. Both filter implementation and benchmark source are byte-identical between baseline `84a96940185cc202aef0451d658bc89b864710a0` and synthetic candidate `ed25833fa8318e493d560239f60e56ec5ffad540`. The retained paired samples and source identities require an owning disposition; unchanged source alone does not classify the timing observation. This advisory result does not replace the protected required checks or existing release gates.
|
||||
|
||||
These are source repairs and qualification evidence. Release inclusion, affected installed workflows and the manual reliability exit decision remain open. No target, lane state or priority changes with this work.
|
||||
|
||||
## Exit decision and feature pickup
|
||||
|
||||
@@ -1960,6 +1960,8 @@ Windows lifecycle controls or macOS installer and logging checks on native CI.
|
||||
104. `.github/workflows/dependency-advisory-watch.yml`
|
||||
105. `scripts/dependency_advisory_watch.py`
|
||||
|
||||
`scripts/installtests/run_unix_contracts.py` is the canonical exhaustive native Unix installer execution runner. Its dedicated verification files are `scripts/installtests/native_unix_contract_runner_test.go` and `scripts/installtests/native_unix_contract_runner_test.py`.
|
||||
|
||||
## Shared Boundaries
|
||||
|
||||
Installer-managed helper upgrades restart the verified executable after effective isolated-unit and fixed socket-identity validation. Daemon reload or enabling an already-active socket alone does not replace an old process. Failure to restart aborts the installer and uses the existing safe-profile recovery transaction; collector-only updates still cannot replace the helper or unit.
|
||||
@@ -8050,6 +8052,32 @@ retains the required names, full race execution and separate dependency audit.
|
||||
These proofs establish CI source admission, not hosted queue-time relief.
|
||||
|
||||
|
||||
### Exhaustive native Unix installer process budgets (18 October 2026)
|
||||
|
||||
`scripts/installtests/run_unix_contracts.py` belongs to deployment installability.
|
||||
It compiles the native installer test binary once per workflow phase and discovers
|
||||
that exact binary's tests, examples and fuzz seeds. The NAS boot-owner preflight
|
||||
and remaining inventory are disjoint and exhaustive, retaining binary discovery
|
||||
order within each phase. Every root check executes in its own process with the
|
||||
existing ten-minute Go alarm, uncached execution and panic-on-exit-zero behavior;
|
||||
a successful exit without that root's PASS or explicit SKIP is rejected.
|
||||
Inventory growth therefore cannot exhaust a later check's process budget.
|
||||
Fixture, installer, transport, cleanup and overall native CI job deadlines do
|
||||
not change, nor do the platform-specific reasons for existing explicit skips.
|
||||
|
||||
The 18 October Intel run at `6bf9e818e8c261012902da79ac2fc2e4a5b57acc`
|
||||
passed the 23 NAS controls, then exhausted the general-1 aggregate ten-minute
|
||||
alarm while its active log-sink recovery control had run only 17 seconds. This
|
||||
runner change removes the aggregate coupling; native CI remains required to
|
||||
prove the complete inventory on Intel. It does not establish installed NAS or
|
||||
customer recovery, release qualification, or closure of the reliability target.
|
||||
|
||||
`TestNativeUnixContractRunnerPartitionsWithoutDroppingChecks` runs the Python
|
||||
controls against a growing synthetic inventory and real compiled Go tests.
|
||||
They prove phase coverage, build/discovery/check failure propagation, passing
|
||||
tests, explicit skips, examples and fuzz seeds; actual failure, exit zero,
|
||||
timeout and unmatched test names must fail.
|
||||
|
||||
## Release-body updater ownership (30 September 2026)
|
||||
|
||||
Each executable `/bin/update --version` example in a published body must carry
|
||||
|
||||
@@ -4706,6 +4706,7 @@
|
||||
"scripts/install.ps1",
|
||||
"scripts/install.sh",
|
||||
"scripts/installtests/auto_update_systemd.py",
|
||||
"scripts/installtests/run_unix_contracts.py",
|
||||
"scripts/installtests/testdata/secure_runtime_docker_fixture.go",
|
||||
"scripts/lib/hot-dev-auth.sh",
|
||||
"scripts/lib/hot-dev-runtime.sh",
|
||||
@@ -5366,6 +5367,20 @@
|
||||
"exact_files": [
|
||||
"scripts/tests/test_e2e_test_images.py"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "native-unix-installer-runner",
|
||||
"label": "exhaustive native Unix installer execution proof",
|
||||
"match_prefixes": [],
|
||||
"match_files": [
|
||||
"scripts/installtests/run_unix_contracts.py"
|
||||
],
|
||||
"allow_same_subsystem_tests": false,
|
||||
"test_prefixes": [],
|
||||
"exact_files": [
|
||||
"scripts/installtests/native_unix_contract_runner_test.go",
|
||||
"scripts/installtests/native_unix_contract_runner_test.py"
|
||||
]
|
||||
}
|
||||
],
|
||||
"match_files": null
|
||||
|
||||
@@ -9,91 +9,13 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Exercise the actual Python runner without recursively running the Go suite.
|
||||
// The hosted failure had 500 checks under one package alarm; this control uses
|
||||
// that inventory size and verifies admission, exact-once execution and failure
|
||||
// propagation across both workflow phases, not a macOS timing reproduction.
|
||||
// The Python controls exercise both workflow phases and real compiled Go tests
|
||||
// without recursively running the installer suite. Native CI supplies the
|
||||
// platform timing proof; these controls reject lost checks and false passes.
|
||||
func TestNativeUnixContractRunnerPartitionsWithoutDroppingChecks(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, "python3", "-c", `
|
||||
import importlib.util
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
spec = importlib.util.spec_from_file_location("native_runner", sys.argv[1])
|
||||
runner = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(runner)
|
||||
names = [f"TestFixture{i:03d}" for i in range(497)]
|
||||
names += ["ExampleInstaller", "FuzzInstaller", "TestFutureFixture"]
|
||||
reset = ["TestRootInstallResetSafety", "TestRootInstallResetPersistence"]
|
||||
preflight = ["TestInstallSHNASBootOwnersQuiesceBeforeRecoveryErasure"]
|
||||
inventory = "\n".join(names + reset + preflight + ["BenchmarkIgnored", "ok package 0.1s"])
|
||||
groups = runner.partition(inventory)
|
||||
assert tuple(runner.GROUPS) == ("nas-preflight", "general-0", "general-1", "reset")
|
||||
assert [len(group) for group in groups] == [1, 250, 250, 2]
|
||||
assert sorted(sum(groups, [])) == sorted(names + reset + preflight)
|
||||
assert len(set(sum(groups, []))) == 503
|
||||
assert groups[0] == preflight and groups[3] == sorted(reset)
|
||||
assert groups == runner.partition("\n".join(reversed(names + reset + preflight)))
|
||||
for unsafe in ["", "TestOnly", "TestOne\nTestTwo", inventory + "\nTestFixture000",
|
||||
inventory + "\nTestUnsafe|TestInjected", inventory + "\nFuzzBad.*",
|
||||
"\n".join(names + reset)]:
|
||||
try:
|
||||
runner.partition(unsafe)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError("unsafe or incomplete inventory admitted")
|
||||
|
||||
def execute(failure_at=None, output=inventory, phase="all"):
|
||||
calls = []
|
||||
selected = groups if phase == "all" else groups[:1] if phase == "preflight" else groups[1:]
|
||||
def run(argv, **kwargs):
|
||||
calls.append(argv)
|
||||
assert kwargs["cwd"] == runner.ROOT and kwargs["check"] is True
|
||||
if len(calls) == failure_at:
|
||||
raise subprocess.CalledProcessError(23, argv)
|
||||
if len(calls) == 1:
|
||||
assert argv == ["go", "test", "-list", ".", runner.PACKAGE]
|
||||
assert kwargs["capture_output"] is True and kwargs["text"] is True
|
||||
return SimpleNamespace(stdout=output)
|
||||
assert argv[:6] == ["go", "test", "-count=1", "-timeout", "10m", "-run"]
|
||||
assert argv[-1] == runner.PACKAGE
|
||||
assert argv[6] == "^(" + "|".join(selected[len(calls) - 2]) + ")$"
|
||||
assert "-parallel" not in argv
|
||||
return SimpleNamespace(returncode=0)
|
||||
with patch.object(runner.subprocess, "run", side_effect=run):
|
||||
try:
|
||||
runner.main(phase)
|
||||
except subprocess.CalledProcessError as error:
|
||||
assert failure_at == len(calls) and error.returncode == 23
|
||||
except ValueError:
|
||||
assert output != inventory and len(calls) == 1
|
||||
else:
|
||||
assert failure_at is None and output == inventory
|
||||
return calls
|
||||
|
||||
calls = execute()
|
||||
assert len(calls) == 5
|
||||
patterns = [re.compile(call[6]) for call in calls[1:]]
|
||||
for name in names + reset + preflight:
|
||||
assert sum(bool(pattern.fullmatch(name)) for pattern in patterns) == 1
|
||||
for name in ["TestFixture000/child", "TestInjected", "BenchmarkIgnored", "TestFixture000Extra"]:
|
||||
assert not any(pattern.fullmatch(name) for pattern in patterns)
|
||||
for failure_at in range(1, 6):
|
||||
assert len(execute(failure_at=failure_at)) == failure_at
|
||||
assert len(execute(output=inventory + "\nTestFixture000")) == 1
|
||||
early = execute(phase="preflight")
|
||||
remaining = execute(phase="remaining")
|
||||
assert early[1:] + remaining[1:] == calls[1:]
|
||||
for phase, count in [("preflight", 2), ("remaining", 4)]:
|
||||
for failure_at in range(1, count + 1):
|
||||
assert len(execute(failure_at=failure_at, phase=phase)) == failure_at
|
||||
`, repoFile("scripts", "installtests", "run_unix_contracts.py"))
|
||||
cmd := exec.CommandContext(ctx, "python3", "-I", "-S", repoFile("scripts", "installtests", "native_unix_contract_runner_test.py"))
|
||||
output, err := cmd.CombinedOutput()
|
||||
if ctx.Err() != nil {
|
||||
t.Fatalf("native installer runner control deadline: %v", ctx.Err())
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Execution controls for the native installer runner, including real Go exits."""
|
||||
import contextlib
|
||||
import importlib.util
|
||||
import io
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
spec = importlib.util.spec_from_file_location("native_runner", Path(__file__).with_name("run_unix_contracts.py"))
|
||||
runner = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(runner)
|
||||
|
||||
|
||||
class InventoryControls(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.names = [f"TestFixture{i:03d}" for i in range(500)]
|
||||
self.names += ["ExampleInstaller", "FuzzInstaller", "TestFutureFixture",
|
||||
"TestRootInstallResetSafety", *runner.PREFLIGHT]
|
||||
self.inventory = "\n".join(self.names + ["BenchmarkIgnored", "ok package 0.1s"])
|
||||
|
||||
def test_growing_inventory_is_exhaustive_disjoint_and_ordered(self):
|
||||
early, remaining = runner.partition(self.inventory)
|
||||
self.assertEqual(early, list(runner.PREFLIGHT))
|
||||
self.assertEqual(remaining, [name for name in self.names if name not in runner.PREFLIGHT])
|
||||
self.assertEqual(sorted(early + remaining), sorted(self.names))
|
||||
for invalid in ["", "TestOnly", "TestOne\nTestTwo", self.inventory + "\nTestFixture000",
|
||||
self.inventory + "\nTestUnsafe|TestInjected", self.inventory + "\nFuzzBad.*",
|
||||
"\n".join(self.names[:-1])]:
|
||||
with self.subTest(invalid=invalid[-60:]), self.assertRaises(ValueError):
|
||||
runner.partition(invalid)
|
||||
|
||||
def execute(self, phase, failure_at=None):
|
||||
calls, checks = [], []
|
||||
def command(argv, **kwargs):
|
||||
calls.append(argv)
|
||||
self.assertTrue(kwargs["check"])
|
||||
if len(calls) == failure_at:
|
||||
raise subprocess.CalledProcessError(23, argv)
|
||||
return SimpleNamespace(stdout=self.inventory)
|
||||
def check(binary, name, **kwargs):
|
||||
checks.append(name)
|
||||
self.assertEqual(kwargs["cwd"], runner.ROOT / "scripts/installtests")
|
||||
if len(checks) + 2 == failure_at:
|
||||
raise subprocess.CalledProcessError(23, [str(binary), name])
|
||||
with patch.object(runner.subprocess, "run", side_effect=command), patch.object(runner, "run_check", side_effect=check), contextlib.redirect_stdout(io.StringIO()):
|
||||
if failure_at:
|
||||
with self.assertRaises(subprocess.CalledProcessError):
|
||||
runner.main(phase)
|
||||
else:
|
||||
runner.main(phase)
|
||||
self.assertEqual(calls[0][:4], ["go", "test", "-c", "-o"])
|
||||
if len(calls) > 1:
|
||||
self.assertEqual(calls[1], [calls[0][4], "-test.list=."])
|
||||
self.assertEqual(calls[0][-1], runner.PACKAGE)
|
||||
return checks
|
||||
|
||||
def test_workflow_phases_execute_every_check_once_and_propagate_failure(self):
|
||||
all_checks = self.execute("all")
|
||||
self.assertEqual(self.execute("preflight") + self.execute("remaining"), all_checks)
|
||||
self.assertEqual(len(all_checks), len(set(all_checks)))
|
||||
self.assertEqual(set(all_checks), set(self.names))
|
||||
for phase in ("all", "preflight", "remaining"):
|
||||
for failure in (1, 2, 3):
|
||||
self.execute(phase, failure_at=failure)
|
||||
with self.assertRaises(ValueError):
|
||||
runner.main("unknown")
|
||||
|
||||
|
||||
class CompiledGoControls(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.directory = tempfile.TemporaryDirectory(prefix="native-runner-control-")
|
||||
cls.cwd = Path(cls.directory.name)
|
||||
cls.binary = cls.cwd / "controls.test"
|
||||
(cls.cwd / "go.mod").write_text("module native.runner.controls\n\ngo 1.24\n")
|
||||
(cls.cwd / "controls_test.go").write_text('''package controls
|
||||
import ("fmt"; "os"; "testing"; "time")
|
||||
func TestPass(t *testing.T) {}
|
||||
func TestSkip(t *testing.T) { t.Skip("explicit platform skip") }
|
||||
func TestFail(t *testing.T) { t.Fatal("real failure") }
|
||||
func TestExitZero(t *testing.T) { os.Exit(0) }
|
||||
func TestTimeout(t *testing.T) { time.Sleep(time.Hour) }
|
||||
func FuzzSeeds(f *testing.F) { f.Add("seed"); f.Fuzz(func(t *testing.T, s string) {}) }
|
||||
func Example_output() { fmt.Println("installer"); /* Output: installer */ }
|
||||
''')
|
||||
subprocess.run(["go", "test", "-c", "-o", str(cls.binary), "."], cwd=cls.cwd, check=True, timeout=90)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.directory.cleanup()
|
||||
|
||||
def test_real_test_skip_example_and_fuzz_seed_complete(self):
|
||||
for name in ("TestPass", "TestSkip", "Example_output", "FuzzSeeds"):
|
||||
with self.subTest(name=name), contextlib.redirect_stdout(io.StringIO()):
|
||||
runner.run_check(self.binary, name, cwd=self.cwd)
|
||||
|
||||
def test_failure_exit_zero_and_timeout_cannot_pass(self):
|
||||
for name in ("TestFail", "TestExitZero", "TestTimeout"):
|
||||
with self.subTest(name=name), contextlib.redirect_stdout(io.StringIO()), self.assertRaises(subprocess.CalledProcessError):
|
||||
runner.run_check(self.binary, name, cwd=self.cwd, timeout="100ms")
|
||||
|
||||
def test_no_matching_test_cannot_pass(self):
|
||||
with contextlib.redirect_stdout(io.StringIO()), self.assertRaises(RuntimeError):
|
||||
runner.run_check(self.binary, "TestMissing", cwd=self.cwd)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
@@ -1,69 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Keep native installer checks exhaustive inside bounded package invocations.
|
||||
"""Run every native installer check with an independent Go process deadline.
|
||||
|
||||
Each Go invocation retains the existing ten-minute package deadline; individual
|
||||
fixture, transport and cleanup deadlines are unchanged. Discover the actual
|
||||
package inventory so future tests, examples and fuzz seeds remain included.
|
||||
The general inventory grew to 500 checks and exhausted that package alarm on
|
||||
macOS Intel. Split it deterministically rather than extending safety deadlines.
|
||||
Run the existing NAS process/boot-owner control before frontend preparation;
|
||||
the remaining phase still requires that identity in the discovered inventory
|
||||
and excludes only the check already executed by the workflow's preflight.
|
||||
Compile once and discover from that exact binary. Inventory growth must not
|
||||
consume another check's ten-minute Go alarm. Fixture, transport, cleanup and
|
||||
workflow job deadlines remain unchanged. The NAS boot-owner preflight runs
|
||||
before frontend preparation; the remaining phase requires that identity too
|
||||
and excludes only the check already executed by preflight.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
PACKAGE = "./scripts/installtests"
|
||||
PREFLIGHT = ("TestInstallSHNASBootOwnersQuiesceBeforeRecoveryErasure",)
|
||||
GROUPS = ("nas-preflight", "general-0", "general-1", "reset")
|
||||
|
||||
|
||||
def partition(output):
|
||||
preflight, general, reset = [], [], []
|
||||
seen = set()
|
||||
names = []
|
||||
for name in output.splitlines():
|
||||
if not name.startswith(("Test", "Example", "Fuzz")):
|
||||
continue
|
||||
if not name.isidentifier() or name in seen:
|
||||
if not name.isidentifier() or name in names:
|
||||
raise ValueError("invalid or repeated Go test identity")
|
||||
seen.add(name)
|
||||
if name in PREFLIGHT:
|
||||
preflight.append(name)
|
||||
(reset if name.startswith("TestRootInstallReset") else general).append(name)
|
||||
if set(preflight) != set(PREFLIGHT) or len(general) - len(preflight) < 2 or not reset:
|
||||
names.append(name)
|
||||
if (not set(PREFLIGHT).issubset(names)
|
||||
or sum(name.startswith("TestRootInstallReset") for name in names) < 1
|
||||
or sum(name not in PREFLIGHT and not name.startswith("TestRootInstallReset")
|
||||
for name in names) < 2):
|
||||
raise ValueError("missing preflight, general or reset installer inventory")
|
||||
# Interleave sorted identities so adjacent fixture families do not all
|
||||
# consume the same package budget. Never omit or retry a discovered check.
|
||||
general.sort()
|
||||
# Remove the preflight only after partitioning so every other check keeps
|
||||
# its existing group and order, rather than reshuffling the package budgets.
|
||||
return (sorted(preflight),
|
||||
[name for name in general[::2] if name not in PREFLIGHT],
|
||||
[name for name in general[1::2] if name not in PREFLIGHT],
|
||||
sorted(reset))
|
||||
# Preserve the compiled binary's order within each workflow phase. Every
|
||||
# discovered test, example and fuzz seed belongs to exactly one phase.
|
||||
return ([name for name in names if name in PREFLIGHT],
|
||||
[name for name in names if name not in PREFLIGHT])
|
||||
|
||||
|
||||
def run_check(binary, name, *, cwd, timeout="10m"):
|
||||
command = [str(binary), "-test.count=1", "-test.timeout=" + timeout,
|
||||
"-test.paniconexit0", "-test.v", "-test.run=^" + name + "$"]
|
||||
completed = False
|
||||
with subprocess.Popen(command, cwd=cwd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, text=True) as process:
|
||||
for line in process.stdout:
|
||||
print(line, end="", flush=True)
|
||||
if re.match(r"^--- (PASS|SKIP): " + re.escape(name) + r"(?:\s|$)", line):
|
||||
completed = True
|
||||
code = process.wait()
|
||||
if code:
|
||||
raise subprocess.CalledProcessError(code, command)
|
||||
if not completed:
|
||||
raise RuntimeError("native installer check did not finish: " + name)
|
||||
|
||||
|
||||
def main(phase="all"):
|
||||
if phase not in ("all", "preflight", "remaining"):
|
||||
raise ValueError("unknown native installer phase")
|
||||
inventory = subprocess.run(
|
||||
["go", "test", "-list", ".", PACKAGE], cwd=ROOT,
|
||||
check=True, capture_output=True, text=True,
|
||||
)
|
||||
for group, names in zip(GROUPS, partition(inventory.stdout)):
|
||||
if phase == "preflight" and group != "nas-preflight":
|
||||
continue
|
||||
if phase == "remaining" and group == "nas-preflight":
|
||||
continue
|
||||
print(f"Native Unix installer {group}: {len(names)} discovered checks", flush=True)
|
||||
subprocess.run(
|
||||
["go", "test", "-count=1", "-timeout", "10m", "-run", "^(" + "|".join(names) + ")$", PACKAGE],
|
||||
cwd=ROOT, check=True,
|
||||
)
|
||||
with tempfile.TemporaryDirectory(prefix="pulse-native-contracts-") as directory:
|
||||
binary = Path(directory) / "installtests.test"
|
||||
subprocess.run(["go", "test", "-c", "-o", str(binary), PACKAGE],
|
||||
cwd=ROOT, check=True)
|
||||
inventory = subprocess.run([str(binary), "-test.list=."], cwd=ROOT / "scripts/installtests",
|
||||
check=True, capture_output=True, text=True)
|
||||
early, remaining = partition(inventory.stdout)
|
||||
selected = early if phase == "preflight" else remaining if phase == "remaining" else early + remaining
|
||||
print(f"Native Unix installer {phase}: {len(selected)} discovered checks", flush=True)
|
||||
for index, name in enumerate(selected, 1):
|
||||
print(f"Native Unix installer {index}/{len(selected)}: {name}", flush=True)
|
||||
run_check(binary, name, cwd=ROOT / "scripts/installtests")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user