Docker Publish #3
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Docker Publish | |
| # Publishes the public, multi-arch pg_durable image to GHCR. | |
| # | |
| # This image is built with the `http-allow-all` Cargo feature (all SSRF / | |
| # outbound-HTTP protections disabled) and other test-friendly settings. It is | |
| # intended for evaluating and learning pg_durable only — NOT for production. | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: > | |
| Release tag to build and publish, e.g. v0.2.2. The workflow logic runs | |
| from the branch selected in "Use workflow from", but the source is | |
| checked out at this ref. Use this to publish a tag created before this | |
| workflow existed. In a dry run, a branch name is also accepted. | |
| required: true | |
| type: string | |
| dry_run: | |
| description: > | |
| Build and smoke-test only — do not push images to GHCR. Use this to | |
| rehearse the publish path (including tag computation) from a feature | |
| branch before merging. | |
| required: false | |
| default: false | |
| type: boolean | |
| permissions: | |
| contents: read | |
| packages: write | |
| env: | |
| IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/pg_durable | |
| PG_MAJOR: '17' | |
| # PG major that also receives the unqualified `latest` tag. Lets future PG | |
| # versions (e.g. pg18) be published without fighting over `latest`. | |
| DEFAULT_PG_MAJOR: '17' | |
| concurrency: | |
| group: docker-publish-${{ github.event.inputs.ref || github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| publish: | |
| name: Build, verify, and publish multi-arch image | |
| runs-on: ubuntu-latest | |
| # Don't run (and push images) on forks. | |
| if: github.repository == 'microsoft/pg_durable' | |
| steps: | |
| # Tooling checkout (the workflow's own ref, e.g. main): provides the local | |
| # composite action and the current Dockerfile (with HTTP_FEATURE support). | |
| # Local actions are resolved from the working tree, so this must be a ref | |
| # that actually contains them. | |
| - name: Checkout workflow tooling | |
| uses: actions/checkout@v4 | |
| # Source checkout (the version to build): may be an older tag that predates | |
| # this workflow. We build its source with the current Dockerfile above, so | |
| # the image still gets the http-allow-all build and current build steps. | |
| - name: Checkout source to build | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.inputs.ref || github.ref }} | |
| fetch-depth: 0 | |
| path: source | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| # Verify the image before publishing anything. Builds amd64 locally with the | |
| # exact feature set we publish, then boots it and checks the extension loads. | |
| - name: Build and smoke-test image (amd64) | |
| uses: ./.github/actions/docker-build-smoke | |
| with: | |
| image-ref: ${{ env.IMAGE_NAME }}:smoke | |
| http-feature: http-allow-all | |
| context: source | |
| dockerfile: ./Dockerfile | |
| - name: Compute image tags | |
| id: meta | |
| shell: bash | |
| working-directory: source | |
| env: | |
| INPUT_REF: ${{ github.event.inputs.ref }} | |
| DRY_RUN: ${{ github.event.inputs.dry_run }} | |
| run: | | |
| set -euo pipefail | |
| if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then | |
| raw_ref="${INPUT_REF}" | |
| else | |
| raw_ref="${GITHUB_REF#refs/tags/}" | |
| fi | |
| if [[ "${raw_ref}" != v* ]]; then | |
| if [[ "${DRY_RUN}" == "true" ]]; then | |
| # Allow non-tag refs (e.g. a branch) in a dry run by synthesizing a | |
| # placeholder version so the publish logic can still be exercised. | |
| echo "::notice::Dry run with non-version ref '${raw_ref}'; using placeholder version 0.0.0-dryrun." | |
| raw_ref="v0.0.0-dryrun" | |
| else | |
| echo "::error::Expected a version tag starting with 'v' (e.g. v0.2.2), got '${raw_ref}'." | |
| exit 1 | |
| fi | |
| fi | |
| version="${raw_ref#v}" | |
| revision="$(git rev-parse HEAD)" | |
| short_sha="$(git rev-parse --short=7 HEAD)" | |
| # Immutable, fully-qualified tags (always published). | |
| tags=( | |
| "${IMAGE_NAME}:${version}-pg${PG_MAJOR}" | |
| "${IMAGE_NAME}:v${version}-pg${PG_MAJOR}" | |
| "${IMAGE_NAME}:sha-${short_sha}-pg${PG_MAJOR}" | |
| ) | |
| # A version containing a hyphen is a semver pre-release (e.g. 0.2.2-rc1). | |
| if [[ "${version}" == *-* ]]; then | |
| echo "Pre-release ${version}: publishing immutable tags only (no floating pg${PG_MAJOR}/latest)." | |
| else | |
| # Floating tags only move forward: only the highest stable version | |
| # across all v* git tags may claim pg${PG_MAJOR} / latest. | |
| highest="$(git tag -l 'v*' | sed 's/^v//' | grep -v -- '-' | sort -V | tail -n1)" | |
| if [[ "${version}" == "${highest}" ]]; then | |
| tags+=("${IMAGE_NAME}:pg${PG_MAJOR}") | |
| if [[ "${PG_MAJOR}" == "${DEFAULT_PG_MAJOR}" ]]; then | |
| tags+=("${IMAGE_NAME}:latest") | |
| fi | |
| else | |
| echo "Version ${version} is not the highest stable release (${highest}); not moving floating tags." | |
| fi | |
| fi | |
| printf 'Publishing tags:\n%s\n' "${tags[*]}" | |
| { | |
| echo "tags<<EOF" | |
| printf '%s\n' "${tags[@]}" | |
| echo "EOF" | |
| echo "version=${version}" | |
| echo "revision=${revision}" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Build and push multi-arch image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: source | |
| file: ./Dockerfile | |
| platforms: linux/amd64,linux/arm64 | |
| # Dry runs build both arches but do not push to GHCR. | |
| push: ${{ github.event.inputs.dry_run != 'true' }} | |
| build-args: | | |
| HTTP_FEATURE=http-allow-all | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: | | |
| org.opencontainers.image.title=pg_durable | |
| org.opencontainers.image.description=Durable SQL function execution for PostgreSQL — test/learning image, not for production | |
| org.opencontainers.image.source=https://github.com/${{ github.repository }} | |
| org.opencontainers.image.url=https://github.com/${{ github.repository }} | |
| org.opencontainers.image.licenses=PostgreSQL | |
| org.opencontainers.image.version=${{ steps.meta.outputs.version }} | |
| org.opencontainers.image.revision=${{ steps.meta.outputs.revision }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max |