Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 79 additions & 55 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ name: Publish Docker Image

on:
push:
tags:
- 'v*'
branches: [ "master" ]
tags: [ 'v*' ]
pull_request:
branches: [ "master" ]
workflow_dispatch:
inputs:
postgres_version:
Expand All @@ -19,65 +21,87 @@ env:
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
security-events: write # Required for Trivy SARIF (if used) or just in case

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_DOCKER }}/${{ env.IMAGE_NAME }}
${{ env.REGISTRY_GHCR }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=tag
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ inputs.postgres_version || '18.1' }}
type=sha
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_DOCKER }}/${{ env.IMAGE_NAME }}
${{ env.REGISTRY_GHCR }}/${{ env.IMAGE_NAME }}
tags: |
type=schedule
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ inputs.postgres_version || '18.1' }}
type=sha

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
platforms: linux/amd64,linux/arm64
build-args: |
POSTGRES_VERSION=${{ inputs.postgres_version || '18.1' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Extract first Docker tag
id: extract_tag
run: echo "tag=$(echo '${{ steps.meta.outputs.tags }}' | head -n 1)" >> $GITHUB_OUTPUT

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
continue-on-error: true
with:
image-ref: ${{ env.REGISTRY_GHCR }}/${{ env.IMAGE_NAME }}:${{ inputs.postgres_version || '18.1' }}
format: 'table'
exit-code: '0'
ignore-unfixed: true
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'
- name: Build and push Docker image
id: build
uses: docker/build-push-action@v5
with:
context: .
# For Pull Requests:
# - Build only for the runner's architecture (linux/amd64 usually) to skip QEMU emulation overhead.
# - 'load: true' is required for Trivy to scan the image locally.
# - 'load: true' is incompatible with multi-platform builds.
# For Push/Release:
# - Build for all platforms.
# - 'push: true' to publish to registries.
load: ${{ github.event_name == 'pull_request' }}
push: ${{ github.event_name != 'pull_request' }}
platforms: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
build-args: |
POSTGRES_VERSION=${{ inputs.postgres_version || '18.1' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Run Trivy vulnerability scanner
if: github.event_name == 'pull_request'
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ steps.extract_tag.outputs.tag }}
format: 'table'
exit-code: '1'
ignore-unfixed: true
vuln-type: 'os,library'
severity: 'CRITICAL,HIGH'
11 changes: 9 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
ARG POSTGRES_VERSION=18.1
FROM postgres:${POSTGRES_VERSION}

# ensure setpriv works as expected
RUN set -eux; \
\
echo 'testing setpriv:' ; \
setpriv --reuid=nobody --regid=nogroup --clear-groups id

RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends pgbackrest gettext-base ca-certificates; \
rm -rf /var/lib/apt/lists/*
rm -rf /var/lib/apt/lists/*; \
rm /usr/local/bin/gosu

RUN mkdir -p /etc/postgresql/conf.d \
&& chmod 750 /etc/postgresql \
Expand All @@ -19,4 +26,4 @@ COPY conf/pg_hba.conf /etc/postgresql/pg_hba.conf
COPY pgbackrest.conf.template /usr/local/share/pgbackrest/pgbackrest.conf.template

ENTRYPOINT ["pg-entrypoint.sh"]
CMD ["postgres"]
CMD ["postgres"]
72 changes: 72 additions & 0 deletions docker-ensure-initdb.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
set -Eeuo pipefail

#
# This script is intended for three main use cases:
#
# 1. (most importantly) as an example of how to use "docker-entrypoint.sh" to extend/reuse the initialization behavior
#
# 2. ("docker-ensure-initdb.sh") as a Kubernetes "init container" to ensure the provided database directory is initialized; see also "startup probes" for an alternative solution
# (no-op if database is already initialized)
#
# 3. ("docker-enforce-initdb.sh") as part of CI to ensure the database is fully initialized before use
# (error if database is already initialized)
#

source /usr/local/bin/docker-entrypoint.sh

# arguments to this script are assumed to be arguments to the "postgres" server (same as "docker-entrypoint.sh"), and most "docker-entrypoint.sh" functions assume "postgres" is the first argument (see "_main" over there)
if [ "$#" -eq 0 ] || [ "$1" != 'postgres' ]; then
set -- postgres "$@"
fi

# see also "_main" in "docker-entrypoint.sh"

docker_setup_env
# setup data directories and permissions (when run as root)
docker_create_db_directories
if [ "$(id -u)" = '0' ]; then
# then restart script as postgres user
exec setpriv --reuid=postgres --regid=postgres --clear-groups "$BASH_SOURCE" "$@"
fi

# only run initialization on an empty data directory
if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
docker_verify_minimum_env
docker_error_old_databases

# check dir permissions to reduce likelihood of half-initialized database
ls /docker-entrypoint-initdb.d/ > /dev/null

docker_init_database_dir
pg_setup_hba_conf "$@"

# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
docker_temp_server_start "$@"

docker_setup_db
docker_process_init_files /docker-entrypoint-initdb.d/*

docker_temp_server_stop
unset PGPASSWORD
else
self="$(basename "$0")"
case "$self" in
docker-ensure-initdb.sh)
echo >&2 "$self: note: database already initialized in '$PGDATA'!"
exit 0
;;

docker-enforce-initdb.sh)
echo >&2 "$self: error: (unexpected) database found in '$PGDATA'!"
exit 1
;;

*)
echo >&2 "$self: error: unknown file name: $self"
exit 99
;;
esac
fi
Loading