name: Deploy blog immutable package on: workflow_dispatch: inputs: revision: description: Full source commit required: true artifact_url: description: Immutable Gitea generic-package URL required: true artifact_digest: description: sha256 digest of site.tar.gz required: true target: description: Provisioned site-scoped release root required: true verify_url: description: Public release marker URL required: true desired_commit: description: gitops-sandbox commit that selected this release required: true desired_generation: description: Ancestor count of desired_commit; the receiver refuses older selections required: true permissions: contents: read # Best-effort only. Ordering is enforced by the receiver, which applies a # selection only if its generation is not older than the one already live, so # a stale dispatch or a re-run of an old run cannot replace a newer release. concurrency: group: blog-deploy cancel-in-progress: false jobs: contract: runs-on: ubuntu-latest container: image: git.librete.ch/public/runner-image:v0.2.0@sha256:f60c587d3c0b0aac04a572db5349e27672bf76baec2ce547a3dcc28cebcf1b7e timeout-minutes: 5 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 - name: Validate dispatch contract without network access env: REVISION: ${{ inputs.revision }} ARTIFACT_URL: ${{ inputs.artifact_url }} ARTIFACT_DIGEST: ${{ inputs.artifact_digest }} TARGET: ${{ inputs.target }} VERIFY_URL: ${{ inputs.verify_url }} DESIRED_COMMIT: ${{ inputs.desired_commit }} DESIRED_GENERATION: ${{ inputs.desired_generation }} run: | set -eu python3 delivery/scripts/deploy_contract.py validate \ --revision "$REVISION" \ --artifact-url "$ARTIFACT_URL" \ --artifact-digest "$ARTIFACT_DIGEST" \ --target "$TARGET" \ --verify-url "$VERIFY_URL" \ --site-config delivery/site.json \ --desired-commit "$DESIRED_COMMIT" \ --desired-generation "$DESIRED_GENERATION" deploy: needs: contract if: ${{ vars.BLOG_STATIC_DEPLOY_ENABLED == 'true' }} runs-on: ubuntu-latest container: image: git.librete.ch/public/runner-image:v0.2.0@sha256:f60c587d3c0b0aac04a572db5349e27672bf76baec2ce547a3dcc28cebcf1b7e timeout-minutes: 30 env: ACTIVATION: ${{ vars.BLOG_STATIC_DEPLOY_ENABLED }} REVISION: ${{ inputs.revision }} ARTIFACT_URL: ${{ inputs.artifact_url }} ARTIFACT_DIGEST: ${{ inputs.artifact_digest }} TARGET: ${{ inputs.target }} VERIFY_URL: ${{ inputs.verify_url }} DESIRED_COMMIT: ${{ inputs.desired_commit }} DESIRED_GENERATION: ${{ inputs.desired_generation }} STATIC_PACKAGE_READ_USER: ${{ secrets.BLOG_PACKAGE_READ_USER }} STATIC_PACKAGE_READ_TOKEN: ${{ secrets.BLOG_PACKAGE_READ_TOKEN }} STATIC_DEPLOY_HOST: ${{ secrets.BLOG_STATIC_DEPLOY_HOST }} STATIC_DEPLOY_USER: ${{ secrets.BLOG_STATIC_DEPLOY_USER }} STATIC_DEPLOY_SSH_KEY: ${{ secrets.BLOG_STATIC_DEPLOY_SSH_KEY }} STATIC_DEPLOY_KNOWN_HOSTS: ${{ secrets.BLOG_STATIC_DEPLOY_KNOWN_HOSTS }} steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 - name: Enforce reviewed deployment gate run: | set -eu test "$ACTIVATION" = true python3 delivery/scripts/delivery_gate.py static_deploy \ --config delivery/.delivery/config.json test -n "$STATIC_DEPLOY_HOST" test -n "$STATIC_DEPLOY_USER" test -n "$STATIC_DEPLOY_SSH_KEY" test -n "$STATIC_DEPLOY_KNOWN_HOSTS" case "$STATIC_DEPLOY_HOST" in *[!A-Za-z0-9.-]*|'') exit 1;; esac case "$STATIC_DEPLOY_USER" in *[!A-Za-z0-9_-]*|'') exit 1;; esac - name: Download and inspect the declared immutable artifact run: | set -eu python3 delivery/scripts/deploy_contract.py prepare \ --revision "$REVISION" \ --artifact-url "$ARTIFACT_URL" \ --artifact-digest "$ARTIFACT_DIGEST" \ --target "$TARGET" \ --verify-url "$VERIFY_URL" \ --site-config delivery/site.json \ --desired-commit "$DESIRED_COMMIT" \ --desired-generation "$DESIRED_GENERATION" \ --archive delivery/.build/site.tar.gz \ --checksum delivery/.build/site.tar.gz.sha256 \ --request-token delivery/.build/deploy-request.token - name: Send the artifact to the site-scoped forced command id: receive run: | set -eu umask 077 mkdir -p "$HOME/.ssh" printf '%s\n' "$STATIC_DEPLOY_SSH_KEY" > "$HOME/.ssh/id_static_deploy" printf '%s\n' "$STATIC_DEPLOY_KNOWN_HOSTS" > "$HOME/.ssh/known_hosts" cat > "$HOME/.ssh/config" <> "$GITHUB_OUTPUT" exit 0 fi test "$status" -eq 0 printf 'superseded=false\n' >> "$GITHUB_OUTPUT" - name: Confirm public revision and digest if: ${{ steps.receive.outputs.superseded == 'false' }} run: | set -eu python3 delivery/scripts/static_release.py verify \ --url "$VERIFY_URL" \ --revision "$REVISION" \ --digest "$ARTIFACT_DIGEST"