Package runtime migration scripts correctly
Dependency Audit / dependency-audit (push) Successful in 1m45s
Deployment Installer / deployment-installer (push) Successful in 6s
Security Audit / security-audit (push) Successful in 11m1s

This commit is contained in:
2026-08-03 17:54:09 +02:00
parent ff8ee991c3
commit 25424187a8
4 changed files with 69 additions and 0 deletions
+33
View File
@@ -53,6 +53,39 @@ jobs:
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020
with: with:
node-version: "22" node-version: "22"
- name: Validate immutable release inputs
env:
VERSION: ${{ inputs.version }}
PYTHON_IMAGE: ${{ inputs.python_image }}
NGINX_IMAGE: ${{ inputs.nginx_image }}
POSTGRES_IMAGE: ${{ inputs.postgres_image }}
REDIS_IMAGE: ${{ inputs.redis_image }}
LOAD_BALANCER_IMAGE: ${{ inputs.load_balancer_image }}
MANAGED_INGRESS_IMAGE: ${{ inputs.managed_ingress_image }}
GARAGE_IMAGE: ${{ inputs.garage_image }}
TEST_MAIL_IMAGE: ${{ inputs.test_mail_image }}
run: |
python - <<'PY'
import os
import re
version = os.environ["VERSION"]
if re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+(?:[-+][A-Za-z0-9.-]+)?", version) is None:
raise SystemExit("version must be a SemVer value without a leading v")
image_pattern = re.compile(r"^[^@\s]+@sha256:[0-9a-f]{64}$")
for name in (
"PYTHON_IMAGE",
"NGINX_IMAGE",
"POSTGRES_IMAGE",
"REDIS_IMAGE",
"LOAD_BALANCER_IMAGE",
"MANAGED_INGRESS_IMAGE",
"GARAGE_IMAGE",
"TEST_MAIL_IMAGE",
):
if image_pattern.fullmatch(os.environ[name]) is None:
raise SystemExit(f"{name} must be an exact sha256 image reference")
PY
- name: Use HTTPS for GovOPlaN repositories - name: Use HTTPS for GovOPlaN repositories
run: | run: |
git config --global --add url."https://git.add-ideas.de/GovOPlaN/govoplan".insteadOf "git@git.add-ideas.de:GovOPlaN/govoplan" git config --global --add url."https://git.add-ideas.de/GovOPlaN/govoplan".insteadOf "git@git.add-ideas.de:GovOPlaN/govoplan"
@@ -148,6 +148,10 @@ single-file deployer, its detached Ed25519 signature, and a signed, expiring
distribution manifest. Evidence generation and signing run through the distribution manifest. Evidence generation and signing run through the
workflow's isolated release Python environment so their cryptographic tooling workflow's isolated release Python environment so their cryptographic tooling
is explicit and independent of packages preinstalled in the Actions runner. is explicit and independent of packages preinstalled in the Actions runner.
The API image points Core at the migration scripts installed from the verified
wheel under `/opt/govoplan/runtime/govoplan_core_runtime`; migrations therefore
do not depend on a source checkout or the build host's Python installation
scheme.
Before publication, the exact amd64 and arm64 image manifests each run release Before publication, the exact amd64 and arm64 image manifests each run release
migrations against the pinned PostgreSQL image, reach API and WebUI readiness migrations against the pinned PostgreSQL image, reach API and WebUI readiness
as non-root/read-only processes, and complete a task through the pinned Redis as non-root/read-only processes, and complete a task through the pinned Redis
+31
View File
@@ -68,6 +68,37 @@ class RuntimeDistributionBuildTests(unittest.TestCase):
workflow, workflow,
) )
def test_workflow_rejects_missing_or_mutable_image_inputs_before_build(self) -> None:
workflow = (ROOT / ".gitea/workflows/runtime-distribution.yml").read_text(
encoding="utf-8"
)
validation = workflow.index("- name: Validate immutable release inputs")
bootstrap = workflow.index("- name: Bootstrap release sources")
self.assertLess(validation, bootstrap)
self.assertIn('image_pattern = re.compile(r"^[^@\\s]+@sha256:', workflow)
for input_name in (
"python_image",
"nginx_image",
"postgres_image",
"redis_image",
"load_balancer_image",
"managed_ingress_image",
"garage_image",
"test_mail_image",
):
self.assertIn(f"inputs.{input_name}", workflow)
def test_api_runtime_points_core_at_packaged_migration_scripts(self) -> None:
dockerfile = (ROOT / "tools/release/runtime/Dockerfile.api").read_text(
encoding="utf-8"
)
self.assertIn(
"GOVOPLAN_CORE_SOURCE_ROOT=/opt/govoplan/runtime/govoplan_core_runtime",
dockerfile,
)
def test_workflow_verifies_portable_bootstrap_artifacts_before_execution( def test_workflow_verifies_portable_bootstrap_artifacts_before_execution(
self, self,
) -> None: ) -> None:
+1
View File
@@ -12,6 +12,7 @@ ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \ PYTHONDONTWRITEBYTECODE=1 \
PYTHONPATH=/opt/govoplan/runtime \ PYTHONPATH=/opt/govoplan/runtime \
PATH=/opt/govoplan/runtime/bin:${PATH} \ PATH=/opt/govoplan/runtime/bin:${PATH} \
GOVOPLAN_CORE_SOURCE_ROOT=/opt/govoplan/runtime/govoplan_core_runtime \
HOME=/var/lib/govoplan HOME=/var/lib/govoplan
COPY wheelhouse/ /opt/govoplan/wheels/ COPY wheelhouse/ /opt/govoplan/wheels/