Skip to content

Docker Publish

Docker Publish #3

Workflow file for this run

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