136 lines
4.5 KiB
Python
136 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import json
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
SCRIPT = ROOT / "scripts" / "build_release_package.py"
|
|
|
|
|
|
def load_script():
|
|
spec = importlib.util.spec_from_file_location("build_release_package", SCRIPT)
|
|
assert spec is not None and spec.loader is not None
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def test_release_version_is_consistent_across_runtime_packages() -> None:
|
|
version = (ROOT / "VERSION").read_text(encoding="utf-8").strip()
|
|
config = (ROOT / "backend" / "app" / "core" / "config.py").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
pyproject = (ROOT / "backend" / "pyproject.toml").read_text(encoding="utf-8")
|
|
frontend = json.loads(
|
|
(ROOT / "frontend" / "package.json").read_text(encoding="utf-8")
|
|
)
|
|
package_lock = json.loads(
|
|
(ROOT / "frontend" / "package-lock.json").read_text(encoding="utf-8")
|
|
)
|
|
|
|
assert version == "1.0.0"
|
|
assert f'default="{version}"' in config
|
|
assert "GEOINTEL_APP_VERSION" in config
|
|
assert 'version = "1.0.0"' in pyproject
|
|
assert frontend["version"] == version
|
|
assert package_lock["version"] == version
|
|
assert package_lock["packages"][""]["version"] == version
|
|
|
|
|
|
def test_release_image_carries_semantic_version_identity() -> None:
|
|
dockerfile = (ROOT / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
deploy = (ROOT / "deploy" / "unraid" / "deploy-release.sh").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
|
|
assert "ARG GEOINTEL_APP_VERSION=1.0.0" in dockerfile
|
|
assert 'org.opencontainers.image.version="${GEOINTEL_APP_VERSION}"' in dockerfile
|
|
assert "GEOINTEL_APP_VERSION=\"$(tr -d '[:space:]' < VERSION)\"" in deploy
|
|
assert "--build-arg GEOINTEL_APP_VERSION=" in deploy
|
|
assert "stored_version" in deploy
|
|
|
|
|
|
@pytest.mark.skipif(shutil.which("ssh-keygen") is None, reason="ssh-keygen unavailable")
|
|
def test_release_package_signature_and_checksums_fail_closed(tmp_path: Path) -> None:
|
|
module = load_script()
|
|
key = tmp_path / "release-key"
|
|
result = subprocess.run(
|
|
["ssh-keygen", "-q", "-t", "ed25519", "-N", "", "-f", str(key)],
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
|
|
package = tmp_path / "package"
|
|
package.mkdir()
|
|
evidence = package / "readiness.txt"
|
|
evidence.write_text("passed\n", encoding="utf-8")
|
|
nested_checksums = package / "backup" / module.CHECKSUMS_NAME
|
|
nested_checksums.parent.mkdir()
|
|
nested_checksums.write_text("backup evidence\n", encoding="utf-8")
|
|
identity = "geointel-release"
|
|
namespace = "geointel-release"
|
|
(package / module.SIGNERS_NAME).write_text(
|
|
f"{identity} {module.public_key(key)}\n",
|
|
encoding="utf-8",
|
|
)
|
|
manifest = {
|
|
"schema_version": 1,
|
|
"release_id": "v1.0.0",
|
|
"version": "1.0.0",
|
|
"scope": "Belgium and the Belgian North Sea",
|
|
"signature": {"identity": identity, "namespace": namespace},
|
|
"evidence": [
|
|
{
|
|
"path": evidence.name,
|
|
"size_bytes": evidence.stat().st_size,
|
|
"sha256": module.sha256(evidence),
|
|
}
|
|
],
|
|
}
|
|
manifest_path = package / module.MANIFEST_NAME
|
|
manifest_path.write_text(json.dumps(manifest) + "\n", encoding="utf-8")
|
|
module.run(
|
|
(
|
|
"ssh-keygen",
|
|
"-Y",
|
|
"sign",
|
|
"-f",
|
|
str(key),
|
|
"-n",
|
|
namespace,
|
|
str(manifest_path),
|
|
)
|
|
)
|
|
module.write_checksums(package)
|
|
|
|
verified = module.verify_package(package)
|
|
assert verified["release_id"] == "v1.0.0"
|
|
|
|
evidence.write_text("tampered\n", encoding="utf-8")
|
|
with pytest.raises(RuntimeError, match="Checksum mismatch"):
|
|
module.verify_package(package)
|
|
|
|
|
|
def test_release_package_cli_requires_tagged_clean_revision() -> None:
|
|
source = SCRIPT.read_text(encoding="utf-8")
|
|
readiness = (ROOT / "scripts" / "run_readiness_check.sh").read_text(
|
|
encoding="utf-8"
|
|
)
|
|
|
|
assert 'run(("git", "status", "--porcelain=v1"))' in source
|
|
assert 'run(("git", "rev-list", "-n", "1", release_id))' in source
|
|
assert "Image revision must equal the tagged Git commit" in source
|
|
assert "ssh-keygen" in source
|
|
assert "verify_checksums(package_dir)" in source
|
|
assert "py_compile scripts/build_release_package.py" in readiness
|