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
46 changes: 0 additions & 46 deletions .github/workflows/build-cnpg-timescaledb-dev.yml

This file was deleted.

46 changes: 0 additions & 46 deletions .github/workflows/build-cnpg-timescaledb-stg.yml

This file was deleted.

72 changes: 72 additions & 0 deletions .github/workflows/build-cnpg-timescaledb.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# ABOUTME: Builds and pushes the CloudNativePG + TimescaleDB database image to the dev, stg or prd
# ABOUTME: ECR namespace, tagged pg<major>-tsdb<version> from the dispatch inputs.
name: Build and Push CNPG TimescaleDB

on:
workflow_dispatch:
inputs:
environment:
description: Target ECR namespace
required: true
default: dev
type: choice
options:
- dev
- stg
- prd
pg_major:
description: PostgreSQL major version
default: '17'
required: true
tsdb_version:
description: TimescaleDB version (semver, e.g. 2.28.2)
default: '2.28.2'
required: true

permissions:
id-token: write
contents: read

concurrency:
group: build-cnpg-timescaledb-${{ inputs.environment }}-pg${{ inputs.pg_major }}-tsdb${{ inputs.tsdb_version }}
cancel-in-progress: false

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Validate inputs
env:
PG_MAJOR: ${{ inputs.pg_major }}
TSDB_VERSION: ${{ inputs.tsdb_version }}
run: |
if [[ ! "$PG_MAJOR" =~ ^[0-9]+$ ]]; then
echo "::error::Invalid pg_major '$PG_MAJOR'. Expected an integer (e.g. 17)."
exit 1
fi
if [[ ! "$TSDB_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Invalid tsdb_version '$TSDB_VERSION'. Expected MAJOR.MINOR.PATCH (e.g. 2.28.2)."
exit 1
fi

- uses: actions/checkout@v5

- name: ECR Login via OIDC
id: ecr-login
uses: stellar/actions/sdf-ecr-login@main

- name: Build and push image
env:
ECR_REGISTRY: ${{ steps.ecr-login.outputs.ecr-registry }}
ECR_REPO: ${{ inputs.environment }}/cnpg-timescaledb
PG_MAJOR: ${{ inputs.pg_major }}
TSDB_VERSION: ${{ inputs.tsdb_version }}
run: |
TAG="${ECR_REGISTRY}/${ECR_REPO}:pg${PG_MAJOR}-tsdb${TSDB_VERSION}"
echo "Building CNPG TimescaleDB image: ${TAG}"
docker build --pull --platform linux/amd64 \
-f Dockerfile-timescale-cnpg \
--build-arg PG_MAJOR="${PG_MAJOR}" \
--build-arg TSDB_VERSION="${TSDB_VERSION}" \
-t "${TAG}" .
docker push "${TAG}"
28 changes: 0 additions & 28 deletions .github/workflows/build-dev.yml

This file was deleted.

30 changes: 0 additions & 30 deletions .github/workflows/build-stg.yml

This file was deleted.

54 changes: 54 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# ABOUTME: Builds and pushes a SHA-tagged wallet-backend image to the dev, stg or prd ECR namespace.
# ABOUTME: Pushes to main publish stg; dev, stg and prd are all reachable by manual dispatch.
name: Build and Push Wallet Backend

on:
push:
branches: [main]
workflow_dispatch:
inputs:
environment:
description: Target ECR namespace
required: true
default: dev
type: choice
options:
- dev
- stg
- prd

permissions:
id-token: write
contents: read

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- name: ECR Login via OIDC
id: ecr-login
uses: stellar/actions/sdf-ecr-login@main

- name: Resolve target namespace
id: target
env:
EVENT_NAME: ${{ github.event_name }}
ENVIRONMENT: ${{ inputs.environment }}
run: |
# A push to main publishes the staging image; a manual dispatch selects the namespace.
if [[ "$EVENT_NAME" == "workflow_dispatch" ]]; then
namespace="$ENVIRONMENT"
else
namespace="stg"
fi
echo "Target namespace: $namespace"
echo "namespace=$namespace" >> "$GITHUB_OUTPUT"

- name: Build and push image
env:
TAG: ${{ steps.ecr-login.outputs.ecr-registry }}/${{ steps.target.outputs.namespace }}/wallet-backend:${{ github.sha }}
run: |
make docker-build
make docker-push
53 changes: 48 additions & 5 deletions Dockerfile-timescale-cnpg
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
# docker build -f Dockerfile-timescale-cnpg --build-arg PG_MAJOR=17 --build-arg TSDB_VERSION=2.28.2 -t cnpg-timescaledb .

ARG PG_MAJOR=17
ARG TSDB_VERSION=2.28.2

# ---------------------------------------------------------------------------
# Stage 1: Install TimescaleDB via apt to capture extension artifacts
# ---------------------------------------------------------------------------
FROM ghcr.io/cloudnative-pg/postgresql:${PG_MAJOR}-bookworm AS timescaledb-builder

ARG PG_MAJOR
ARG TSDB_VERSION=2.28.2
ARG TSDB_VERSION

USER root

Expand All @@ -35,21 +36,44 @@ RUN echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg m
&& curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \
| gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg

# Install TimescaleDB pinned to the requested version.
# Install TimescaleDB pinned to the requested version. Two packages are needed
# and both are pinned:
# timescaledb-2-postgresql-NN versioned .so and .sql artifacts
# timescaledb-2-loader-postgresql-NN the unversioned loader .so plus
# timescaledb.control, whose
# default_version and module_pathname are
# stamped with the loader's own version
# The bundle depends on the loader with ">=", so an unpinned loader resolves to
# the newest published release and the control file then names a version whose
# install script and library this image does not carry.
# The glob (=${TSDB_VERSION}*) matches the distro suffix automatically
# (e.g., 2.19.3~debian12-1234).
# (e.g., 2.28.2~debian12-1710); the dpkg-query check rejects a longer upstream
# version that the same glob would also match (e.g., 2.28.21).
RUN apt-get update && apt-get install -y --no-install-recommends \
timescaledb-2-postgresql-${PG_MAJOR}=${TSDB_VERSION}* \
&& rm -rf /var/lib/apt/lists/*
timescaledb-2-loader-postgresql-${PG_MAJOR}=${TSDB_VERSION}* \
&& rm -rf /var/lib/apt/lists/* \
&& set -eu \
&& for pkg in timescaledb-2-postgresql-${PG_MAJOR} timescaledb-2-loader-postgresql-${PG_MAJOR}; do \
ver="$(dpkg-query -W -f='${Version}' "$pkg")"; \
case "$ver" in \
"${TSDB_VERSION}" | "${TSDB_VERSION}"[~-]*) ;; \
*) echo "FATAL: $pkg resolved to $ver, expected upstream version ${TSDB_VERSION}" >&2; exit 1 ;; \
esac; \
done

# ---------------------------------------------------------------------------
# Stage 2: Assemble final image — only extension artifacts, no build deps
# ---------------------------------------------------------------------------
FROM ghcr.io/cloudnative-pg/postgresql:${PG_MAJOR}-bookworm

ARG PG_MAJOR
ARG TSDB_VERSION

# Copy TimescaleDB shared libraries
# Copy TimescaleDB shared libraries. The glob keeps every version the bundle
# ships (timescaledb-<v>.so, -tsl-<v>.so, -invalidations-<v>.so): a database
# with an older extension version registered resolves pg_proc.probin to
# $libdir/timescaledb-<that version>, so its library must remain in the image.
COPY --from=timescaledb-builder \
/usr/lib/postgresql/${PG_MAJOR}/lib/timescaledb*.so \
/usr/lib/postgresql/${PG_MAJOR}/lib/
Expand All @@ -62,8 +86,27 @@ COPY --from=timescaledb-builder \
/usr/share/postgresql/${PG_MAJOR}/extension/timescaledb.control \
/usr/share/postgresql/${PG_MAJOR}/extension/

# timescaledb.control is what a bare CREATE EXTENSION and ALTER EXTENSION ...
# UPDATE resolve against, so default_version must name a version whose install
# script and versioned library are both present in this image.
RUN set -eu; \
ext="/usr/share/postgresql/${PG_MAJOR}/extension"; \
lib="/usr/lib/postgresql/${PG_MAJOR}/lib"; \
ctl="$ext/timescaledb.control"; \
want_mp='$libdir/timescaledb-'"${TSDB_VERSION}"; \
dv="$(grep -E '^[[:space:]]*default_version[[:space:]]*=' "$ctl" | head -n1 | cut -d '=' -f2- | tr -d "[:space:]\"'")"; \
mp="$(grep -E '^[[:space:]]*module_pathname[[:space:]]*=' "$ctl" | head -n1 | cut -d '=' -f2- | tr -d "[:space:]\"'")"; \
[ "$dv" = "${TSDB_VERSION}" ] || { echo "FATAL: default_version=$dv in $ctl, expected ${TSDB_VERSION}" >&2; exit 1; }; \
[ "$mp" = "$want_mp" ] || { echo "FATAL: module_pathname=$mp in $ctl, expected $want_mp" >&2; exit 1; }; \
[ -f "$ext/timescaledb--${TSDB_VERSION}.sql" ] || { echo "FATAL: missing $ext/timescaledb--${TSDB_VERSION}.sql" >&2; exit 1; }; \
[ -f "$lib/timescaledb-${TSDB_VERSION}.so" ] || { echo "FATAL: missing $lib/timescaledb-${TSDB_VERSION}.so" >&2; exit 1; }; \
[ -f "$lib/timescaledb-tsl-${TSDB_VERSION}.so" ] || { echo "FATAL: missing $lib/timescaledb-tsl-${TSDB_VERSION}.so" >&2; exit 1; }; \
[ -f "$lib/timescaledb.so" ] || { echo "FATAL: missing loader $lib/timescaledb.so" >&2; exit 1; }; \
echo "timescaledb ${TSDB_VERSION}: control file and artifacts verified"

# Switch to CNPG's unprivileged user (UID 26)
USER 26

LABEL org.opencontainers.image.title="CloudNativePG + TimescaleDB (Bookworm)"
LABEL org.opencontainers.image.description="PostgreSQL ${PG_MAJOR} with TimescaleDB for CloudNativePG on Kubernetes"
LABEL org.opencontainers.image.version="pg${PG_MAJOR}-tsdb${TSDB_VERSION}"
Loading
Loading