feat(container): harden v0.1.1 publication
This commit is contained in:
@@ -24,13 +24,17 @@ class RecordingRunner:
|
||||
if command[:2] == ["docker", "load"]: return image_release.Result(f"Loaded image: {LOCAL}\n")
|
||||
if command[:3] == ["docker", "image", "inspect"] and "{{.Id}}" in command: return image_release.Result(IMAGE_ID + "\n")
|
||||
if command[:3] == ["docker", "image", "inspect"] and "{{.Config.User}}" in command: return image_release.Result("10001:10001\n")
|
||||
if command[:3] == ["docker", "image", "inspect"] and "{{.Architecture}}" in command: return image_release.Result("amd64\n")
|
||||
if command[:3] == ["docker", "container", "inspect"] and "{{.State.ExitCode}}" in command: return image_release.Result("0\n")
|
||||
if command[:3] == ["docker", "image", "inspect"] and any("org.opencontainers.image.version" in part for part in command): return image_release.Result("v1.2.3\n")
|
||||
if command[:3] == ["docker", "image", "inspect"]: return image_release.Result(COMMIT + "\n")
|
||||
if command[:2] == ["docker", "push"]: self.push_count += 1; return image_release.Result(f"digest: {DIGEST} size: 1\n")
|
||||
if command[:2] == ["docker", "create"]: return image_release.Result("created-container\n")
|
||||
if command[:3] == ["docker", "run", "--rm"] and "urllib.request" in command[-1]:
|
||||
enabled = "TARGET:8000" not in command[-1] and "plain" not in command[-1]
|
||||
return image_release.Result('{"status":"ok","db_enabled":' + str(enabled).lower() + '}')
|
||||
if command[:3] == ["docker", "run", "--rm"] and "alembic_version" in command[-1]: return image_release.Result("20260715_0028\n")
|
||||
if command[:3] == ["docker", "run", "--rm"] and "alembic_version" in command[-1]: return image_release.Result("20260814_0029\n")
|
||||
if command[:3] == ["docker", "run", "--rm"] and "health-check" in command: return image_release.Result('{"database_revision":"20260814_0029"}\n')
|
||||
return image_release.Result()
|
||||
|
||||
def artifacts(tmp_path: Path):
|
||||
@@ -58,11 +62,24 @@ def test_checkout_identity_is_exact():
|
||||
assert image_release.validate_checkout_identity(COMMIT, COMMIT) == COMMIT
|
||||
with pytest.raises(ValueError): image_release.validate_checkout_identity("d"*40, COMMIT)
|
||||
|
||||
|
||||
def test_project_version_must_match_release_tag(tmp_path):
|
||||
project = tmp_path / "pyproject.toml"
|
||||
project.write_text('[project]\nversion = "0.1.1"\n')
|
||||
assert image_release.project_release_version(project) == "v0.1.1"
|
||||
assert image_release.validate_release_event("push", "refs/tags/v0.1.1", COMMIT, "v0.1.1") == "v0.1.1"
|
||||
with pytest.raises(ValueError, match="project version"):
|
||||
image_release.validate_release_event("push", "refs/tags/v0.1.2", COMMIT, "v0.1.1")
|
||||
|
||||
def test_installed_smoke_has_import_cli_exact_inventory_and_cleanup():
|
||||
runner = RecordingRunner(); image_release.smoke_installed_image("candidate", ("001.py",), runner, 17)
|
||||
inventory = tuple(
|
||||
[f"202601{i:02d}_{i:04d}_migration.py" for i in range(1, 29)]
|
||||
+ ["20260814_0029_strategy_catalog_v1.py"]
|
||||
)
|
||||
runner = RecordingRunner(); image_release.smoke_installed_image("candidate", inventory, runner, 17)
|
||||
runs = [x[0] for x in runner.commands if x[0][:2] == ["docker","run"]]
|
||||
assert len(runs) == 3 and all("--read-only" in x and "none" in x for x in runs)
|
||||
assert "migration_inventory" in runs[-1][-1] and "001.py" in runs[-1][-1]
|
||||
assert "migration_inventory" in runs[-1][-1] and "20260814_0029" in runs[-1][-1]
|
||||
assert len([x for x in runner.commands if x[0][:3] == ["docker","rm","--force"]]) == 3
|
||||
|
||||
def _assert_exact_startup_cleanup(commands: list[list[str]]) -> None:
|
||||
|
||||
@@ -24,15 +24,44 @@ def test_tag_publication_is_strict_single_build_handoff_without_latest():
|
||||
assert "ref: ${{ github.sha }}" in text and text.count("docker build ")==1
|
||||
assert "docker build" not in publish and "packages: write" not in verify and "packages: write" in publish
|
||||
assert "tested-image/scripts/image_release.py publish" in publish and ":latest" not in text
|
||||
assert "--platform linux/amd64" in verify and '--build-arg "RELEASE_VERSION=${GITHUB_REF_NAME}"' in verify
|
||||
for name in ("portfolio-hub-image.tar","portfolio-hub-image.tar.sha256","portfolio-hub-image.id","migration-inventory.txt"):
|
||||
assert name in text
|
||||
|
||||
def test_dockerfile_python311_locked_non_root_explicit_uvicorn_and_resources():
|
||||
text=(REPO/"Dockerfile").read_text()
|
||||
assert text.count("FROM python:3.11-slim")==2 and "uv sync --locked --no-dev --no-editable" in text
|
||||
assert text.count("FROM python:3.11-slim@sha256:")==2 and "uv sync --locked --no-dev --no-editable" in text
|
||||
assert "FROM ghcr.io/astral-sh/uv:0.11.14@sha256:" in text
|
||||
assert text.startswith("# syntax=docker/dockerfile:1@sha256:")
|
||||
assert "USER 10001:10001" in text and 'CMD ["uvicorn", "app.main:app"' in text
|
||||
assert "COPY app/ ./app/" in text and "COPY ." not in text
|
||||
assert "org.opencontainers.image.source" in text and "org.opencontainers.image.revision" in text
|
||||
assert "org.opencontainers.image.version" in text and "ARG RELEASE_VERSION" in text
|
||||
|
||||
|
||||
def test_workflow_actions_are_commit_pinned_and_verify_version_is_project_derived():
|
||||
for workflow in (PUBLISH, VERIFY):
|
||||
for line in workflow.read_text().splitlines():
|
||||
if "uses: actions/" in line:
|
||||
revision = line.split("@", 1)[1].split()[0]
|
||||
assert len(revision) == 40 and all(char in "0123456789abcdef" for char in revision)
|
||||
verify = VERIFY.read_text()
|
||||
assert "tomllib" in verify and 'RELEASE_VERSION=${RELEASE_VERSION}' in verify
|
||||
assert 'RELEASE_VERSION=v0.1.1' not in verify
|
||||
|
||||
|
||||
def test_release_version_and_migration_contract_are_v011_head_0029():
|
||||
project = (REPO / "pyproject.toml").read_text()
|
||||
config = (REPO / "app/core/config.py").read_text()
|
||||
migrations = sorted(
|
||||
path.name
|
||||
for path in (REPO / "app/db/alembic/versions").glob("*.py")
|
||||
if path.name != "__init__.py"
|
||||
)
|
||||
assert 'version = "0.1.1"' in project
|
||||
assert 'app_version: str = "0.1.1"' in config
|
||||
assert len(migrations) == 29
|
||||
assert migrations[-1].startswith("20260814_0029")
|
||||
|
||||
def test_dockerignore_is_deny_by_default_required_inputs_only():
|
||||
lines=(REPO/".dockerignore").read_text().splitlines(); assert lines[0]=="**"
|
||||
|
||||
+16
-1
@@ -783,14 +783,29 @@ def test_cli_write_command_can_abort() -> None:
|
||||
assert repo._portfolio is None
|
||||
|
||||
|
||||
def test_cli_health_check_reports_repository() -> None:
|
||||
def test_cli_health_check_reports_repository(monkeypatch) -> None:
|
||||
repo = FakeRepo(_portfolio())
|
||||
runner = build_runner(repo)
|
||||
monkeypatch.setattr(health_commands, "current_revision", lambda _settings: "20260814_0029")
|
||||
monkeypatch.setattr(health_commands, "head_revision", lambda _settings: "20260814_0029")
|
||||
|
||||
result = runner.invoke(runner.app, ["health-check"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert "repository: FakeRepo" in result.stdout
|
||||
assert "database_revision: 20260814_0029" in result.stdout
|
||||
|
||||
|
||||
def test_cli_health_check_rejects_database_behind_packaged_head(monkeypatch) -> None:
|
||||
repo = FakeRepo(_portfolio())
|
||||
runner = build_runner(repo)
|
||||
monkeypatch.setattr(health_commands, "current_revision", lambda _settings: "20260715_0028")
|
||||
monkeypatch.setattr(health_commands, "head_revision", lambda _settings: "20260814_0029")
|
||||
|
||||
result = runner.invoke(runner.app, ["health-check"])
|
||||
|
||||
assert result.exit_code == 1
|
||||
assert "is not packaged head" in result.stdout
|
||||
|
||||
|
||||
def test_cli_init_db_reports_created(monkeypatch) -> None:
|
||||
|
||||
@@ -10,7 +10,7 @@ from unittest.mock import patch
|
||||
|
||||
_MIGRATION_PATH = (
|
||||
Path(__file__).parents[1]
|
||||
/ "migrations/versions/20260814_0029_strategy_catalog_v1.py"
|
||||
/ "app/db/alembic/versions/20260814_0029_strategy_catalog_v1.py"
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user