Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
19 changes: 17 additions & 2 deletions .github/workflows/build_documentdb_images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}

Expand Down
16 changes: 16 additions & 0 deletions .github/workflows/release_documentdb_images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
166 changes: 166 additions & 0 deletions .github/workflows/watch_documentdb_images.yml
Original file line number Diff line number Diff line change
@@ -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:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor — no concurrency: group on the watch workflow.

The daily schedule cron, a repository_dispatch, and a manual workflow_dispatch can overlap for the same upstream version, launching parallel build→promote→PR chains. The detect "images already exist" guard narrows but doesn't close the window — two runs can both pass it before either finishes pushing, racing the promote step and the auto/documentdb-<version> PR branch.

Fix: add a single-flight group, e.g.

concurrency:
  group: documentdb-watch-${{ github.event.client_payload.version || github.event.inputs.version || 'auto' }}
  cancel-in-progress: false

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of cron I was hoping for a web-hhok somethign we are also planning for COPR

@Ritvik-Jayaswal Ritvik-Jayaswal Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ec9dbe8. Added a single-flight concurrency group keyed on the resolved upstream version, with cancel-in-progress: false so a half-finished promotion is allowed to converge rather than being killed mid-flight:

concurrency:
  group: documentdb-watch-${{ github.event.client_payload.version || github.event.inputs.version || 'auto' }}
  cancel-in-progress: false

This closes the window you described — overlapping cron / repository_dispatch / workflow_dispatch runs for the same version now serialize instead of racing the promote step and the auto/documentdb-<version> PR branch.

@Ritvik-Jayaswal Ritvik-Jayaswal Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of cron I was hoping for a web-hhok somethign we are also planning for COPR

Agreed, and that's already the primary path here: the workflow's main trigger is a repository_dispatch (documentdb-release) that upstream documentdb/documentdb fires on release: published — a webhook-style push, not polling. A reference sender workflow is drafted in docs/designs/upstream-release-dispatch-sender.md. The daily schedule cron is kept only as a low-frequency safety-net for a missed/dropped dispatch, and it's cheap (it exits early at detect when there's nothing newer). Happy to align the dispatch event name / payload shape with whatever you land on for COPR so both consumers share one sender contract.

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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 35 additions & 0 deletions docs/designs/image-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: <image_tag from build>
├── version: <new_version>
└── update_defaults: true → opens the version-bump PR
```

Idempotency: once the release images are promoted, the `documentdb:<version>`
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
Expand Down