A `review` target in the Dockerfile: its own user (nisse-review, uid 10002), git for its clones, state and clones in /var/lib/nisse-review, configuration at /etc/nisse-review, and only its own agent installed. release.yml publishes gitea.dongar.net/dev/nisse-review beside nisse-dev, at the same version. review-bringup.sh builds and runs the image, as bringup.sh runs nisse-dev's. The test stage adds an agent package its base lacks, without its dependencies, since the suite's own code imports every agent's. Without that, the tests over nisse-dev's image no longer import since test_review_live arrived. tests/deploy/test_images.py checks the image and that the test stage leaves what it installed as it was. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
97 lines
3.1 KiB
Python
97 lines
3.1 KiB
Python
"""Every agent is its own image (Development Plan §3, M7.6): nisse-review's
|
|
runs as its own user with git and only its own agent, and the test stage
|
|
over it keeps what it installed. Builds the images, so it needs Docker."""
|
|
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).parent.parent.parent
|
|
pytestmark = [
|
|
pytest.mark.integration,
|
|
pytest.mark.skipif(
|
|
shutil.which("docker") is None
|
|
or subprocess.run(
|
|
["docker", "info"], capture_output=True, check=False
|
|
).returncode,
|
|
reason="needs Docker",
|
|
),
|
|
]
|
|
|
|
# The installed packages' files and their times, hashed: the same in an
|
|
# image and the test stage over it means the tests ran against what was
|
|
# built, not a reinstall (even one from the same source).
|
|
HASH = (
|
|
"import hashlib, pathlib, nisse_review, nisse_core; h = hashlib.sha256(); "
|
|
"[h.update(p.read_bytes() + str(p.stat().st_mtime_ns).encode()) for m in (nisse_review, nisse_core) "
|
|
"for p in sorted(pathlib.Path(m.__file__).parent.rglob('*.py'))]; "
|
|
"print(h.hexdigest())"
|
|
)
|
|
|
|
|
|
def build(tag: str, *args: str) -> str:
|
|
subprocess.run(
|
|
["docker", "build", "--quiet", "-t", tag, *args, str(ROOT)],
|
|
check=True,
|
|
capture_output=True,
|
|
timeout=1200,
|
|
)
|
|
return tag
|
|
|
|
|
|
def sh(image: str, script: str) -> subprocess.CompletedProcess[str]:
|
|
return subprocess.run(
|
|
["docker", "run", "--rm", "--entrypoint", "sh", image, "-c", script],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=120,
|
|
check=False,
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def review() -> str:
|
|
return build("nisse-review:image-test", "--target", "review")
|
|
|
|
|
|
def test_the_reviewer_runs_as_its_own_user_with_git(review: str) -> None:
|
|
done = sh(review, "id -un; git --version; command -v nisse-review")
|
|
user, git, entry = done.stdout.splitlines()
|
|
assert user == "nisse-review" and git.startswith("git version")
|
|
assert entry == "/opt/nisse/bin/nisse-review"
|
|
inspected = subprocess.run(
|
|
["docker", "inspect", "-f", "{{json .Config.Volumes}}", review],
|
|
capture_output=True,
|
|
text=True,
|
|
check=True,
|
|
).stdout
|
|
assert "/var/lib/nisse-review" in inspected
|
|
|
|
|
|
def test_the_reviewer_image_holds_no_other_agent(review: str) -> None:
|
|
assert sh(review, "python -c 'import nisse_review'").returncode == 0
|
|
assert sh(review, "python -c 'import nisse_dev'").returncode != 0
|
|
|
|
|
|
def test_its_state_is_writable_and_its_config_folder_is_not(review: str) -> None:
|
|
assert sh(review, "touch /var/lib/nisse-review/x").returncode == 0
|
|
assert sh(review, "touch /etc/nisse-review/x").returncode != 0
|
|
|
|
|
|
def test_the_test_stage_over_it_keeps_what_it_installed(review: str) -> None:
|
|
tests = build(
|
|
"nisse-review:image-test-tests",
|
|
"--target",
|
|
"test",
|
|
"--build-arg",
|
|
f"TEST_BASE={review}",
|
|
)
|
|
both = sh(tests, "python -c 'import nisse_dev, nisse_review, pytest'")
|
|
assert both.returncode == 0, both.stderr # the suite's imports resolve
|
|
assert (
|
|
sh(review, f'python -c "{HASH}"').stdout
|
|
== sh(tests, f'python -c "{HASH}"').stdout
|
|
)
|