From 93c28bcb581b385521ca90cd2c7484fd345208a3 Mon Sep 17 00:00:00 2001 From: Ritvik Jayaswal Date: Wed, 24 Jun 2026 12:35:07 -0400 Subject: [PATCH 1/8] feat(ci): auto-build DocumentDB images on new upstream release Closes #360. Add watch_documentdb_images.yml which polls the upstream documentdb/documentdb repo on a schedule and, when a newer release than the current default is published, builds candidate documentdb + gateway images and opens a version-bump PR (human-merged gate). Make build_documentdb_images.yml and release_documentdb_images.yml reusable via workflow_call (build exposes documentdb_version and image_tag outputs) so the watcher can chain them. Document the automation in image-management.md and AGENTS.md. Signed-off-by: Ritvik Jayaswal --- .github/workflows/build_documentdb_images.yml | 19 +- .../workflows/release_documentdb_images.yml | 16 ++ .github/workflows/watch_documentdb_images.yml | 166 ++++++++++++++++++ AGENTS.md | 1 + docs/designs/image-management.md | 35 ++++ 5 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/watch_documentdb_images.yml diff --git a/.github/workflows/build_documentdb_images.yml b/.github/workflows/build_documentdb_images.yml index bdd009c9f..5e67b0a82 100644 --- a/.github/workflows/build_documentdb_images.yml +++ b/.github/workflows/build_documentdb_images.yml @@ -14,6 +14,21 @@ on: required: false default: '0.110.0' + workflow_call: + inputs: + version: + description: 'Released DocumentDB version to package (for example 0.110.0)' + required: false + type: string + default: '0.110.0' + outputs: + documentdb_version: + description: 'Resolved DocumentDB version (dotted semver, e.g. 0.110.0)' + value: ${{ jobs.resolve-public-artifacts.outputs.documentdb_version }} + image_tag: + description: 'Candidate image tag produced by this build' + value: ${{ jobs.resolve-public-artifacts.outputs.image_tag }} + repository_dispatch: types: [documentdb-release] @@ -44,7 +59,7 @@ jobs: id: version run: | set -euo pipefail - RAW_VERSION="${{ github.event.inputs.version || github.event.client_payload.version || env.DEFAULT_DOCUMENTDB_VERSION }}" + RAW_VERSION="${{ inputs.version || github.event.client_payload.version || env.DEFAULT_DOCUMENTDB_VERSION }}" if [[ "$RAW_VERSION" =~ ^[0-9]+\.[0-9]+-[0-9]+$ ]]; then VERSION="${RAW_VERSION/-/.}" else @@ -189,7 +204,7 @@ jobs: DIGEST=$(docker buildx imagetools inspect ghcr.io/${{ github.repository }}/${{ matrix.image }}:${{ env.IMAGE_TAG }} \ | awk '/^Digest:/ { print $2 }') cosign verify \ - --certificate-identity "https://github.com/${{ github.repository }}/.github/workflows/build_documentdb_images.yml@${{ github.ref }}" \ + --certificate-identity-regexp "^https://github\.com/${{ github.repository }}/\.github/workflows/build_documentdb_images\.yml@" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ ghcr.io/${{ github.repository }}/${{ matrix.image }}@${DIGEST} diff --git a/.github/workflows/release_documentdb_images.yml b/.github/workflows/release_documentdb_images.yml index b6e81c086..91c717dcb 100644 --- a/.github/workflows/release_documentdb_images.yml +++ b/.github/workflows/release_documentdb_images.yml @@ -21,6 +21,22 @@ on: default: true type: boolean + workflow_call: + inputs: + candidate_version: + description: 'Database candidate tag to promote (e.g., 0.111.0-build-123456789-1-deadbee)' + required: true + type: string + version: + description: 'Database image release version (e.g., 0.111.0)' + required: true + type: string + update_defaults: + description: 'Create PR to update default image versions in code' + required: false + default: true + type: boolean + permissions: contents: write packages: write diff --git a/.github/workflows/watch_documentdb_images.yml b/.github/workflows/watch_documentdb_images.yml new file mode 100644 index 000000000..e4c4d29b2 --- /dev/null +++ b/.github/workflows/watch_documentdb_images.yml @@ -0,0 +1,166 @@ +name: WATCH - DocumentDB Releases + +# Polls the upstream documentdb/documentdb repository for new releases and, when a +# newer version than the operator's current default is published, automatically: +# 1. Builds candidate documentdb + gateway images (build_documentdb_images.yml) +# 2. Promotes them to release tags and opens a "chore: bump DocumentDB images" PR +# (release_documentdb_images.yml) +# +# The version-bump PR is the human gate: a maintainer reviews and merges it, which +# is what actually makes the new version the default for new installs. +# +# Only handles the DATABASE version track (documentDbVersion). Operator/sidecar +# images follow a separate track (build_operator_images.yml / release_operator.yml). + +on: + schedule: + # Every 6 hours. GitHub's releases/latest excludes drafts and pre-releases, + # so pre-releases never trigger this automation. + - cron: '0 */6 * * *' + + workflow_dispatch: + inputs: + version: + description: 'Override upstream version to release (e.g. 0.111.0). Leave empty to auto-detect latest.' + required: false + default: '' + dry_run: + description: 'Only detect and report; do not build or open a PR.' + required: false + default: false + type: boolean + +permissions: + contents: write + packages: write + pull-requests: write + id-token: write + +env: + UPSTREAM_REPO: documentdb/documentdb + +jobs: + # --------------------------------------------------------------------------- + # Detect whether a newer upstream release exists + # --------------------------------------------------------------------------- + detect: + name: Detect new DocumentDB release + runs-on: ubuntu-22.04 + outputs: + new_version: ${{ steps.check.outputs.new_version }} + should_release: ${{ steps.check.outputs.should_release }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Resolve latest upstream release + id: upstream + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + OVERRIDE="${{ github.event.inputs.version || '' }}" + if [[ -n "$OVERRIDE" ]]; then + RAW="$OVERRIDE" + echo "Using manual version override: $RAW" + else + RAW=$(gh api "repos/${UPSTREAM_REPO}/releases/latest" --jq '.tag_name') + echo "Latest upstream release tag: $RAW" + fi + # Normalize: strip leading 'v', convert dashed (0.110-0) to dotted (0.110.0). + RAW="${RAW#v}" + if [[ "$RAW" =~ ^[0-9]+\.[0-9]+-[0-9]+$ ]]; then + VERSION="${RAW/-/.}" + else + VERSION="$RAW" + fi + if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Could not parse a dotted semver from upstream tag '$RAW'" >&2 + exit 1 + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Upstream DocumentDB version: $VERSION" + + - name: Read current default version + id: current + run: | + set -euo pipefail + CURRENT=$(sed -nE 's|^[[:space:]]*DEFAULT_DOCUMENTDB_IMAGE[[:space:]]*=.*:([0-9]+\.[0-9]+\.[0-9]+)".*|\1|p' \ + operator/src/internal/utils/constants.go | head -1) + if [[ -z "$CURRENT" ]]; then + echo "Failed to read DEFAULT_DOCUMENTDB_IMAGE from operator/src/internal/utils/constants.go" >&2 + exit 1 + fi + echo "version=$CURRENT" >> "$GITHUB_OUTPUT" + echo "Current default DocumentDB version: $CURRENT" + + - name: Decide whether to release + id: check + run: | + set -euo pipefail + NEW="${{ steps.upstream.outputs.version }}" + CUR="${{ steps.current.outputs.version }}" + echo "new_version=$NEW" >> "$GITHUB_OUTPUT" + + # Not newer than the current default? Nothing to do. + if [[ "$NEW" == "$CUR" ]] || \ + [[ "$(printf '%s\n%s\n' "$CUR" "$NEW" | sort -V | tail -1)" != "$NEW" ]]; then + echo "Upstream $NEW is not newer than current default $CUR. Nothing to do." + echo "should_release=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # Already promoted? If the release tag exists, the bump PR is likely + # pending review/merge, so don't rebuild on every cron tick. + echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + if docker manifest inspect "ghcr.io/${{ github.repository }}/documentdb:${NEW}" >/dev/null 2>&1; then + echo "Release image documentdb:${NEW} already exists; version-bump PR is likely pending merge. Skipping." + echo "should_release=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + if [[ "${{ github.event.inputs.dry_run }}" == "true" ]]; then + echo "Dry run: a newer version $NEW (current $CUR) was detected but no build/PR will be created." + echo "should_release=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + echo "New upstream version $NEW detected (current default $CUR). Proceeding to build + release." + echo "should_release=true" >> "$GITHUB_OUTPUT" + + - name: Detection summary + if: always() + run: | + echo "## DocumentDB Release Watch" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "- **Upstream latest**: \`${{ steps.upstream.outputs.version }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Current default**: \`${{ steps.current.outputs.version }}\`" >> "$GITHUB_STEP_SUMMARY" + echo "- **Action**: ${{ steps.check.outputs.should_release == 'true' && 'Building candidate images and opening version-bump PR' || 'No release needed' }}" >> "$GITHUB_STEP_SUMMARY" + + # --------------------------------------------------------------------------- + # Build candidate images for the new version + # --------------------------------------------------------------------------- + build: + name: Build candidate images + needs: detect + if: needs.detect.outputs.should_release == 'true' + uses: ./.github/workflows/build_documentdb_images.yml + with: + version: ${{ needs.detect.outputs.new_version }} + secrets: inherit + + # --------------------------------------------------------------------------- + # Promote candidate images and open the version-bump PR + # --------------------------------------------------------------------------- + release: + name: Promote images and open version-bump PR + needs: [detect, build] + if: needs.detect.outputs.should_release == 'true' + uses: ./.github/workflows/release_documentdb_images.yml + with: + candidate_version: ${{ needs.build.outputs.image_tag }} + version: ${{ needs.detect.outputs.new_version }} + update_defaults: true + secrets: inherit diff --git a/AGENTS.md b/AGENTS.md index b34f9f5a3..456c9575e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -286,6 +286,7 @@ Types: - `build_documentdb_images.yml` - Build documentdb/gateway candidate images (database version track) - `release_operator.yml` - Promote operator/sidecar images and publish Helm chart - `release_documentdb_images.yml` - Promote documentdb/gateway images and auto-PR version bumps +- `watch_documentdb_images.yml` - Poll upstream documentdb/documentdb releases (cron) and auto-build + open version-bump PR on a new release - `build_images.yml` - [DEPRECATED] Combined image builds (replaced by split workflows above) - `release_images.yml` - [DEPRECATED] Combined release (replaced by split workflows above) - `deploy_docs.yml` - Documentation deployment diff --git a/docs/designs/image-management.md b/docs/designs/image-management.md index c07cc050d..81d7baf28 100644 --- a/docs/designs/image-management.md +++ b/docs/designs/image-management.md @@ -302,6 +302,41 @@ Flow: └── Opens PR: "chore: bump DocumentDB images to 0.111.0" ``` +### Automatic Release Detection (`watch_documentdb_images.yml`) + +Watches the upstream [`documentdb/documentdb`](https://github.com/documentdb/documentdb) +repository for new releases and drives the database track end-to-end without manual +intervention. This is the automation behind keeping new installs on the latest +DocumentDB version. + +| Aspect | Details | +|--------|---------| +| **Trigger** | `schedule` (cron `0 */6 * * *`), `workflow_dispatch` (manual, with optional `version` override and `dry_run`) | +| **Detection** | Reads upstream `releases/latest` (drafts and pre-releases are excluded by GitHub) and compares against the current `DEFAULT_DOCUMENTDB_IMAGE` in `constants.go` | +| **Chaining** | Calls `build_documentdb_images.yml` then `release_documentdb_images.yml` as reusable workflows (`workflow_call`) | +| **Human gate** | The auto-generated `chore: bump DocumentDB images` PR — a maintainer reviews and merges it to make the new version the default | + +``` +Flow: + 1. detect + ├── Resolve upstream latest release tag (e.g. v0.111-0 → 0.111.0) + ├── Read current default from constants.go + ├── Skip if not newer, if release tag already exists (PR pending), or dry_run + └── Output: should_release, new_version + + 2. build (uses build_documentdb_images.yml) ── if should_release + └── Output: image_tag (candidate) + + 3. release (uses release_documentdb_images.yml) ── if should_release + ├── candidate_version: + ├── version: + └── update_defaults: true → opens the version-bump PR +``` + +Idempotency: once the release images are promoted, the `documentdb:` +tag exists, so subsequent cron ticks short-circuit until the bump PR is merged +(which advances the default and stops further detection for that version). + --- ## Test Pipelines From 48bad409726a8d49bf14d5f7449487b3c7a3d1bf Mon Sep 17 00:00:00 2001 From: Ritvik Jayaswal Date: Fri, 26 Jun 2026 14:09:21 -0400 Subject: [PATCH 2/8] refactor(ci): build DocumentDB extension from official APT repo + webhook trigger Address review feedback on PR #410: - Install postgresql-18-documentdb from the official DocumentDB APT repo (documentdb.io/deb) instead of GitHub-release .deb assets; the meta-package pulls in Citus/RUM/pgvector/PostGIS so explicit cron/pgvector/postgis installs are dropped. APT package version is pinnable per build. - Make repository_dispatch (documentdb-release) the primary trigger for watch_documentdb_images.yml; demote cron to a daily safety-net. Add reference upstream sender workflow as docs/designs/upstream-release-dispatch-sender.md. - Idempotency: skip only when BOTH documentdb and gateway release tags exist; rebuild on partial promotion. - Gateway build unchanged (still from documentdb-local public image). Co-authored-by: Copilot Signed-off-by: Ritvik Jayaswal --- .github/dockerfiles/Dockerfile_extension | 39 +++++-- .github/workflows/build_documentdb_images.yml | 60 +++++----- .github/workflows/watch_documentdb_images.yml | 55 +++++++-- AGENTS.md | 2 +- docs/designs/image-management.md | 39 ++++--- .../upstream-release-dispatch-sender.md | 105 ++++++++++++++++++ 6 files changed, 234 insertions(+), 66 deletions(-) create mode 100644 docs/designs/upstream-release-dispatch-sender.md diff --git a/.github/dockerfiles/Dockerfile_extension b/.github/dockerfiles/Dockerfile_extension index c0317e502..9bccb802c 100644 --- a/.github/dockerfiles/Dockerfile_extension +++ b/.github/dockerfiles/Dockerfile_extension @@ -6,10 +6,16 @@ # Follows the pattern from: # https://github.com/cloudnative-pg/postgres-extensions-containers # +# The DocumentDB extension is installed from the official DocumentDB APT +# repository (https://documentdb.io/deb). The `postgresql-${PG_MAJOR}-documentdb` +# meta-package pulls in its own runtime dependencies (Citus, RUM, libbson, +# PCRE2, pgvector, PostGIS, ...), so no extra extension packages are installed +# explicitly here. +# # Usage: # docker build \ # --build-arg PG_MAJOR=18 \ -# --build-arg DEB_PACKAGE_REL_PATH=packages/documentdb_0.110-0_arm64.deb \ +# --build-arg DOCUMENTDB_APT_VERSION=0.110.0 \ # -t documentdb-extension:latest \ # -f Dockerfile_extension . @@ -17,24 +23,33 @@ ARG BASE=ghcr.io/cloudnative-pg/postgresql:18-minimal-trixie FROM ${BASE} AS builder ARG PG_MAJOR=18 -ARG DEB_PACKAGE_REL_PATH +# Full Debian package version of postgresql-${PG_MAJOR}-documentdb to install +# from the official DocumentDB APT repository. Pin this per build for +# reproducibility. If empty, the latest version in the 'stable' channel is used. +ARG DOCUMENTDB_APT_VERSION USER 0 RUN set -eux && \ # Snapshot base image system libraries for later diffing ldconfig -p | awk '{print $NF}' | grep '^/' | sort | uniq > /tmp/base-image-libs.out && \ - # Install pgdg extension packages + # Tools needed to add the official DocumentDB APT repository apt-get update && \ - apt-get install -y --no-install-recommends \ - postgresql-${PG_MAJOR}-cron \ - postgresql-${PG_MAJOR}-pgvector \ - postgresql-${PG_MAJOR}-postgis-3 - -# Install the DocumentDB extension from a pre-built .deb -COPY ${DEB_PACKAGE_REL_PATH} /tmp/documentdb.deb -RUN dpkg -i /tmp/documentdb.deb && \ - rm -f /tmp/documentdb.deb + apt-get install -y --no-install-recommends ca-certificates curl gnupg && \ + # Install the official DocumentDB signing key and APT source + curl -fsSL https://documentdb.io/documentdb-archive-keyring.gpg \ + -o /usr/share/keyrings/documentdb-archive-keyring.gpg && \ + echo "deb [signed-by=/usr/share/keyrings/documentdb-archive-keyring.gpg] https://documentdb.io/deb stable main" \ + > /etc/apt/sources.list.d/documentdb.list && \ + apt-get update && \ + # Install the DocumentDB extension meta-package from the official repo. + if [ -n "${DOCUMENTDB_APT_VERSION}" ]; then \ + apt-get install -y --no-install-recommends \ + "postgresql-${PG_MAJOR}-documentdb=${DOCUMENTDB_APT_VERSION}"; \ + else \ + apt-get install -y --no-install-recommends \ + "postgresql-${PG_MAJOR}-documentdb"; \ + fi # Gather system library dependencies not present in the CNPG base image RUN set -eux && \ diff --git a/.github/workflows/build_documentdb_images.yml b/.github/workflows/build_documentdb_images.yml index 5e67b0a82..2c94303a9 100644 --- a/.github/workflows/build_documentdb_images.yml +++ b/.github/workflows/build_documentdb_images.yml @@ -1,7 +1,8 @@ name: RELEASE - Build DocumentDB Candidate Images # Builds documentdb extension and gateway images from public DocumentDB release artifacts. -# - documentdb image: public deb13 PostgreSQL 18 extension package +# - documentdb image: official DocumentDB extension package from the APT repo +# (https://documentdb.io/deb), installed as postgresql-18-documentdb # - gateway image: public documentdb-local image payload # These images follow the DATABASE version track (documentDbVersion in values.yaml). # For operator/sidecar images, see build_operator_images.yml. @@ -13,6 +14,10 @@ on: description: 'Released DocumentDB version to package (for example 0.110.0)' required: false default: '0.110.0' + documentdb_apt_version: + description: 'Debian package version of postgresql-18-documentdb to pin (e.g. 0.110.0). Leave empty to derive from version.' + required: false + default: '' workflow_call: inputs: @@ -21,6 +26,11 @@ on: required: false type: string default: '0.110.0' + documentdb_apt_version: + description: 'Debian package version of postgresql-18-documentdb to pin. Leave empty to derive from version.' + required: false + type: string + default: '' outputs: documentdb_version: description: 'Resolved DocumentDB version (dotted semver, e.g. 0.110.0)' @@ -29,9 +39,6 @@ on: description: 'Candidate image tag produced by this build' value: ${{ jobs.resolve-public-artifacts.outputs.image_tag }} - repository_dispatch: - types: [documentdb-release] - permissions: packages: write contents: read @@ -39,7 +46,8 @@ permissions: env: DEFAULT_DOCUMENTDB_VERSION: '0.110.0' - DOCUMENTDB_RELEASE_REPO: documentdb/documentdb + DOCUMENTDB_APT_REPO_URL: https://documentdb.io/deb + DOCUMENTDB_APT_KEYRING_URL: https://documentdb.io/documentdb-archive-keyring.gpg GATEWAY_SOURCE_IMAGE_REPO: ghcr.io/documentdb/documentdb/documentdb-local jobs: @@ -52,6 +60,7 @@ jobs: outputs: documentdb_version: ${{ steps.version.outputs.documentdb_version }} documentdb_version_dash: ${{ steps.version.outputs.documentdb_version_dash }} + documentdb_apt_version: ${{ steps.version.outputs.documentdb_apt_version }} image_tag: ${{ steps.version.outputs.image_tag }} gateway_source_image: ${{ steps.version.outputs.gateway_source_image }} steps: @@ -59,7 +68,7 @@ jobs: id: version run: | set -euo pipefail - RAW_VERSION="${{ inputs.version || github.event.client_payload.version || env.DEFAULT_DOCUMENTDB_VERSION }}" + RAW_VERSION="${{ inputs.version || env.DEFAULT_DOCUMENTDB_VERSION }}" if [[ "$RAW_VERSION" =~ ^[0-9]+\.[0-9]+-[0-9]+$ ]]; then VERSION="${RAW_VERSION/-/.}" else @@ -70,28 +79,31 @@ jobs: exit 1 fi VERSION_DASH=$(echo "$VERSION" | sed -E 's/^([0-9]+\.[0-9]+)\.([0-9]+)$/\1-\2/') + # APT package version to pin. Defaults to the dotted semver version. + APT_VERSION="${{ inputs.documentdb_apt_version || '' }}" + if [[ -z "$APT_VERSION" ]]; then + APT_VERSION="$VERSION" + fi SHORT_SHA=$(echo "$GITHUB_SHA" | cut -c1-7) IMAGE_TAG="${VERSION}-build-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${SHORT_SHA}" GATEWAY_SOURCE_IMAGE="${{ env.GATEWAY_SOURCE_IMAGE_REPO }}:pg17-${VERSION}" echo "documentdb_version=$VERSION" >> $GITHUB_OUTPUT echo "documentdb_version_dash=$VERSION_DASH" >> $GITHUB_OUTPUT + echo "documentdb_apt_version=$APT_VERSION" >> $GITHUB_OUTPUT echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT echo "gateway_source_image=$GATEWAY_SOURCE_IMAGE" >> $GITHUB_OUTPUT echo "DocumentDB version: $VERSION" - echo "Release tag: v$VERSION_DASH" + echo "APT package version: $APT_VERSION" echo "Candidate image tag: $IMAGE_TAG" echo "Gateway source image: $GATEWAY_SOURCE_IMAGE" - - name: Verify public extension release assets - env: - VERSION_DASH: ${{ steps.version.outputs.documentdb_version_dash }} + - name: Verify official DocumentDB APT repository is reachable run: | set -euo pipefail - for ARCH in amd64 arm64; do - ASSET_URL="https://github.com/${{ env.DOCUMENTDB_RELEASE_REPO }}/releases/download/v${VERSION_DASH}/deb13-postgresql-18-documentdb_${VERSION_DASH}_${ARCH}.deb" - echo "Checking $ASSET_URL" - curl -fsI -L "$ASSET_URL" >/dev/null - done + echo "Checking keyring: ${{ env.DOCUMENTDB_APT_KEYRING_URL }}" + curl -fsI -L "${{ env.DOCUMENTDB_APT_KEYRING_URL }}" >/dev/null + echo "Checking APT repo: ${{ env.DOCUMENTDB_APT_REPO_URL }}" + curl -fsI -L "${{ env.DOCUMENTDB_APT_REPO_URL }}/dists/stable/Release" >/dev/null - name: Verify public gateway source image env: @@ -128,16 +140,6 @@ jobs: with: persist-credentials: false - - name: Download public extension package - if: matrix.image.name == 'documentdb' - run: | - set -euo pipefail - mkdir -p packages - DEB_FILE="deb13-postgresql-18-documentdb_${{ needs.resolve-public-artifacts.outputs.documentdb_version_dash }}_${{ matrix.arch }}.deb" - ASSET_URL="https://github.com/${{ env.DOCUMENTDB_RELEASE_REPO }}/releases/download/v${{ needs.resolve-public-artifacts.outputs.documentdb_version_dash }}/${DEB_FILE}" - curl -fsSL -o "packages/${DEB_FILE}" -L "$ASSET_URL" - ls -lh packages/ - - name: Login to GHCR run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin @@ -151,9 +153,9 @@ jobs: case "${{ matrix.image.name }}" in documentdb) - DEB_FILE="deb13-postgresql-18-documentdb_${{ needs.resolve-public-artifacts.outputs.documentdb_version_dash }}_${{ matrix.arch }}.deb" - echo "Using deb: $DEB_FILE" - BUILD_ARGS="--build-arg PG_MAJOR=18 --build-arg DEB_PACKAGE_REL_PATH=packages/$DEB_FILE" + APT_VERSION="${{ needs.resolve-public-artifacts.outputs.documentdb_apt_version }}" + echo "Installing postgresql-18-documentdb=$APT_VERSION from official APT repo" + BUILD_ARGS="--build-arg PG_MAJOR=18 --build-arg DOCUMENTDB_APT_VERSION=$APT_VERSION" ;; gateway) echo "Using public gateway source image: ${{ needs.resolve-public-artifacts.outputs.gateway_source_image }}" @@ -223,7 +225,7 @@ jobs: echo "" >> $GITHUB_STEP_SUMMARY echo "- **DocumentDB Version**: \`${{ needs.resolve-public-artifacts.outputs.documentdb_version }}\`" >> $GITHUB_STEP_SUMMARY echo "- **Candidate Image Tag**: \`${{ needs.resolve-public-artifacts.outputs.image_tag }}\`" >> $GITHUB_STEP_SUMMARY - echo "- **Extension Package Source**: \`https://github.com/${{ env.DOCUMENTDB_RELEASE_REPO }}/releases/download/v${{ needs.resolve-public-artifacts.outputs.documentdb_version_dash }}/deb13-postgresql-18-documentdb_${{ needs.resolve-public-artifacts.outputs.documentdb_version_dash }}_{amd64,arm64}.deb\`" >> $GITHUB_STEP_SUMMARY + echo "- **Extension Package Source**: \`${{ env.DOCUMENTDB_APT_REPO_URL }}\` (postgresql-18-documentdb=${{ needs.resolve-public-artifacts.outputs.documentdb_apt_version }})" >> $GITHUB_STEP_SUMMARY echo "- **Gateway Source Image**: \`${{ needs.resolve-public-artifacts.outputs.gateway_source_image }}\`" >> $GITHUB_STEP_SUMMARY echo "- **Images**: documentdb, gateway" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/watch_documentdb_images.yml b/.github/workflows/watch_documentdb_images.yml index e4c4d29b2..0ce6a1a58 100644 --- a/.github/workflows/watch_documentdb_images.yml +++ b/.github/workflows/watch_documentdb_images.yml @@ -1,7 +1,7 @@ name: WATCH - DocumentDB Releases -# Polls the upstream documentdb/documentdb repository for new releases and, when a -# newer version than the operator's current default is published, automatically: +# Reacts to new upstream DocumentDB releases and, when a newer version than the +# operator's current default is published, automatically: # 1. Builds candidate documentdb + gateway images (build_documentdb_images.yml) # 2. Promotes them to release tags and opens a "chore: bump DocumentDB images" PR # (release_documentdb_images.yml) @@ -9,14 +9,25 @@ name: WATCH - DocumentDB Releases # The version-bump PR is the human gate: a maintainer reviews and merges it, which # is what actually makes the new version the default for new installs. # +# Primary trigger: a `repository_dispatch` event of type `documentdb-release`, +# sent by the upstream documentdb/documentdb repository on `release: published`. +# A reference sender workflow lives in docs/designs/upstream-release-dispatch-sender.md. +# A low-frequency cron poll is kept only as a safety net in case the dispatch is +# missed, and `workflow_dispatch` allows manual runs. +# # Only handles the DATABASE version track (documentDbVersion). Operator/sidecar # images follow a separate track (build_operator_images.yml / release_operator.yml). on: + # Primary trigger: upstream fires this on release: published. + # client_payload: { "version": "0.111.0", "apt_version": "0.111.0" (optional) } + repository_dispatch: + types: [documentdb-release] + + # Safety-net poll in case a dispatch event is missed. GitHub's releases/latest + # excludes drafts and pre-releases, so pre-releases never trigger this. schedule: - # Every 6 hours. GitHub's releases/latest excludes drafts and pre-releases, - # so pre-releases never trigger this automation. - - cron: '0 */6 * * *' + - cron: '0 7 * * *' workflow_dispatch: inputs: @@ -24,6 +35,10 @@ on: description: 'Override upstream version to release (e.g. 0.111.0). Leave empty to auto-detect latest.' required: false default: '' + documentdb_apt_version: + description: 'Debian package version of postgresql-18-documentdb to pin. Leave empty to derive from version.' + required: false + default: '' dry_run: description: 'Only detect and report; do not build or open a PR.' required: false @@ -48,6 +63,7 @@ jobs: runs-on: ubuntu-22.04 outputs: new_version: ${{ steps.check.outputs.new_version }} + apt_version: ${{ steps.upstream.outputs.apt_version }} should_release: ${{ steps.check.outputs.should_release }} steps: - name: Checkout code @@ -61,8 +77,13 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | set -euo pipefail + # Precedence: repository_dispatch payload > manual override > auto-detect latest. + PAYLOAD_VERSION="${{ github.event.client_payload.version || '' }}" OVERRIDE="${{ github.event.inputs.version || '' }}" - if [[ -n "$OVERRIDE" ]]; then + if [[ -n "$PAYLOAD_VERSION" ]]; then + RAW="$PAYLOAD_VERSION" + echo "Using repository_dispatch payload version: $RAW" + elif [[ -n "$OVERRIDE" ]]; then RAW="$OVERRIDE" echo "Using manual version override: $RAW" else @@ -80,7 +101,10 @@ jobs: echo "Could not parse a dotted semver from upstream tag '$RAW'" >&2 exit 1 fi + # Optional APT package version to pin (payload or manual input). + APT_VERSION="${{ github.event.client_payload.apt_version || github.event.inputs.documentdb_apt_version || '' }}" echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "apt_version=$APT_VERSION" >> "$GITHUB_OUTPUT" echo "Upstream DocumentDB version: $VERSION" - name: Read current default version @@ -112,14 +136,26 @@ jobs: exit 0 fi - # Already promoted? If the release tag exists, the bump PR is likely - # pending review/merge, so don't rebuild on every cron tick. + # Already promoted? Only skip when BOTH the documentdb and gateway + # release images exist for this version. If just one is present (a + # partial/aborted promotion), fall through and rebuild to converge. echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + DOCDB_EXISTS=false + GW_EXISTS=false if docker manifest inspect "ghcr.io/${{ github.repository }}/documentdb:${NEW}" >/dev/null 2>&1; then - echo "Release image documentdb:${NEW} already exists; version-bump PR is likely pending merge. Skipping." + DOCDB_EXISTS=true + fi + if docker manifest inspect "ghcr.io/${{ github.repository }}/gateway:${NEW}" >/dev/null 2>&1; then + GW_EXISTS=true + fi + if [[ "$DOCDB_EXISTS" == "true" && "$GW_EXISTS" == "true" ]]; then + echo "Release images documentdb:${NEW} and gateway:${NEW} already exist; version-bump PR is likely pending merge. Skipping." echo "should_release=false" >> "$GITHUB_OUTPUT" exit 0 fi + if [[ "$DOCDB_EXISTS" == "true" || "$GW_EXISTS" == "true" ]]; then + echo "Partial promotion detected (documentdb=$DOCDB_EXISTS, gateway=$GW_EXISTS); rebuilding to converge." + fi if [[ "${{ github.event.inputs.dry_run }}" == "true" ]]; then echo "Dry run: a newer version $NEW (current $CUR) was detected but no build/PR will be created." @@ -149,6 +185,7 @@ jobs: uses: ./.github/workflows/build_documentdb_images.yml with: version: ${{ needs.detect.outputs.new_version }} + documentdb_apt_version: ${{ needs.detect.outputs.apt_version }} secrets: inherit # --------------------------------------------------------------------------- diff --git a/AGENTS.md b/AGENTS.md index 456c9575e..2e0733bfe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -286,7 +286,7 @@ Types: - `build_documentdb_images.yml` - Build documentdb/gateway candidate images (database version track) - `release_operator.yml` - Promote operator/sidecar images and publish Helm chart - `release_documentdb_images.yml` - Promote documentdb/gateway images and auto-PR version bumps -- `watch_documentdb_images.yml` - Poll upstream documentdb/documentdb releases (cron) and auto-build + open version-bump PR on a new release +- `watch_documentdb_images.yml` - React to upstream documentdb/documentdb releases via `repository_dispatch` (`documentdb-release`, primary) with a daily cron safety-net, then auto-build + open version-bump PR on a new release (sender draft: `docs/designs/upstream-release-dispatch-sender.md`) - `build_images.yml` - [DEPRECATED] Combined image builds (replaced by split workflows above) - `release_images.yml` - [DEPRECATED] Combined release (replaced by split workflows above) - `deploy_docs.yml` - Documentation deployment diff --git a/docs/designs/image-management.md b/docs/designs/image-management.md index 81d7baf28..aee4bb55c 100644 --- a/docs/designs/image-management.md +++ b/docs/designs/image-management.md @@ -44,7 +44,7 @@ All images are published to **GitHub Container Registry (GHCR)** under `ghcr.io/ | Image | GHCR Path | Source | Dockerfile | Purpose | |-------|-----------|--------|------------|---------| -| **documentdb** | `.../documentdb` | Public `deb13` PostgreSQL 18 package from `documentdb/documentdb` releases | `.github/dockerfiles/Dockerfile_extension` | DocumentDB PostgreSQL extension files for CNPG ImageVolume mode | +| **documentdb** | `.../documentdb` | Official `postgresql-18-documentdb` package from the DocumentDB APT repo (`https://documentdb.io/deb`) | `.github/dockerfiles/Dockerfile_extension` | DocumentDB PostgreSQL extension files for CNPG ImageVolume mode | | **gateway** | `.../gateway` | Public gateway payload copied from `ghcr.io/documentdb/documentdb/documentdb-local:pg17-` | `.github/dockerfiles/Dockerfile_gateway_public_image` | MongoDB wire-protocol gateway binary (Rust) | ### External Image (Not Built Here) @@ -203,20 +203,20 @@ Builds documentdb extension and gateway images from public DocumentDB release ar | Aspect | Details | |--------|---------| -| **Trigger** | `workflow_dispatch`, `repository_dispatch` (from upstream) | +| **Trigger** | `workflow_dispatch` (manual), `workflow_call` (invoked by `watch_documentdb_images.yml`) | | **Images** | documentdb, gateway | | **Dockerfiles** | `.github/dockerfiles/Dockerfile_extension`, `.github/dockerfiles/Dockerfile_gateway_public_image` | | **Tag pattern** | `{documentdb_version}-build-{run_id}-{attempt}-{sha}` (candidate) | | **Build time** | ~5 minutes (public artifact download + image build) | | **Multi-arch** | amd64 + arm64 → multi-arch manifest | | **Signing** | cosign keyless (OIDC) | -| **Version detection** | Workflow input / repository dispatch payload (defaults to released `0.110.0`) | +| **Version detection** | Workflow input `version` (track) + optional `documentdb_apt_version` (APT pin); defaults to released `0.110.0` | The build process: -1. Resolves the released DocumentDB version to package -2. Downloads the public `deb13` PostgreSQL 18 extension package from `documentdb/documentdb` release assets +1. Resolves the released DocumentDB version to package (and the APT package version to pin) +2. Verifies the official DocumentDB APT repo (`https://documentdb.io/deb`) and signing keyring are reachable 3. Verifies the public multi-arch `documentdb-local:pg17-` image exists -4. Builds `Dockerfile_extension` using the public extension `.deb` (installs pg_cron, pgvector, postgis alongside) +4. Builds `Dockerfile_extension`, installing `postgresql-18-documentdb` from the official APT repo (its meta-package pulls in Citus, RUM, pgvector, PostGIS, etc.) 5. Builds `Dockerfile_gateway_public_image` by copying the gateway binary and runtime files from the public upstream image ### Dockerfile Details @@ -236,7 +236,7 @@ The build process: - **Multi-stage**: 2 stages - **No entrypoint** — this is an ImageVolume source, not a runnable container - Follows the [cloudnative-pg/postgres-extensions-containers](https://github.com/cloudnative-pg/postgres-extensions-containers) pattern -- Installs DocumentDB extension + pg_cron + pgvector + PostGIS +- Installs `postgresql-18-documentdb` from the official DocumentDB APT repo (`https://documentdb.io/deb`); the meta-package pulls in its own dependencies (Citus, RUM, pgvector, PostGIS, ...) - Copies only extension artifacts (`.so`, `.control`, `.sql`, bitcode) and required system libraries - Resolves Debian-alternatives symlinks (they break in ImageVolume mode) @@ -309,20 +309,27 @@ repository for new releases and drives the database track end-to-end without man intervention. This is the automation behind keeping new installs on the latest DocumentDB version. +The **primary trigger** is a `repository_dispatch` event of type `documentdb-release` +sent by the upstream repo on `release: published`. See +[upstream-release-dispatch-sender.md](upstream-release-dispatch-sender.md) for the +reference sender workflow that lives in `documentdb/documentdb`. A daily cron poll +is kept only as a safety net in case a dispatch is missed. + | Aspect | Details | |--------|---------| -| **Trigger** | `schedule` (cron `0 */6 * * *`), `workflow_dispatch` (manual, with optional `version` override and `dry_run`) | -| **Detection** | Reads upstream `releases/latest` (drafts and pre-releases are excluded by GitHub) and compares against the current `DEFAULT_DOCUMENTDB_IMAGE` in `constants.go` | +| **Trigger** | `repository_dispatch` (`documentdb-release`, primary), `schedule` (daily cron safety-net), `workflow_dispatch` (manual, with optional `version` / `documentdb_apt_version` override and `dry_run`) | +| **Detection** | Reads the dispatch payload version (or upstream `releases/latest` for cron/manual; drafts and pre-releases are excluded by GitHub) and compares against the current `DEFAULT_DOCUMENTDB_IMAGE` in `constants.go` | | **Chaining** | Calls `build_documentdb_images.yml` then `release_documentdb_images.yml` as reusable workflows (`workflow_call`) | | **Human gate** | The auto-generated `chore: bump DocumentDB images` PR — a maintainer reviews and merges it to make the new version the default | ``` Flow: 1. detect - ├── Resolve upstream latest release tag (e.g. v0.111-0 → 0.111.0) + ├── Resolve version (dispatch payload → manual override → upstream latest) ├── Read current default from constants.go - ├── Skip if not newer, if release tag already exists (PR pending), or dry_run - └── Output: should_release, new_version + ├── Skip if not newer, if BOTH documentdb+gateway release tags already exist + │ (PR pending), or dry_run; rebuild on partial promotion + └── Output: should_release, new_version, apt_version 2. build (uses build_documentdb_images.yml) ── if should_release └── Output: image_tag (candidate) @@ -333,9 +340,11 @@ Flow: └── update_defaults: true → opens the version-bump PR ``` -Idempotency: once the release images are promoted, the `documentdb:` -tag exists, so subsequent cron ticks short-circuit until the bump PR is merged -(which advances the default and stops further detection for that version). +Idempotency: once the release images are promoted, both the `documentdb:` +and `gateway:` tags exist, so subsequent triggers short-circuit until the +bump PR is merged (which advances the default and stops further detection for that +version). A partial promotion (only one of the two tags) does not short-circuit — +the build re-runs to converge. --- diff --git a/docs/designs/upstream-release-dispatch-sender.md b/docs/designs/upstream-release-dispatch-sender.md new file mode 100644 index 000000000..546349771 --- /dev/null +++ b/docs/designs/upstream-release-dispatch-sender.md @@ -0,0 +1,105 @@ +# Upstream Release Dispatch Sender (Draft / Reference) + +> **Status:** Reference draft. This workflow does **not** live in this repository — +> it must be added to the upstream **`documentdb/documentdb`** repository. It is +> provided here so the upstream maintainers (e.g. Guanzhou) can wire DocumentDB +> releases to this operator's image automation. + +## What it does + +When `documentdb/documentdb` publishes a GitHub release, this workflow sends a +[`repository_dispatch`](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#repository_dispatch) +event of type `documentdb-release` to +`documentdb/documentdb-kubernetes-operator`. That event is the **primary trigger** +for [`watch_documentdb_images.yml`](../../.github/workflows/watch_documentdb_images.yml), +which builds candidate `documentdb` + `gateway` images for the new version and +opens a "chore: bump DocumentDB images" PR (the human merge gate). + +This replaces polling: instead of the operator repo checking upstream every day, +upstream notifies the operator repo the moment a release is published. The daily +cron in `watch_documentdb_images.yml` remains only as a safety net. + +## Prerequisites + +A credential in the **upstream** repo that is allowed to dispatch into the +operator repo. Either: + +- A **fine-grained PAT** (or classic PAT with `repo` scope) belonging to a user + with write access to `documentdb/documentdb-kubernetes-operator`, stored as a + secret named `OPERATOR_DISPATCH_TOKEN`; **or** +- A **GitHub App** installed on the operator repo with `contents: write` (or the + `repository_dispatch` permission), with its token minted at run time. + +A fine-grained PAT scoped to only the operator repo with the **Contents: +read/write** permission is the least-privilege option. + +## Reference workflow (add to `documentdb/documentdb`) + +```yaml +# .github/workflows/notify-operator.yml (in documentdb/documentdb) +name: Notify operator of new release + +on: + release: + types: [published] + +permissions: + contents: read + +jobs: + dispatch: + runs-on: ubuntu-latest + steps: + - name: Send repository_dispatch to operator repo + env: + # Least-privilege fine-grained PAT (Contents: write) scoped to the + # operator repo, or a GitHub App token. Do NOT use the default + # GITHUB_TOKEN — it cannot dispatch across repositories. + DISPATCH_TOKEN: ${{ secrets.OPERATOR_DISPATCH_TOKEN }} + run: | + set -euo pipefail + # Normalize tag (strip leading 'v'; dashed 0.110-0 -> dotted 0.110.0). + RAW="${GITHUB_REF_NAME#v}" + if [[ "$RAW" =~ ^[0-9]+\.[0-9]+-[0-9]+$ ]]; then + VERSION="${RAW/-/.}" + else + VERSION="$RAW" + fi + echo "Dispatching documentdb-release for version $VERSION" + curl -fsSL -X POST \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${DISPATCH_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/documentdb/documentdb-kubernetes-operator/dispatches \ + -d "$(jq -nc --arg v "$VERSION" \ + '{event_type:"documentdb-release", client_payload:{version:$v, apt_version:$v}}')" +``` + +## Payload contract + +The operator's `watch_documentdb_images.yml` reads: + +| Field | Required | Meaning | +|-----------------------------|----------|-------------------------------------------------------------------------| +| `client_payload.version` | yes | Dotted semver of the release (e.g. `0.111.0`). Drives image tag + track. | +| `client_payload.apt_version`| no | Debian package version of `postgresql-18-documentdb` to pin. Defaults to `version`. | + +If `apt_version` differs from `version` (the APT `stable` channel may publish a +different revision than the dotted release version), set it explicitly so the +extension image pins the exact package. + +## Testing without upstream + +Until the sender lands upstream, the same flow can be exercised manually: + +- Manually run `watch_documentdb_images.yml` via **workflow_dispatch** with a + `version` (and optional `documentdb_apt_version`) input, or +- Send a one-off dispatch with a token that has access to the operator repo: + + ```bash + curl -fsSL -X POST \ + -H "Authorization: Bearer " \ + -H "Accept: application/vnd.github+json" \ + https://api.github.com/repos/documentdb/documentdb-kubernetes-operator/dispatches \ + -d '{"event_type":"documentdb-release","client_payload":{"version":"0.111.0"}}' + ``` From 90d90cd294dc9f275c40a091b5ec9603a80e070b Mon Sep 17 00:00:00 2001 From: Ritvik Jayaswal Date: Mon, 29 Jun 2026 14:15:32 -0400 Subject: [PATCH 3/8] fix(ci): pin DocumentDB APT package with dashed Debian version The official APT repo serves dashed Debian versions (e.g. 0.113-0), not dotted semver. Default the apt pin to VERSION_DASH and verify the exact postgresql-18-documentdb= is present in the stable index (with bounded retry to absorb publish lag) before building, instead of only checking that the repo responds. Addresses review feedback from WentingWu666666 on PR #410. Co-authored-by: Copilot Signed-off-by: Ritvik Jayaswal --- .github/dockerfiles/Dockerfile_extension | 8 ++-- .github/workflows/build_documentdb_images.yml | 43 ++++++++++++++++--- .github/workflows/watch_documentdb_images.yml | 4 +- .../upstream-release-dispatch-sender.md | 14 +++--- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/.github/dockerfiles/Dockerfile_extension b/.github/dockerfiles/Dockerfile_extension index 9bccb802c..2879e32a2 100644 --- a/.github/dockerfiles/Dockerfile_extension +++ b/.github/dockerfiles/Dockerfile_extension @@ -15,7 +15,7 @@ # Usage: # docker build \ # --build-arg PG_MAJOR=18 \ -# --build-arg DOCUMENTDB_APT_VERSION=0.110.0 \ +# --build-arg DOCUMENTDB_APT_VERSION=0.110-0 \ # -t documentdb-extension:latest \ # -f Dockerfile_extension . @@ -24,8 +24,10 @@ FROM ${BASE} AS builder ARG PG_MAJOR=18 # Full Debian package version of postgresql-${PG_MAJOR}-documentdb to install -# from the official DocumentDB APT repository. Pin this per build for -# reproducibility. If empty, the latest version in the 'stable' channel is used. +# from the official DocumentDB APT repository, in dashed Debian format (e.g. +# 0.110-0, not dotted 0.110.0 — apt's '=' requires an exact string match). Pin +# this per build for reproducibility. If empty, the latest version in the +# 'stable' channel is used. ARG DOCUMENTDB_APT_VERSION USER 0 diff --git a/.github/workflows/build_documentdb_images.yml b/.github/workflows/build_documentdb_images.yml index a6a08c577..e326adf74 100644 --- a/.github/workflows/build_documentdb_images.yml +++ b/.github/workflows/build_documentdb_images.yml @@ -15,7 +15,7 @@ on: required: false default: '0.110.0' documentdb_apt_version: - description: 'Debian package version of postgresql-18-documentdb to pin (e.g. 0.110.0). Leave empty to derive from version.' + description: 'Debian package version of postgresql-18-documentdb to pin, dashed format (e.g. 0.110-0). Leave empty to derive from version.' required: false default: '' documentdb_gateway_image_repo: @@ -31,7 +31,7 @@ on: type: string default: '0.110.0' documentdb_apt_version: - description: 'Debian package version of postgresql-18-documentdb to pin. Leave empty to derive from version.' + description: 'Debian package version of postgresql-18-documentdb to pin, dashed format (e.g. 0.110-0). Leave empty to derive from version.' required: false type: string default: '' @@ -84,10 +84,11 @@ jobs: exit 1 fi VERSION_DASH=$(echo "$VERSION" | sed -E 's/^([0-9]+\.[0-9]+)\.([0-9]+)$/\1-\2/') - # APT package version to pin. Defaults to the dotted semver version. + # APT package version to pin. The official APT repo serves dashed Debian + # versions (e.g. 0.113-0), not dotted semver, so default to VERSION_DASH. APT_VERSION="${{ inputs.documentdb_apt_version || '' }}" if [[ -z "$APT_VERSION" ]]; then - APT_VERSION="$VERSION" + APT_VERSION="$VERSION_DASH" fi SHORT_SHA=$(echo "$GITHUB_SHA" | cut -c1-7) IMAGE_TAG="${VERSION}-build-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${SHORT_SHA}" @@ -102,13 +103,41 @@ jobs: echo "Candidate image tag: $IMAGE_TAG" echo "Gateway source image: $GATEWAY_SOURCE_IMAGE" - - name: Verify official DocumentDB APT repository is reachable + - name: Verify DocumentDB package is available in the APT index + env: + APT_VERSION: ${{ steps.version.outputs.documentdb_apt_version }} run: | set -euo pipefail echo "Checking keyring: ${{ env.DOCUMENTDB_APT_KEYRING_URL }}" curl -fsI -L "${{ env.DOCUMENTDB_APT_KEYRING_URL }}" >/dev/null - echo "Checking APT repo: ${{ env.DOCUMENTDB_APT_REPO_URL }}" - curl -fsI -L "${{ env.DOCUMENTDB_APT_REPO_URL }}/dists/stable/Release" >/dev/null + # Verify the exact package version is actually present in the index, not + # just that the repo responds. The release that triggered this build and + # the APT channel are published by separate pipelines, so absorb publish + # lag with a bounded retry before failing. + for ARCH in amd64 arm64; do + PKG_URL="${{ env.DOCUMENTDB_APT_REPO_URL }}/dists/stable/main/binary-${ARCH}/Packages" + echo "Looking for postgresql-18-documentdb=${APT_VERSION} (${ARCH}) in $PKG_URL" + FOUND="" + for ATTEMPT in 1 2 3 4 5 6; do + # Index may be served plain or gzip-compressed; try both. + PACKAGES=$(curl -fsSL --compressed "$PKG_URL" 2>/dev/null \ + || curl -fsSL "${PKG_URL}.gz" 2>/dev/null | gunzip -c) || PACKAGES="" + if printf '%s\n' "$PACKAGES" \ + | grep -A1 '^Package: postgresql-18-documentdb$' \ + | grep -qx "Version: ${APT_VERSION}"; then + FOUND=yes + break + fi + echo "Not in index yet (attempt ${ATTEMPT}/6); retrying in 30s..." + sleep 30 + done + if [[ -z "$FOUND" ]]; then + echo "postgresql-18-documentdb=${APT_VERSION} (${ARCH}) not found in APT 'stable' index." >&2 + echo "The release may not yet be published to ${{ env.DOCUMENTDB_APT_REPO_URL }}." >&2 + exit 1 + fi + echo "Found postgresql-18-documentdb=${APT_VERSION} (${ARCH})." + done - name: Verify public gateway source image env: diff --git a/.github/workflows/watch_documentdb_images.yml b/.github/workflows/watch_documentdb_images.yml index 0ce6a1a58..bf19f018a 100644 --- a/.github/workflows/watch_documentdb_images.yml +++ b/.github/workflows/watch_documentdb_images.yml @@ -20,7 +20,7 @@ name: WATCH - DocumentDB Releases on: # Primary trigger: upstream fires this on release: published. - # client_payload: { "version": "0.111.0", "apt_version": "0.111.0" (optional) } + # client_payload: { "version": "0.111.0", "apt_version": "0.111-0" (optional, dashed Debian version) } repository_dispatch: types: [documentdb-release] @@ -36,7 +36,7 @@ on: required: false default: '' documentdb_apt_version: - description: 'Debian package version of postgresql-18-documentdb to pin. Leave empty to derive from version.' + description: 'Debian package version of postgresql-18-documentdb to pin, dashed format (e.g. 0.111-0). Leave empty to derive from version.' required: false default: '' dry_run: diff --git a/docs/designs/upstream-release-dispatch-sender.md b/docs/designs/upstream-release-dispatch-sender.md index 546349771..199824c91 100644 --- a/docs/designs/upstream-release-dispatch-sender.md +++ b/docs/designs/upstream-release-dispatch-sender.md @@ -66,13 +66,16 @@ jobs: VERSION="$RAW" fi echo "Dispatching documentdb-release for version $VERSION" + # Omit apt_version: the operator derives the dashed Debian version + # (0.111.0 -> 0.111-0) the APT repo expects. Only send apt_version + # explicitly if the APT channel uses a non-standard revision. curl -fsSL -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer ${DISPATCH_TOKEN}" \ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/repos/documentdb/documentdb-kubernetes-operator/dispatches \ -d "$(jq -nc --arg v "$VERSION" \ - '{event_type:"documentdb-release", client_payload:{version:$v, apt_version:$v}}')" + '{event_type:"documentdb-release", client_payload:{version:$v}}')" ``` ## Payload contract @@ -82,11 +85,12 @@ The operator's `watch_documentdb_images.yml` reads: | Field | Required | Meaning | |-----------------------------|----------|-------------------------------------------------------------------------| | `client_payload.version` | yes | Dotted semver of the release (e.g. `0.111.0`). Drives image tag + track. | -| `client_payload.apt_version`| no | Debian package version of `postgresql-18-documentdb` to pin. Defaults to `version`. | +| `client_payload.apt_version`| no | Debian package version of `postgresql-18-documentdb` to pin, in dashed Debian format (e.g. `0.111-0`). Defaults to the dashed form of `version`. | -If `apt_version` differs from `version` (the APT `stable` channel may publish a -different revision than the dotted release version), set it explicitly so the -extension image pins the exact package. +The official APT `stable` channel serves **dashed** Debian versions (e.g. +`0.111-0`), not dotted semver. If omitted, the operator derives `apt_version` +by converting the dotted `version` to its dashed form. Set it explicitly only +if the APT channel publishes a revision that doesn't follow that convention. ## Testing without upstream From ba17048fe5da18e7893e20717156b001b4cd18ab Mon Sep 17 00:00:00 2001 From: Ritvik Jayaswal Date: Tue, 30 Jun 2026 14:05:42 -0400 Subject: [PATCH 4/8] ci: exercise Dockerfile_extension in e2e build mode with local deb Addresses WentingWu666666's e2e/build-mode review feedback on PR #410: 1. test-e2e.yml now triggers on .github/dockerfiles/** so Dockerfile_extension changes run e2e. 2. probe-images forces build mode when Dockerfile_extension is in the PR diff, so registry mode no longer short-circuits the modified Dockerfile. 3. Dockerfile_extension gains an optional DOCUMENTDB_DEB_PACKAGE build arg (installed via RUN --mount=type=bind) so build mode validates the source-built/unpublished .deb instead of silently installing the latest published package. Build-mode workflows pass it instead of the now-unused DEB_PACKAGE_REL_PATH; runtime deps still resolve from the official APT repo. Co-authored-by: Copilot Signed-off-by: Ritvik Jayaswal --- .github/dockerfiles/Dockerfile_extension | 29 +++++++++++++++++-- .github/workflows/build_documentdb_images.yml | 3 ++ .github/workflows/build_images.yml | 5 +++- .github/workflows/test-build-and-package.yml | 21 +++++++++++++- .github/workflows/test-e2e.yml | 1 + 5 files changed, 54 insertions(+), 5 deletions(-) diff --git a/.github/dockerfiles/Dockerfile_extension b/.github/dockerfiles/Dockerfile_extension index 2879e32a2..ab3da7e16 100644 --- a/.github/dockerfiles/Dockerfile_extension +++ b/.github/dockerfiles/Dockerfile_extension @@ -1,3 +1,4 @@ +# syntax=docker/dockerfile:1 # DocumentDB extension image for CNPG ImageVolume mode. # Contains only extension artifacts (.so, .control, .sql, bitcode) and # runtime shared-library dependencies. PostgreSQL is provided by the @@ -12,12 +13,24 @@ # PCRE2, pgvector, PostGIS, ...), so no extra extension packages are installed # explicitly here. # +# For CI build mode, a locally-built .deb can be installed instead of the +# published package by passing DOCUMENTDB_DEB_PACKAGE (see below); its runtime +# dependencies are still resolved from the official APT repo. +# # Usage: +# # Install a pinned published version from the APT repo: # docker build \ # --build-arg PG_MAJOR=18 \ # --build-arg DOCUMENTDB_APT_VERSION=0.110-0 \ # -t documentdb-extension:latest \ # -f Dockerfile_extension . +# +# # Install a locally-built .deb (CI build mode, unpublished versions): +# docker build \ +# --build-arg PG_MAJOR=18 \ +# --build-arg DOCUMENTDB_DEB_PACKAGE=packages/postgresql-18-documentdb_0.111-0_amd64.deb \ +# -t documentdb-extension:latest \ +# -f Dockerfile_extension . ARG BASE=ghcr.io/cloudnative-pg/postgresql:18-minimal-trixie FROM ${BASE} AS builder @@ -29,10 +42,17 @@ ARG PG_MAJOR=18 # this per build for reproducibility. If empty, the latest version in the # 'stable' channel is used. ARG DOCUMENTDB_APT_VERSION +# Optional path (relative to the build context) to a locally-built +# postgresql-${PG_MAJOR}-documentdb .deb. When set, the extension is installed +# from this file instead of the APT repo — used by CI build mode to validate +# source-built / unpublished versions. Takes precedence over +# DOCUMENTDB_APT_VERSION. Runtime dependencies (Citus, RUM, pgvector, PostGIS, +# ...) are still resolved from the official APT repo configured below. +ARG DOCUMENTDB_DEB_PACKAGE USER 0 -RUN set -eux && \ +RUN --mount=type=bind,target=/ctx set -eux && \ # Snapshot base image system libraries for later diffing ldconfig -p | awk '{print $NF}' | grep '^/' | sort | uniq > /tmp/base-image-libs.out && \ # Tools needed to add the official DocumentDB APT repository @@ -44,8 +64,11 @@ RUN set -eux && \ echo "deb [signed-by=/usr/share/keyrings/documentdb-archive-keyring.gpg] https://documentdb.io/deb stable main" \ > /etc/apt/sources.list.d/documentdb.list && \ apt-get update && \ - # Install the DocumentDB extension meta-package from the official repo. - if [ -n "${DOCUMENTDB_APT_VERSION}" ]; then \ + # Install the DocumentDB extension: a locally-built .deb (CI build mode) + # takes precedence, then a pinned APT version, otherwise latest stable. + if [ -n "${DOCUMENTDB_DEB_PACKAGE}" ]; then \ + apt-get install -y --no-install-recommends "/ctx/${DOCUMENTDB_DEB_PACKAGE}"; \ + elif [ -n "${DOCUMENTDB_APT_VERSION}" ]; then \ apt-get install -y --no-install-recommends \ "postgresql-${PG_MAJOR}-documentdb=${DOCUMENTDB_APT_VERSION}"; \ else \ diff --git a/.github/workflows/build_documentdb_images.yml b/.github/workflows/build_documentdb_images.yml index e326adf74..0c411d883 100644 --- a/.github/workflows/build_documentdb_images.yml +++ b/.github/workflows/build_documentdb_images.yml @@ -178,6 +178,9 @@ jobs: run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - name: Build and Push ${{ matrix.image.name }} (${{ matrix.arch }}) + env: + # Dockerfile_extension uses RUN --mount, which requires BuildKit. + DOCKER_BUILDKIT: '1' run: | set -euo pipefail TAG=${{ env.IMAGE_TAG }}-${{ matrix.arch }} diff --git a/.github/workflows/build_images.yml b/.github/workflows/build_images.yml index 1134512bf..f42f5b056 100644 --- a/.github/workflows/build_images.yml +++ b/.github/workflows/build_images.yml @@ -179,6 +179,9 @@ jobs: - name: Build and Push ${{ matrix.image.name }} (${{ matrix.arch }}) if: steps.should_build.outputs.skip != 'true' + env: + # Dockerfile_extension uses RUN --mount, which requires BuildKit. + DOCKER_BUILDKIT: '1' run: | set -euo pipefail TAG=${{ env.IMAGE_TAG }}-${{ matrix.arch }} @@ -199,7 +202,7 @@ jobs: exit 1 fi echo "Using deb: $DEB_FILE" - BUILD_ARGS="--build-arg PG_MAJOR=18 --build-arg DEB_PACKAGE_REL_PATH=packages/$DEB_FILE" + BUILD_ARGS="--build-arg PG_MAJOR=18 --build-arg DOCUMENTDB_DEB_PACKAGE=packages/$DEB_FILE" ;; gateway) GW_DEB=$(ls gateway-context/packages/ | grep -E 'gateway.*\.deb' | head -1) diff --git a/.github/workflows/test-build-and-package.yml b/.github/workflows/test-build-and-package.yml index 44f36295b..22ec85212 100644 --- a/.github/workflows/test-build-and-package.yml +++ b/.github/workflows/test-build-and-package.yml @@ -47,6 +47,8 @@ jobs: ext_image_tag: ${{ steps.probe.outputs.ext_image_tag }} steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Probe documentdb + gateway images id: probe shell: bash @@ -74,6 +76,23 @@ jobs: exit 0 fi + # Force build mode when the extension Dockerfile changed in this PR, so + # the modified Dockerfile is actually exercised instead of pulling a + # prebuilt image from the registry (registry mode would short-circuit it). + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + BASE_SHA="${{ github.event.pull_request.base.sha }}" + if git cat-file -e "$BASE_SHA" 2>/dev/null; then + if git diff --name-only "$BASE_SHA" HEAD \ + | grep -qx '.github/dockerfiles/Dockerfile_extension'; then + echo ".github/dockerfiles/Dockerfile_extension changed in this PR → forcing build mode." + echo "mode=build" >> "$GITHUB_OUTPUT" + exit 0 + fi + else + echo "::warning::base SHA $BASE_SHA unavailable; skipping Dockerfile-change detection." + fi + fi + OWNER="${{ github.repository_owner }}" # Use the GitHub Packages REST API (works with the workflow's # GITHUB_TOKEN — no anonymous registry token dance required). @@ -399,7 +418,7 @@ jobs: docker buildx build \ --platform linux/${{ matrix.arch }} \ --build-arg PG_MAJOR=18 \ - --build-arg DEB_PACKAGE_REL_PATH=packages/$DEB_FILE \ + --build-arg DOCUMENTDB_DEB_PACKAGE=packages/$DEB_FILE \ --tag "$TARGET" \ --load \ -f .github/dockerfiles/Dockerfile_extension . diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index bfbe52e7b..9fbb4499d 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -23,6 +23,7 @@ on: - 'operator/documentdb-helm-chart/**' - '.github/workflows/test-e2e.yml' - '.github/workflows/test-build-and-package.yml' + - '.github/dockerfiles/**' - '.github/actions/**' workflow_dispatch: inputs: From 80d0f0a1a3a47cf49cf11169bd33dff272acf583 Mon Sep 17 00:00:00 2001 From: Ritvik Jayaswal Date: Tue, 30 Jun 2026 14:23:58 -0400 Subject: [PATCH 5/8] fix(ci): parse APT Packages index by stanza in documentdb verify The verify step matched the package version with grep -A1 '^Package:' | grep -qx 'Version: ...', which assumes Version is the line immediately after Package. In the real Debian Packages index the stanza order is Package / Source / Version, so the Version line was never captured and the check produced a false negative even when the version was published (e.g. 0.113-0). Replace it with a stanza-aware awk parser. Co-authored-by: Copilot Signed-off-by: Ritvik Jayaswal --- .github/workflows/build_documentdb_images.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_documentdb_images.yml b/.github/workflows/build_documentdb_images.yml index 0c411d883..799304343 100644 --- a/.github/workflows/build_documentdb_images.yml +++ b/.github/workflows/build_documentdb_images.yml @@ -122,9 +122,14 @@ jobs: # Index may be served plain or gzip-compressed; try both. PACKAGES=$(curl -fsSL --compressed "$PKG_URL" 2>/dev/null \ || curl -fsSL "${PKG_URL}.gz" 2>/dev/null | gunzip -c) || PACKAGES="" - if printf '%s\n' "$PACKAGES" \ - | grep -A1 '^Package: postgresql-18-documentdb$' \ - | grep -qx "Version: ${APT_VERSION}"; then + # Parse the Debian Packages index stanza-by-stanza (fields are + # blank-line separated and NOT in a fixed order — Version typically + # follows Source, not Package — so a positional grep won't work). + if printf '%s\n' "$PACKAGES" | awk -v pkg="postgresql-18-documentdb" -v ver="$APT_VERSION" ' + /^Package:[[:space:]]/ { p=$2 } + /^Version:[[:space:]]/ { v=$2 } + /^[[:space:]]*$/ { if (p==pkg && v==ver) { found=1; exit }; p=""; v="" } + END { if (p==pkg && v==ver) found=1; exit found?0:1 }'; then FOUND=yes break fi From 6d18ca9d6eeb802a9dfe72cff39eafc198557be9 Mon Sep 17 00:00:00 2001 From: Ritvik Jayaswal Date: Tue, 30 Jun 2026 14:30:05 -0400 Subject: [PATCH 6/8] fix(ci): lowercase GHCR repository in documentdb image refs GHCR rejects tags whose repository path contains uppercase characters (repository name must be lowercase). The image references were built directly from github.repository, which preserves the owner login case, so builds failed on forks whose owner has uppercase letters (e.g. Ritvik-Jayaswal). Normalize the owner/repo to lowercase for all ghcr.io image refs in the documentdb build, watch idempotency check, and promote workflows. The cosign certificate-identity-regexp keeps the original case to match the OIDC certificate subject. Co-authored-by: Copilot Signed-off-by: Ritvik Jayaswal --- .github/workflows/build_documentdb_images.yml | 24 ++++++++++++------- .../workflows/release_documentdb_images.yml | 9 ++++--- .github/workflows/watch_documentdb_images.yml | 7 ++++-- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build_documentdb_images.yml b/.github/workflows/build_documentdb_images.yml index 799304343..043a65467 100644 --- a/.github/workflows/build_documentdb_images.yml +++ b/.github/workflows/build_documentdb_images.yml @@ -188,8 +188,11 @@ jobs: DOCKER_BUILDKIT: '1' run: | set -euo pipefail + # GHCR repository names must be lowercase; the owner login may contain + # uppercase characters (e.g. on forks), so normalize it. + REPO=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') TAG=${{ env.IMAGE_TAG }}-${{ matrix.arch }} - IMAGE=ghcr.io/${{ github.repository }}/${{ matrix.image.name }}:$TAG + IMAGE=ghcr.io/$REPO/${{ matrix.image.name }}:$TAG BUILD_ARGS="" @@ -226,31 +229,34 @@ jobs: - name: Login to GHCR run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + - name: Compute lowercase repository + run: echo "IMAGE_REPO=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV" + - name: Create and Push Manifest run: | - docker manifest create ghcr.io/${{ github.repository }}/${{ matrix.image }}:${{ env.IMAGE_TAG }} \ - --amend ghcr.io/${{ github.repository }}/${{ matrix.image }}:${{ env.IMAGE_TAG }}-amd64 \ - --amend ghcr.io/${{ github.repository }}/${{ matrix.image }}:${{ env.IMAGE_TAG }}-arm64 - docker manifest push ghcr.io/${{ github.repository }}/${{ matrix.image }}:${{ env.IMAGE_TAG }} + docker manifest create ghcr.io/${{ env.IMAGE_REPO }}/${{ matrix.image }}:${{ env.IMAGE_TAG }} \ + --amend ghcr.io/${{ env.IMAGE_REPO }}/${{ matrix.image }}:${{ env.IMAGE_TAG }}-amd64 \ + --amend ghcr.io/${{ env.IMAGE_REPO }}/${{ matrix.image }}:${{ env.IMAGE_TAG }}-arm64 + docker manifest push ghcr.io/${{ env.IMAGE_REPO }}/${{ matrix.image }}:${{ env.IMAGE_TAG }} - name: Install cosign uses: sigstore/cosign-installer@v3.8.2 - name: Sign manifest (keyless) run: | - DIGEST=$(docker buildx imagetools inspect ghcr.io/${{ github.repository }}/${{ matrix.image }}:${{ env.IMAGE_TAG }} \ + DIGEST=$(docker buildx imagetools inspect ghcr.io/${{ env.IMAGE_REPO }}/${{ matrix.image }}:${{ env.IMAGE_TAG }} \ | awk '/^Digest:/ { print $2 }') echo "Signing manifest-list@${DIGEST}" - cosign sign ghcr.io/${{ github.repository }}/${{ matrix.image }}@${DIGEST} -y + cosign sign ghcr.io/${{ env.IMAGE_REPO }}/${{ matrix.image }}@${DIGEST} -y - name: Verify manifest signature (keyless) run: | - DIGEST=$(docker buildx imagetools inspect ghcr.io/${{ github.repository }}/${{ matrix.image }}:${{ env.IMAGE_TAG }} \ + DIGEST=$(docker buildx imagetools inspect ghcr.io/${{ env.IMAGE_REPO }}/${{ matrix.image }}:${{ env.IMAGE_TAG }} \ | awk '/^Digest:/ { print $2 }') cosign verify \ --certificate-identity-regexp "^https://github\.com/${{ github.repository }}/\.github/workflows/build_documentdb_images\.yml@" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ - ghcr.io/${{ github.repository }}/${{ matrix.image }}@${DIGEST} + ghcr.io/${{ env.IMAGE_REPO }}/${{ matrix.image }}@${DIGEST} # --------------------------------------------------------------------------- # Summary diff --git a/.github/workflows/release_documentdb_images.yml b/.github/workflows/release_documentdb_images.yml index 91c717dcb..4c45c6e5b 100644 --- a/.github/workflows/release_documentdb_images.yml +++ b/.github/workflows/release_documentdb_images.yml @@ -57,11 +57,14 @@ jobs: - name: Login to GHCR run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + - name: Compute lowercase repository + run: echo "IMAGE_REPO=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_ENV" + - name: Verify candidate exists run: | echo "Verifying ${{ matrix.image }}:${{ inputs.candidate_version }} exists..." docker buildx imagetools inspect \ - ghcr.io/${{ github.repository }}/${{ matrix.image }}:${{ inputs.candidate_version }} + ghcr.io/${{ env.IMAGE_REPO }}/${{ matrix.image }}:${{ inputs.candidate_version }} - name: Retag existing manifest env: @@ -70,8 +73,8 @@ jobs: run: | echo "Promoting ${{ matrix.image }} from $SOURCE_TAG to $TARGET_TAG" docker buildx imagetools create \ - -t ghcr.io/${{ github.repository }}/${{ matrix.image }}:${{ env.TARGET_TAG }} \ - ghcr.io/${{ github.repository }}/${{ matrix.image }}:${{ env.SOURCE_TAG }} + -t ghcr.io/${{ env.IMAGE_REPO }}/${{ matrix.image }}:${{ env.TARGET_TAG }} \ + ghcr.io/${{ env.IMAGE_REPO }}/${{ matrix.image }}:${{ env.SOURCE_TAG }} # --------------------------------------------------------------------------- # Update default versions in code and create PR diff --git a/.github/workflows/watch_documentdb_images.yml b/.github/workflows/watch_documentdb_images.yml index bf19f018a..2667500e9 100644 --- a/.github/workflows/watch_documentdb_images.yml +++ b/.github/workflows/watch_documentdb_images.yml @@ -140,12 +140,15 @@ jobs: # release images exist for this version. If just one is present (a # partial/aborted promotion), fall through and rebuild to converge. echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + # GHCR repository names must be lowercase; the owner login may contain + # uppercase characters (e.g. on forks), so normalize it. + REPO=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]') DOCDB_EXISTS=false GW_EXISTS=false - if docker manifest inspect "ghcr.io/${{ github.repository }}/documentdb:${NEW}" >/dev/null 2>&1; then + if docker manifest inspect "ghcr.io/${REPO}/documentdb:${NEW}" >/dev/null 2>&1; then DOCDB_EXISTS=true fi - if docker manifest inspect "ghcr.io/${{ github.repository }}/gateway:${NEW}" >/dev/null 2>&1; then + if docker manifest inspect "ghcr.io/${REPO}/gateway:${NEW}" >/dev/null 2>&1; then GW_EXISTS=true fi if [[ "$DOCDB_EXISTS" == "true" && "$GW_EXISTS" == "true" ]]; then From b8686608e58904cc214c825a01d2ec7ab7915b97 Mon Sep 17 00:00:00 2001 From: Ritvik Jayaswal Date: Tue, 30 Jun 2026 14:35:24 -0400 Subject: [PATCH 7/8] fix(ci): guard optional ARGs under set -u in Dockerfile_extension The extension install RUN uses 'set -eux' (set -u). Docker does not inject an ARG into the RUN environment when it has no value, so the APT path (which passes only DOCUMENTDB_APT_VERSION) failed with 'DOCUMENTDB_DEB_PACKAGE: parameter not set'. Use \ default expansion for both optional ARGs so an unset value is treated as empty. Co-authored-by: Copilot Signed-off-by: Ritvik Jayaswal --- .github/dockerfiles/Dockerfile_extension | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/dockerfiles/Dockerfile_extension b/.github/dockerfiles/Dockerfile_extension index ab3da7e16..d60f101a6 100644 --- a/.github/dockerfiles/Dockerfile_extension +++ b/.github/dockerfiles/Dockerfile_extension @@ -66,9 +66,11 @@ RUN --mount=type=bind,target=/ctx set -eux && \ apt-get update && \ # Install the DocumentDB extension: a locally-built .deb (CI build mode) # takes precedence, then a pinned APT version, otherwise latest stable. - if [ -n "${DOCUMENTDB_DEB_PACKAGE}" ]; then \ + # Both ARGs are optional; ${VAR:-} keeps them safe under `set -u` when + # Docker does not inject an unset ARG into the RUN environment. + if [ -n "${DOCUMENTDB_DEB_PACKAGE:-}" ]; then \ apt-get install -y --no-install-recommends "/ctx/${DOCUMENTDB_DEB_PACKAGE}"; \ - elif [ -n "${DOCUMENTDB_APT_VERSION}" ]; then \ + elif [ -n "${DOCUMENTDB_APT_VERSION:-}" ]; then \ apt-get install -y --no-install-recommends \ "postgresql-${PG_MAJOR}-documentdb=${DOCUMENTDB_APT_VERSION}"; \ else \ From 5f7c17fff9c7f672b74e4759d3e1dff2b832d654 Mon Sep 17 00:00:00 2001 From: Ritvik Jayaswal Date: Tue, 30 Jun 2026 14:45:01 -0400 Subject: [PATCH 8/8] fix(ci): stop editing workflow files in version-bump PR The built-in GITHUB_TOKEN cannot push changes under .github/workflows/** (GitHub blocks it without the 'workflow' scope, which that token cannot be granted), so create-pull-request failed with 'refusing to allow a GitHub App to create or update workflow ... without workflows permission'. Drop the sed edits that rewrote DEFAULT_DOCUMENTDB_VERSION and dispatch 'default:' values in build_documentdb_images.yml and release_documentdb_images.yml. Those are only manual-dispatch fallbacks; the watch workflow resolves the real version dynamically from upstream releases, so leaving them static is harmless and keeps the automation self-contained (no PAT/App token required). Update the PR body to match. Co-authored-by: Copilot Signed-off-by: Ritvik Jayaswal --- .../workflows/release_documentdb_images.yml | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release_documentdb_images.yml b/.github/workflows/release_documentdb_images.yml index 4c45c6e5b..3ffc81e46 100644 --- a/.github/workflows/release_documentdb_images.yml +++ b/.github/workflows/release_documentdb_images.yml @@ -140,17 +140,16 @@ jobs: sed -i "s|:${OLD_VERSION}\"|:${NEW_VERSION}\"|g" \ operator/cnpg-plugins/sidecar-injector/internal/config/config_test.go - # 6. Update build workflow defaults - sed -i "s|DEFAULT_DOCUMENTDB_VERSION: '${OLD_VERSION}'|DEFAULT_DOCUMENTDB_VERSION: '${NEW_VERSION}'|" \ - .github/workflows/build_documentdb_images.yml - sed -i "s|default: '${OLD_VERSION}'|default: '${NEW_VERSION}'|g" \ - .github/workflows/build_documentdb_images.yml - - # 7. Update release workflow default version - sed -i "s|default: '${OLD_VERSION}'|default: '${NEW_VERSION}'|g" \ - .github/workflows/release_documentdb_images.yml - - # 8. Update gateway Dockerfile default source image ARG + # NOTE: Workflow files (build_documentdb_images.yml, + # release_documentdb_images.yml) are intentionally NOT edited here. + # The built-in GITHUB_TOKEN cannot push changes under + # .github/workflows/** (GitHub blocks it without the 'workflow' + # scope, which that token cannot be granted). Their version values + # are only manual-dispatch fallbacks; the watch workflow resolves the + # real version dynamically from upstream releases, so leaving them + # static is harmless. + + # 6. Update gateway Dockerfile default source image ARG sed -i "s|pg17-${OLD_VERSION}|pg17-${NEW_VERSION}|" \ .github/dockerfiles/Dockerfile_gateway_public_image @@ -175,9 +174,13 @@ jobs: - Updated `DEFAULT_DOCUMENTDB_IMAGE` and `DEFAULT_GATEWAY_IMAGE` in `constants.go` - Updated sidecar plugin default gateway image in `config.go` and `config_test.go` - Updated `documentDbVersion` in Helm chart `values.yaml` - - Updated build/release workflow defaults in `build_documentdb_images.yml` and `release_documentdb_images.yml` - Updated gateway Dockerfile default source image in `Dockerfile_gateway_public_image` + > Workflow default-version fallbacks are intentionally left + > unchanged: the built-in `GITHUB_TOKEN` cannot modify files under + > `.github/workflows/**`, and those values are only manual-dispatch + > fallbacks (the real version is resolved dynamically). + ### Image References - `ghcr.io/${{ github.repository }}/documentdb:${{ inputs.version }}` - `ghcr.io/${{ github.repository }}/gateway:${{ inputs.version }}`