name: Runtime Distribution on: workflow_dispatch: inputs: version: description: Release version without leading v required: true type: string python_image: description: Digest-pinned multi-architecture Python 3.12 slim image required: true type: string nginx_image: description: Digest-pinned multi-architecture nginx-unprivileged image required: true type: string postgres_image: description: Digest-pinned PostgreSQL image required: true type: string redis_image: description: Digest-pinned Redis image required: true type: string load_balancer_image: description: Digest-pinned HAProxy image required: true type: string managed_ingress_image: description: Digest-pinned Caddy image required: true type: string garage_image: description: Digest-pinned Garage image required: true type: string test_mail_image: description: Digest-pinned GreenMail image required: true type: string jobs: publish-runtime: runs-on: ubuntu-latest steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: path: govoplan - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 with: python-version: "3.12" - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 with: node-version: "22" - name: Use HTTPS for GovOPlaN repositories 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 "ssh://git@git.add-ideas.de/GovOPlaN/govoplan" - name: Bootstrap release sources working-directory: govoplan run: python tools/repo/bootstrap-repositories.py --parent .. --transport public-https --exclude-repo addideas-govoplan-website - name: Build release wheel roots and WebUI working-directory: govoplan run: | python -m venv .runtime-build .runtime-build/bin/python -m pip install --upgrade pip wheel cryptography mkdir -p runtime-output/local-wheels .runtime-build/bin/python -m pip wheel --no-deps --wheel-dir runtime-output/local-wheels --requirement requirements-release.txt bash tools/release/install-webui-release-dependencies.sh ../govoplan-core/webui npm --prefix ../govoplan-core/webui run build .runtime-build/bin/python tools/release/prepare-runtime-context.py \ --wheelhouse runtime-output/local-wheels \ --web-dist ../govoplan-core/webui/dist \ --output runtime-output/common \ --required-module tenancy \ --required-module organizations \ --required-module identity \ --required-module idm \ --required-module access \ --required-module admin \ --required-module dashboard \ --required-module policy \ --required-module audit \ --required-module docs \ --required-module ops - name: Resolve architecture-specific offline wheelhouses working-directory: govoplan run: | mkdir -p runtime-output/wheels-amd64 runtime-output/wheels-arm64 cp runtime-output/local-wheels/*.whl runtime-output/wheels-amd64/ cp runtime-output/local-wheels/*.whl runtime-output/wheels-arm64/ .runtime-build/bin/python -m pip download --only-binary=:all: \ --platform manylinux_2_17_x86_64 --platform manylinux2014_x86_64 \ --implementation cp --python-version 3.12 --abi cp312 \ --find-links runtime-output/local-wheels \ --dest runtime-output/wheels-amd64 \ --requirement runtime-output/common/requirements-runtime.txt .runtime-build/bin/python -m pip download --only-binary=:all: \ --platform manylinux_2_17_aarch64 --platform manylinux2014_aarch64 \ --implementation cp --python-version 3.12 --abi cp312 \ --find-links runtime-output/local-wheels \ --dest runtime-output/wheels-arm64 \ --requirement runtime-output/common/requirements-runtime.txt .runtime-build/bin/python tools/release/prepare-runtime-context.py \ --wheelhouse runtime-output/wheels-amd64 \ --web-dist ../govoplan-core/webui/dist \ --output runtime-output/context-amd64 .runtime-build/bin/python tools/release/prepare-runtime-context.py \ --wheelhouse runtime-output/wheels-arm64 \ --web-dist ../govoplan-core/webui/dist \ --output runtime-output/context-arm64 cmp runtime-output/context-amd64/composition.json runtime-output/context-arm64/composition.json - name: Build one-file deployer working-directory: govoplan run: python tools/deployment/build-deployer-zipapp.py --output runtime-output/govoplan-deploy.pyz - name: Authenticate OCI publication working-directory: govoplan env: REGISTRY_USERNAME: ${{ secrets.GOVOPLAN_REGISTRY_USERNAME }} REGISTRY_TOKEN: ${{ secrets.GOVOPLAN_REGISTRY_TOKEN }} run: | test -n "$REGISTRY_USERNAME" test -n "$REGISTRY_TOKEN" printf '%s' "$REGISTRY_TOKEN" | docker login git.add-ideas.de --username "$REGISTRY_USERNAME" --password-stdin docker buildx create --name govoplan-runtime --use - name: Build and publish architecture images working-directory: govoplan env: VERSION: ${{ inputs.version }} PYTHON_IMAGE: ${{ inputs.python_image }} NGINX_IMAGE: ${{ inputs.nginx_image }} run: | COMPOSITION_SHA256="$(sha256sum runtime-output/context-amd64/composition.json | cut -d' ' -f1)" for ARCH in amd64 arm64; do docker buildx build --platform "linux/$ARCH" --push \ --file tools/release/runtime/Dockerfile.api \ --build-arg "PYTHON_IMAGE=$PYTHON_IMAGE" \ --build-arg "GOVOPLAN_RELEASE_VERSION=$VERSION" \ --build-arg "GOVOPLAN_COMPOSITION_SHA256=$COMPOSITION_SHA256" \ --tag "git.add-ideas.de/govoplan/runtime-api:$VERSION-$ARCH" \ "runtime-output/context-$ARCH" docker buildx build --platform "linux/$ARCH" --push \ --file tools/release/runtime/Dockerfile.web \ --build-arg "NGINX_IMAGE=$NGINX_IMAGE" \ --build-arg "GOVOPLAN_RELEASE_VERSION=$VERSION" \ --build-arg "GOVOPLAN_COMPOSITION_SHA256=$COMPOSITION_SHA256" \ --tag "git.add-ideas.de/govoplan/runtime-web:$VERSION-$ARCH" \ "runtime-output/context-$ARCH" done docker buildx imagetools create \ --tag "git.add-ideas.de/govoplan/runtime-api:$VERSION" \ "git.add-ideas.de/govoplan/runtime-api:$VERSION-amd64" \ "git.add-ideas.de/govoplan/runtime-api:$VERSION-arm64" docker buildx imagetools create \ --tag "git.add-ideas.de/govoplan/runtime-web:$VERSION" \ "git.add-ideas.de/govoplan/runtime-web:$VERSION-amd64" \ "git.add-ideas.de/govoplan/runtime-web:$VERSION-arm64" docker buildx imagetools inspect "git.add-ideas.de/govoplan/runtime-api:$VERSION" --raw > runtime-output/api-index.json docker buildx imagetools inspect "git.add-ideas.de/govoplan/runtime-web:$VERSION" --raw > runtime-output/web-index.json API_DIGEST="sha256:$(sha256sum runtime-output/api-index.json | cut -d' ' -f1)" WEB_DIGEST="sha256:$(sha256sum runtime-output/web-index.json | cut -d' ' -f1)" python tools/release/resolve-oci-platforms.py --repository git.add-ideas.de/govoplan/runtime-api --index-digest "$API_DIGEST" --index runtime-output/api-index.json --output runtime-output/api-metadata.json python tools/release/resolve-oci-platforms.py --repository git.add-ideas.de/govoplan/runtime-web --index-digest "$WEB_DIGEST" --index runtime-output/web-index.json --output runtime-output/web-metadata.json - name: Generate and sign distribution evidence working-directory: govoplan env: VERSION: ${{ inputs.version }} SOURCE_COMMIT: ${{ gitea.sha }} SIGNING_KEY: ${{ secrets.RUNTIME_DISTRIBUTION_SIGNING_KEY }} SIGNING_KEY_ID: ${{ secrets.RUNTIME_DISTRIBUTION_SIGNING_KEY_ID }} TRUSTED_KEYRING: ${{ secrets.RUNTIME_DISTRIBUTION_KEYRING }} 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: | test -n "$SIGNING_KEY" test -n "$SIGNING_KEY_ID" test -n "$TRUSTED_KEYRING" printf '%s\n' "$SIGNING_KEY" > runtime-output/signing-key.pem printf '%s\n' "$TRUSTED_KEYRING" > runtime-output/distribution-keyring.json chmod 600 runtime-output/signing-key.pem ARTIFACT_BASE="https://git.add-ideas.de/GovOPlaN/govoplan/releases/download/v$VERSION" python tools/release/finalize-runtime-distribution.py \ --composition runtime-output/context-amd64/composition.json \ --api-metadata runtime-output/api-metadata.json \ --web-metadata runtime-output/web-metadata.json \ --deployer runtime-output/govoplan-deploy.pyz \ --deployer-url "$ARTIFACT_BASE/govoplan-deploy.pyz" \ --artifact-base-url "$ARTIFACT_BASE" \ --source-commit "$SOURCE_COMMIT" \ --version "$VERSION" \ --sequence "$(date -u +%Y%m%d%H%M)" \ --dependency "postgres=$POSTGRES_IMAGE" \ --dependency "redis=$REDIS_IMAGE" \ --dependency "load_balancer=$LOAD_BALANCER_IMAGE" \ --dependency "managed_ingress=$MANAGED_INGRESS_IMAGE" \ --dependency "garage=$GARAGE_IMAGE" \ --dependency "test_mail=$TEST_MAIL_IMAGE" \ --output-directory runtime-output/evidence \ --descriptor runtime-output/distribution-descriptor.json python tools/release/generate-runtime-distribution.py \ --descriptor runtime-output/distribution-descriptor.json \ --signing-key "$SIGNING_KEY_ID=runtime-output/signing-key.pem" \ --output runtime-output/distribution-manifest.json openssl pkeyutl -sign -inkey runtime-output/signing-key.pem -rawin \ -in runtime-output/govoplan-deploy.pyz \ -out runtime-output/govoplan-deploy.pyz.sig sha256sum runtime-output/govoplan-deploy.pyz > runtime-output/govoplan-deploy.pyz.sha256 sha256sum runtime-output/distribution-manifest.json > runtime-output/distribution-manifest.json.sha256 rm runtime-output/signing-key.pem - name: Verify the published bundle contract with the zipapp working-directory: govoplan env: VERSION: ${{ inputs.version }} run: | MANIFEST_SHA256="$(cut -d' ' -f1 runtime-output/distribution-manifest.json.sha256)" python runtime-output/govoplan-deploy.pyz init \ --directory runtime-output/acceptance-install \ --non-interactive --module-set base python runtime-output/govoplan-deploy.pyz verify-release \ --directory runtime-output/acceptance-install \ --manifest runtime-output/distribution-manifest.json \ --manifest-sha256 "$MANIFEST_SHA256" \ --trusted-keyring runtime-output/distribution-keyring.json \ --adopt - name: Exercise the managed ingress boundary working-directory: govoplan env: MANAGED_INGRESS_IMAGE: ${{ inputs.managed_ingress_image }} LOAD_BALANCER_IMAGE: ${{ inputs.load_balancer_image }} run: >- python tools/checks/managed-ingress-drill.py --caddy-image "$MANAGED_INGRESS_IMAGE" --load-balancer-image "$LOAD_BALANCER_IMAGE" - name: Publish immutable Gitea release assets working-directory: govoplan env: VERSION: ${{ inputs.version }} GITEA_RELEASE_TOKEN: ${{ secrets.GOVOPLAN_RELEASE_TOKEN }} run: | python tools/release/publish-runtime-release.py \ --tag "v$VERSION" \ --title "GovOPlaN v$VERSION runtime distribution" \ --asset runtime-output/govoplan-deploy.pyz \ --asset runtime-output/govoplan-deploy.pyz.sig \ --asset runtime-output/govoplan-deploy.pyz.sha256 \ --asset runtime-output/distribution-manifest.json \ --asset runtime-output/distribution-manifest.json.sha256 \ --asset runtime-output/distribution-keyring.json \ --asset runtime-output/context-amd64/composition.json \ --asset runtime-output/evidence/api-sbom.cdx.json \ --asset runtime-output/evidence/web-sbom.cdx.json \ --asset runtime-output/evidence/api-provenance.json \ --asset runtime-output/evidence/web-provenance.json