Package runtime migration scripts correctly
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|||||||
Reference in New Issue
Block a user