8 Commits
7 changed files with 3761 additions and 45 deletions
@@ -0,0 +1 @@
{"schemaVersion":"nodedc.mission-core-map-access.v1","state":"enabled"}
@@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""Deterministic data-only declaration for the registered NAS map access domain."""
import argparse
import gzip
import hashlib
import io
import json
import re
import tarfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def build(patch, state="enabled"):
if not re.fullmatch(r"[A-Za-z0-9._-]{1,96}", patch) or state not in ("enabled", "disabled"):
raise ValueError("Invalid map access release")
descriptor = json.loads((ROOT / "deployment/mission-core-map-access/access.json").read_text())
if descriptor != {"schemaVersion": "nodedc.mission-core-map-access.v1", "state": "enabled"}:
raise ValueError("Unexpected source contract")
descriptor["state"] = state
members = {
"manifest.env": f"id={patch}\ncomponent=mission-core-map-access\ntype=app-overlay\n".encode(),
"files.txt": b"access.json\n",
"payload/access.json": (json.dumps(descriptor, sort_keys=True, separators=(",", ":")) + "\n").encode(),
}
result = io.BytesIO()
with gzip.GzipFile(fileobj=result, mode="wb", filename="", mtime=0) as compressed:
with tarfile.open(fileobj=compressed, mode="w", format=tarfile.USTAR_FORMAT) as archive:
for name, content in members.items():
member = tarfile.TarInfo(name); member.mode = 0o644; member.size = len(content)
archive.addfile(member, io.BytesIO(content))
return result.getvalue()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("patch")
parser.add_argument("--state", choices=("enabled", "disabled"), default="enabled")
args = parser.parse_args()
raw = build(args.patch, args.state)
target = ROOT / "infra/deploy-artifacts" / ("nodedc-" + args.patch + ".tgz")
target.parent.mkdir(parents=True, exist_ok=True)
with target.open("xb") as output:
output.write(raw)
digest = hashlib.sha256(raw).hexdigest()
target.with_suffix(target.suffix + ".sha256").write_text(digest + " " + target.name + "\n")
print(json.dumps({"artifact": str(target), "sha256": digest, "bytes": len(raw)}))
@@ -0,0 +1,126 @@
#!/usr/bin/env node
import { createHash } from "node:crypto";
import { spawnSync } from "node:child_process";
import { cp, lstat, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const scriptDir = dirname(fileURLToPath(import.meta.url));
const platformRoot = resolve(scriptDir, "../..");
const taskerRoot = resolve(
process.env.NODEDC_TASKMANAGER_ROOT || resolve(platformRoot, "../../data/dc_taskmanager/NODEDC_TASKMANAGER"),
);
const artifactDir = resolve(process.env.NODEDC_DEPLOY_ARTIFACT_DIR || resolve(scriptDir, "../deploy-artifacts"));
const [release = "20260829-001", ...extra] = process.argv.slice(2);
if (extra.length || release !== "20260829-001") {
throw new Error("usage: build-tasker-attachment-formats-artifact.mjs [20260829-001]");
}
const descriptor = {
artifactBasename: `nodedc-tasker-attachment-formats-${release}.tgz`,
component: "tasker",
expectedCommit: "f9308539bfe71e4e2359c47ed5c230fa1fb386c9",
files: [
"plane-src/apps/api/plane/settings/common.py",
"plane-src/apps/web/core/components/issues/attachment/attachment-list-item.tsx",
"plane-src/apps/web/core/components/issues/peek-overview/view.tsx",
"plane-src/apps/web/styles/globals.css",
"plane-src/packages/services/src/file/helper.ts",
],
patchId: `tasker-attachment-formats-${release}`,
sourceRoot: taskerRoot,
};
const sourceCommit = gitOutput(descriptor.sourceRoot, ["rev-parse", "HEAD"]);
if (sourceCommit !== descriptor.expectedCommit) {
throw new Error(`source_commit_mismatch:${descriptor.component}:${sourceCommit}`);
}
const sourceStatus = gitOutput(descriptor.sourceRoot, ["status", "--porcelain"]);
if (sourceStatus) throw new Error(`source_worktree_not_clean:${descriptor.component}`);
await mkdir(artifactDir, { recursive: true });
const stage = await mkdtemp(join(tmpdir(), "nodedc-tasker-attachment-formats-"));
const payload = join(stage, "payload");
const artifact = join(artifactDir, descriptor.artifactBasename);
try {
await assertFresh(artifact);
await mkdir(payload, { recursive: true });
for (const relativePath of descriptor.files) {
const source = resolve(descriptor.sourceRoot, relativePath);
const sourceStat = await lstat(source);
if (!sourceStat.isFile() || sourceStat.isSymbolicLink()) {
throw new Error(`source_file_rejected:${descriptor.component}:${relativePath}`);
}
const destination = join(payload, relativePath);
await mkdir(dirname(destination), { recursive: true });
await cp(source, destination, { force: false, verbatimSymlinks: true });
}
await writeFile(
join(stage, "manifest.env"),
`id=${descriptor.patchId}\ncomponent=${descriptor.component}\ntype=app-overlay\n`,
"utf8",
);
await writeFile(join(stage, "files.txt"), `${descriptor.files.join("\n")}\n`, "utf8");
const tar = spawnSync("python3", ["-c", canonicalTarScript(), artifact, stage], {
encoding: "utf8",
maxBuffer: 128 * 1024 * 1024,
});
if (tar.status !== 0) throw new Error(`tar_failed:${descriptor.component}:${tar.stderr || tar.stdout}`);
const sha256 = createHash("sha256").update(await readFile(artifact)).digest("hex");
console.log(
JSON.stringify(
{
ok: true,
release,
artifact,
component: descriptor.component,
files: descriptor.files,
patchId: descriptor.patchId,
sha256,
sourceCommit,
},
null,
2,
),
);
} finally {
await rm(stage, { recursive: true, force: true });
}
function gitOutput(cwd, args) {
const result = spawnSync("git", args, { cwd, encoding: "utf8" });
if (result.status !== 0) throw new Error(`git_failed:${cwd}:${args.join("_")}:${result.stderr || result.stdout}`);
return result.stdout.trim();
}
async function assertFresh(path) {
try {
await lstat(path);
} catch (error) {
if (error?.code === "ENOENT") return;
throw error;
}
throw new Error(`output_already_exists:${path}`);
}
function canonicalTarScript() {
return [
"import gzip,io,pathlib,sys,tarfile",
"root=pathlib.Path(sys.argv[2])",
"with open(sys.argv[1],'wb') as out:",
" with gzip.GzipFile(filename='',mode='wb',fileobj=out,compresslevel=9,mtime=0) as gz:",
" with tarfile.open(fileobj=gz,mode='w',format=tarfile.PAX_FORMAT) as tar:",
" for top in ('manifest.env','files.txt','payload'):",
" p=root/top; paths=[p]+(sorted(p.rglob('*')) if p.is_dir() else [])",
" for x in paths:",
" info=tar.gettarinfo(str(x),arcname=x.relative_to(root).as_posix())",
" info.uid=info.gid=0; info.uname=info.gname='root'; info.mtime=0; info.mode=0o755 if info.isdir() else 0o644",
" with (open(x,'rb') if info.isfile() else io.BytesIO()) as src: tar.addfile(info,src if info.isfile() else None)",
].join("\n");
}
File diff suppressed because it is too large Load Diff
@@ -55,6 +55,154 @@ class DevicePlaneRegistryTest(unittest.TestCase):
("device-manager",),
)
def test_manager_v10_pins_successful_v8_and_control_core_v4(self):
self.assertEqual(
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V10_PREDECESSOR_PATCH_ID,
"device-manager-release-v8-20260822-039",
)
self.assertEqual(
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V10_CONTROL_CORE_PREDECESSOR_PATCH_ID,
"device-control-core-release-v4-20260823-047",
)
boundaries = RUNNER.expected_device_plane_manager_release_v10_boundaries()
self.assertEqual(
boundaries["controlCorePredecessor"]["artifactSha256"],
"4aecceeb8d400fdd3dbec8fc86b151691f389d165e72677f396f8994823b6b5c",
)
self.assertEqual(
boundaries["telemetryWorkspace"],
"mission-core-compute-module-parity-v1",
)
self.assertTrue(
RUNNER.is_device_plane_manager_release_v10_slice(
"device-plane",
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V10_ENTRIES,
)
)
self.assertEqual(
RUNNER.component_services(
"device-plane",
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V10_ENTRIES,
),
("device-manager",),
)
def test_manager_v11_pins_v10_and_mission_core_visual_parity(self):
self.assertEqual(
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V11_PREDECESSOR_PATCH_ID,
"device-manager-release-v10-20260823-048",
)
self.assertEqual(
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V11_PREDECESSOR_ARTIFACT_SHA256,
"e6b983a314db4f8c27d89062dfedf5ed0523cc30421170799d181a19e2d85d4c",
)
boundaries = RUNNER.expected_device_plane_manager_release_v11_boundaries()
self.assertEqual(
boundaries["missionCoreReference"],
"compute-modules-workspace-71c8b04",
)
self.assertEqual(
boundaries["telemetryWorkspace"],
"mission-core-compute-module-visual-parity-v2",
)
self.assertEqual(
boundaries["telemetrySurface"],
"borderless-soft-surface-v1",
)
self.assertEqual(
boundaries["telemetryScroll"],
"reset-on-workspace-transition-v1",
)
self.assertTrue(
RUNNER.is_device_plane_manager_release_v11_slice(
"device-plane",
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V11_ENTRIES,
)
)
self.assertEqual(
RUNNER.component_services(
"device-plane",
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V11_ENTRIES,
),
("device-manager",),
)
def test_manager_v12_pins_v11_and_adaptive_telemetry_graphs(self):
self.assertEqual(
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V12_PREDECESSOR_PATCH_ID,
"device-manager-release-v11-20260823-049",
)
self.assertEqual(
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V12_PREDECESSOR_ARTIFACT_SHA256,
"c1e2056b50bfbb0d03d077461d0c27cc56cc52967c3f5620be14871c8a6d5cf0",
)
boundaries = RUNNER.expected_device_plane_manager_release_v12_boundaries()
self.assertEqual(
boundaries["telemetryWorkspace"],
"mission-core-compute-module-adaptive-window-v3",
)
self.assertEqual(
boundaries["telemetryGraphScale"],
"adaptive-observed-window-explicit-domain-v1",
)
self.assertEqual(
boundaries["telemetryNetworkMissingSemantics"],
"missing-counters-never-zero-v1",
)
self.assertTrue(
RUNNER.is_device_plane_manager_release_v12_slice(
"device-plane",
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V12_ENTRIES,
)
)
self.assertEqual(
RUNNER.component_services(
"device-plane",
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V12_ENTRIES,
),
("device-manager",),
)
def test_manager_v13_pins_v12_and_host_inventory_accordion(self):
self.assertEqual(
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V13_PREDECESSOR_PATCH_ID,
"device-manager-release-v12-20260823-050",
)
self.assertEqual(
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V13_PREDECESSOR_ARTIFACT_SHA256,
"1a49839140e5f2e49763d78f24ee47d946e244bcfde15a9c38266e8bd14c0d49",
)
boundaries = RUNNER.expected_device_plane_manager_release_v13_boundaries()
self.assertEqual(
boundaries["hostInventoryComposition"],
"mission-core-compute-host-accordion-v2",
)
self.assertEqual(
boundaries["hostInventoryRow"],
"compact-centered-accordion-v1",
)
self.assertEqual(
boundaries["hostInventoryRelations"],
"host-scoped-endpoint-deployment-service-v1",
)
self.assertEqual(
boundaries["telemetryWorkspace"],
"mission-core-compute-module-adaptive-window-v3",
)
self.assertTrue(
RUNNER.is_device_plane_manager_release_v13_slice(
"device-plane",
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V13_ENTRIES,
)
)
self.assertEqual(
RUNNER.component_services(
"device-plane",
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V13_ENTRIES,
),
("device-manager",),
)
def test_control_core_v3_registers_telemetry_contract_slice(self):
predecessor = {
"kind": "release",
@@ -83,6 +231,109 @@ class DevicePlaneRegistryTest(unittest.TestCase):
)
)
def test_control_core_v4_pins_terminal_recovery_046(self):
predecessor = {
"kind": "migration-replay-checkpoint-recovery",
"patchId": (
"device-control-core-migration-replay-checkpoint-recovery-"
"20260822-046"
),
"artifactSha256": (
"46000c76977fb583fc7c9cf74ecf624efd8b404f7b8d0322e0270e7b8ac6e450"
),
}
descriptor = RUNNER.expected_device_plane_control_core_release_descriptor(
RUNNER.DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_PATCH_ID,
predecessor,
schema_version="v4",
)
self.assertEqual(
descriptor["schemaVersion"],
"nodedc.device-plane.device-control-core-release.v4",
)
self.assertEqual(
descriptor["recoveryPredecessor"],
"terminal-applied-046-exact-source-runtime-database",
)
self.assertEqual(
descriptor["databaseSchemaOutcome"],
"migration-017-host-telemetry-table-present",
)
self.assertTrue(
RUNNER.is_device_plane_control_core_release_v4_slice(
"device-plane",
RUNNER.DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES,
)
)
self.assertEqual(
RUNNER.component_services(
"device-plane",
RUNNER.DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES,
),
("device-control-core",),
)
def test_control_core_v4_health_contract_keeps_typed_commands(self):
checks = RUNNER.component_healthchecks(
"device-plane",
RUNNER.DEVICE_PLANE_CONTROL_CORE_RELEASE_V4_ENTRIES,
("device-control-core",),
)
self.assertEqual(
checks[0]["expected_json"]["commandTransport"],
"typed-service-ping-v1",
)
def test_control_core_v4_telemetry_database_acceptance(self):
result = mock.Mock(
returncode=0,
stdout="true\t19\t2\ttrue\t0\t0\n",
stderr="",
)
with (
mock.patch.object(
RUNNER,
"device_plane_service_container_ids",
return_value=("a" * 64,),
),
mock.patch.object(
RUNNER.subprocess,
"run",
return_value=result,
),
):
evidence = (
RUNNER.collect_device_plane_control_core_host_telemetry_database_evidence()
)
self.assertTrue(evidence["tablePresent"])
self.assertEqual(evidence["columnCount"], 19)
self.assertEqual(evidence["indexCount"], 2)
self.assertEqual(evidence["expiredSampleCount"], 0)
def test_control_core_v4_telemetry_database_rejects_missing_index(self):
result = mock.Mock(
returncode=0,
stdout="true\t19\t1\ttrue\t0\t0\n",
stderr="",
)
with (
mock.patch.object(
RUNNER,
"device_plane_service_container_ids",
return_value=("a" * 64,),
),
mock.patch.object(
RUNNER.subprocess,
"run",
return_value=result,
),
self.assertRaisesRegex(
RUNNER.DeployError,
"host telemetry database invariant mismatch",
),
):
RUNNER.collect_device_plane_control_core_host_telemetry_database_evidence()
def test_manager_v8_pins_production_v6_and_canonical_ontology_runtime(self):
self.assertEqual(
RUNNER.DEVICE_PLANE_MANAGER_RELEASE_V8_PREDECESSOR_PATCH_ID,
@@ -417,11 +668,20 @@ class DevicePlaneRegistryTest(unittest.TestCase):
"Health": {"Status": "unhealthy"},
},
}
preserved_inventory = {
**inventory,
"services": inventory["services"][1:],
}
with (
mock.patch.object(
RUNNER,
"healthcheck_compose_service_with_grace",
) as preserved_health,
mock.patch.object(
RUNNER,
"device_plane_runtime_inventory",
return_value=preserved_inventory,
),
mock.patch.object(
RUNNER,
"compose_service_container_id",
@@ -0,0 +1,95 @@
import hashlib
import importlib.machinery
import importlib.util
import io
import json
import stat
from types import SimpleNamespace
import tarfile
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
ROOT = Path(__file__).resolve().parent
def load(name, path):
loader = importlib.machinery.SourceFileLoader(name, str(path))
spec = importlib.util.spec_from_loader(name, loader)
module = importlib.util.module_from_spec(spec); loader.exec_module(module)
return module
R = load("map_access_runner", ROOT / "nodedc-deploy")
B = load("map_access_builder", ROOT / "build-mission-core-map-access-artifact.py")
class MapAccessTests(unittest.TestCase):
def test_scope_never_selects_docker_or_accepts_host_policy_payload(self):
name = "mission-core-map-access"
self.assertEqual(R.component_services(name, ["access.json"]), ())
self.assertEqual(R.component_builds(name, ["access.json"]), ())
self.assertTrue(R.component_artifact_only(name))
self.assertTrue(R.allowed_payload_path(name, "access.json"))
for path in ("sshd_config", "../sshd_config", "script.py", "docker-compose.yml", ".env", "secrets/key"):
with self.assertRaises(R.DeployError): R.allowed_payload_path(name, path)
def test_deterministic_data_bundle_and_fail_closed_descriptor(self):
raw = B.build("map-access-test-001")
self.assertEqual(raw, B.build("map-access-test-001"))
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
artifact = root / "candidate.tgz"
artifact.write_bytes(raw)
with tarfile.open(fileobj=io.BytesIO(raw)) as archive:
self.assertEqual(archive.getnames(), ["manifest.env", "files.txt", "payload/access.json"])
unpacked = root / "unpacked"
unpacked.mkdir()
manifest, entries, payload = R.load_artifact(artifact, unpacked)
self.assertEqual(manifest["component"], "mission-core-map-access")
self.assertEqual(R.read_map_access_descriptor(payload, entries), "enabled")
for value in ({"schemaVersion":"nodedc.mission-core-map-access.v1","state":"enabled","user":"root"}, {"schemaVersion":"unknown","state":"enabled"}):
(payload/"access.json").write_text(json.dumps(value))
with self.assertRaises(R.DeployError): R.read_map_access_descriptor(payload, entries)
def test_exact_predecessor_idempotence_and_explicit_disable(self):
base = b"AllowTcpForwarding no\n"
with patch.object(R, "MAP_ACCESS_BASE_SHA", hashlib.sha256(base).hexdigest()):
enabled = R.map_access_policy(base, "enabled")
self.assertEqual(enabled, base + R.MAP_ACCESS_APPEND)
self.assertEqual(R.map_access_policy(enabled, "enabled"), enabled)
self.assertEqual(R.map_access_policy(enabled, "disabled"), base)
for drift in (base+b"# external drift\n", enabled+b"# external drift\n", enabled+R.MAP_ACCESS_APPEND):
with self.assertRaises(R.DeployError): R.map_access_policy(drift, "enabled")
def test_effective_policy_may_only_change_exact_user_and_destination(self):
with tempfile.TemporaryDirectory() as tmp, patch.object(R, "TMP_DIR", Path(tmp)), patch.object(R.subprocess,"run") as run:
run.return_value.returncode=0
candidate=Path(tmp)/"candidate";candidate.write_bytes(R.MAP_ACCESS_APPEND)
baseline={"allowtcpforwarding":"no", "permitopen":"any", "passwordauthentication":"yes"}
def effective(config,user,address):
value=dict(baseline)
if str(config)==str(candidate) and user=="dctouch":value.update(allowtcpforwarding="local",permitopen="127.0.0.1:18103")
return value
with patch.object(R,"map_access_effective",side_effect=effective):R.map_access_validate_candidate(b"baseline",candidate)
def broad(config,user,address):
value=effective(config,user,address)
if str(config)==str(candidate):value["permitopen"]="any"
return value
with patch.object(R,"map_access_effective",side_effect=broad), self.assertRaises(R.DeployError):R.map_access_validate_candidate(b"baseline",candidate)
def test_reload_failure_is_routed_to_domain_rollback(self):
with tempfile.TemporaryDirectory() as tmp, patch.object(R,"preflight_map_access",return_value=(b"old",b"new")), patch.object(R,"map_access_replace") as replace, patch.object(R,"map_access_reload",side_effect=R.DeployError("reload")):
with self.assertRaises(R.DeployError):R.apply_map_access(Path(tmp),["access.json"])
replace.assert_called_once_with(b"new")
def test_rollback_restores_exact_policy_and_descriptor(self):
base=b"AllowTcpForwarding no\n"
with tempfile.TemporaryDirectory() as tmp:
root=Path(tmp); backup=root/"backup"; backup.mkdir()
(backup/"sshd-config-before").write_bytes(base)
calls=[]
metadata=SimpleNamespace(st_mode=stat.S_IFREG|0o600,st_uid=0)
with patch.object(R,"MAP_ACCESS_BASE_SHA",hashlib.sha256(base).hexdigest()), patch.object(Path,"lstat",return_value=metadata), patch.object(R,"map_access_read_config",side_effect=[base+R.MAP_ACCESS_APPEND,base]), patch.object(R,"map_access_validate_candidate"), patch.object(R,"map_access_replace",side_effect=lambda value:calls.append(("replace",value))), patch.object(R,"map_access_reload",side_effect=lambda:calls.append(("reload",))), patch.object(R,"restore_overlay_source",side_effect=lambda *args:calls.append(("restore",))):
R.rollback_map_access(root,backup,["access.json"],"stamp")
self.assertEqual(calls,[("replace",base),("reload",),("restore",)])
if __name__ == "__main__": unittest.main()
@@ -0,0 +1,109 @@
#!/usr/bin/env python3
import hashlib
import importlib.machinery
import importlib.util
import json
import os
import subprocess
import tempfile
import unittest
from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
PLATFORM_ROOT = SCRIPT_DIR.parent.parent
BUILDER_PATH = SCRIPT_DIR / "build-tasker-attachment-formats-artifact.mjs"
RUNNER_PATH = SCRIPT_DIR / "nodedc-deploy"
RELEASE = "20260829-001"
SOURCE_COMMIT = "f9308539bfe71e4e2359c47ed5c230fa1fb386c9"
EXPECTED_FILES = {
"plane-src/apps/api/plane/settings/common.py",
"plane-src/apps/web/core/components/issues/attachment/attachment-list-item.tsx",
"plane-src/apps/web/core/components/issues/peek-overview/view.tsx",
"plane-src/apps/web/styles/globals.css",
"plane-src/packages/services/src/file/helper.ts",
}
def load_runner():
loader = importlib.machinery.SourceFileLoader("nodedc_tasker_attachment_formats_runner", str(RUNNER_PATH))
spec = importlib.util.spec_from_loader(loader.name, loader)
module = importlib.util.module_from_spec(spec)
loader.exec_module(module)
return module
RUNNER = load_runner()
class TaskerAttachmentFormatsArtifactTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.temporary = tempfile.TemporaryDirectory(prefix="nodedc-tasker-attachment-formats-")
cls.root = Path(cls.temporary.name)
cls.builds = []
for index in range(2):
output = cls.root / f"build-{index}"
output.mkdir()
env = os.environ.copy()
env["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(output)
result = subprocess.run(
["node", str(BUILDER_PATH), RELEASE],
cwd=PLATFORM_ROOT,
env=env,
check=True,
capture_output=True,
text=True,
)
cls.builds.append(json.loads(result.stdout))
@classmethod
def tearDownClass(cls):
cls.temporary.cleanup()
def test_artifact_is_reproducible_and_runner_accepted(self):
first = self.builds[0]
second = self.builds[1]
first_path = Path(first["artifact"])
second_path = Path(second["artifact"])
first_bytes = first_path.read_bytes()
self.assertEqual(first_bytes, second_path.read_bytes())
self.assertEqual(first_bytes[4:8], bytes(4))
self.assertEqual(first["sha256"], hashlib.sha256(first_bytes).hexdigest())
self.assertEqual(first["sourceCommit"], SOURCE_COMMIT)
self.assertEqual(first["patchId"], f"tasker-attachment-formats-{RELEASE}")
self.assertEqual(set(first["files"]), EXPECTED_FILES)
with tempfile.TemporaryDirectory() as directory:
manifest, entries, payload = RUNNER.load_artifact(first_path, Path(directory))
self.assertEqual(manifest, {
"id": first["patchId"],
"component": "tasker",
"type": "app-overlay",
})
self.assertEqual(entries, first["files"])
for relative_path in entries:
self.assertTrue((payload / relative_path).is_file())
def test_runner_selects_only_registered_tasker_scope(self):
entries = self.builds[0]["files"]
self.assertEqual(RUNNER.component_services("tasker", entries), ("api", "worker", "beat-worker", "web"))
self.assertEqual(len(RUNNER.component_builds("tasker", entries)), 2)
def test_builder_rejects_an_unexpected_release(self):
env = os.environ.copy()
env["NODEDC_DEPLOY_ARTIFACT_DIR"] = str(self.root / "unexpected-release")
result = subprocess.run(
["node", str(BUILDER_PATH), "20260829-002"],
cwd=PLATFORM_ROOT,
env=env,
capture_output=True,
text=True,
)
self.assertNotEqual(result.returncode, 0)
self.assertIn("usage: build-tasker-attachment-formats-artifact.mjs", result.stderr)
if __name__ == "__main__":
unittest.main(verbosity=2)