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
111 changes: 0 additions & 111 deletions .github/workflows/build-wheel.yml

This file was deleted.

198 changes: 81 additions & 117 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -1,134 +1,98 @@
name: Build and Push Docker Image

# Description:
# Triggered automatically after the PyPI Release workflow succeeds,
# or manually via workflow_dispatch.
# Determines whether the release is stable (vX.Y.Z) or a pre-release.
# For stable releases only, triggers a Jenkins job to publish the Docker image
# to the public registry with the 'latest' tag applied.

on:
workflow_run:
workflows: ["PyPI Release"]
types: [completed]
workflow_dispatch:
inputs:
version:
type: string
required: true
description: version tag name
push:
tags: ["v*"]
pull_request:
branches: [main]

env:
REGISTRY: docker.io
IMAGE_NAME: couchbase/mcp-server

jobs:
publish-docker-image:
name: Publish Docker Image (public) via Jenkins
docker:
runs-on: ubuntu-latest
timeout-minutes: 60
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}

steps:
- name: Download version artifact
if: github.event_name == 'workflow_run'
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: release-version
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
- name: Checkout
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

- name: Read version from artifact
if: github.event_name == 'workflow_run'
id: artifact-version
run: echo "version=$(cat version.txt)" >> "$GITHUB_OUTPUT"
- name: Set up QEMU
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0

- name: Log in to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Check if stable release
id: check-stable
if: startsWith(github.ref, 'refs/tags/')
run: |
VERSION="${{ github.event.inputs.version || steps.artifact-version.outputs.version || github.event.workflow_run.head_branch }}"
echo "Checking version: $VERSION"
if [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
TAG_NAME="${GITHUB_REF#refs/tags/}"
echo "Checking tag: $TAG_NAME"

# Only match vX.Y.Z format (no suffixes like rc, alpha, beta)
if [[ "$TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "is_stable=true" >> "$GITHUB_OUTPUT"
echo "This is a STABLE release: $VERSION"
echo "This is a STABLE release: $TAG_NAME"
else
echo "is_stable=false" >> "$GITHUB_OUTPUT"
echo "This is a PRE-RELEASE: $VERSION (will not update 'latest' or major.minor tags)"
echo "This is a PRE-RELEASE: $TAG_NAME (will not update 'latest' or major.minor tags)"
fi

- name: Trigger Jenkins Job (public registry)
run: |
set -eo pipefail
queued_job_url=$(curl -X POST \
"$JENKINS_BASE_URL/job/$JOB_NAME/buildWithParameters" \
-d "PRODUCT=${PRODUCT}" \
-d "VERSION=${VERSION}" \
-d "REGISTRY=${REGISTRY}" \
-d "TAG_LATEST=${TAG_LATEST}" \
--user "$JENKINS_USER:$JENKINS_API_TOKEN" \
--fail -s -D - -o /dev/null | grep -i "location:" | cut -d' ' -f2 | tr -d '\r')

echo "Queued job URL: ${queued_job_url}"

if [[ "$queued_job_url" =~ ^http: ]]; then
queued_job_url="https:${queued_job_url:5}"
fi
echo "Queued job URL after conversion: ${queued_job_url}"

echo "Waiting for build to start..."
build_url=""
for (( count=0; count<60; count++ )); do
response=$(curl -s --user "${JENKINS_USER}:${JENKINS_API_TOKEN}" \
"${queued_job_url}/api/json")

build_url=$(echo "$response" | jq -r '.executable.url // empty')
if [ -n "${build_url}" ]; then
echo "Build URL: ${build_url}"
break
fi

stuck=$(echo "$response" | jq -r '.stuck // false')
why=$(echo "$response" | jq -r '.why // empty')
echo "Build not started yet (stuck=${stuck}): ${why}"

if [[ "${stuck}" == "true" ]]; then
echo "Jenkins reports the queued item is stuck; giving up."
echo "Check job status at: ${JENKINS_BASE_URL}/job/${JOB_NAME}"
exit 1
fi

sleep 10
done

if [ -z "${build_url}" ]; then
echo "Unable to determine URL for the jenkins run."
echo "Check job status at: ${JENKINS_BASE_URL}/job/${JOB_NAME}"
exit 1
fi

echo "Build started. Check progress at: ${build_url}"

echo "Polling for build completion..."
while true; do
response=$(curl -s --user "${JENKINS_USER}:${JENKINS_API_TOKEN}" \
"${build_url}/api/json")
building=$(echo "$response" | jq -r .building)

if [[ "$building" == "false" ]]; then
result=$(echo "$response" | jq -r .result)
echo "Build finished with result: ${result}"

if [[ "$result" != "SUCCESS" ]]; then
echo "Jenkins build did not succeed. Check details at: ${build_url}"
exit 1
fi
break
fi

echo "Still building... checking again in 30 seconds."
sleep 30
done
env:
JENKINS_BASE_URL: "https://server.jenkins.couchbase.com"
JENKINS_USER: ${{ secrets.SERVER_JENKINS_USERNAME }}
JENKINS_API_TOKEN: ${{ secrets.SERVER_JENKINS_PAT }}
PRODUCT: "mcp-server-couchbase"
VERSION: ${{ github.event.inputs.version || steps.artifact-version.outputs.version || github.event.workflow_run.head_branch }}
REGISTRY: "public"
TAG_LATEST: ${{ steps.check-stable.outputs.is_stable }}
JOB_NAME: "docker-publish"
- name: Extract metadata
id: meta
uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
flavor: |
# Disable automatic 'latest' tag to have explicit control
latest=false
tags: |
# For PRs: tag as pr-<number>
type=ref,event=pr
# For all tags: extract full version without 'v' prefix (e.g., 0.5.2 or 0.5.2rc3)
type=match,pattern=v(.*),group=1
# For stable releases only: extract major.minor (e.g., 0.5 from v0.5.2)
type=match,pattern=v(\d+\.\d+),group=1,enable=${{ steps.check-stable.outputs.is_stable == 'true' }}
# For stable releases only: tag as 'latest'
type=raw,value=latest,enable=${{ steps.check-stable.outputs.is_stable == 'true' }}

- name: Set build timestamp
id: timestamp
run: echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> "$GITHUB_OUTPUT"

- name: Build and push Docker image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
GIT_COMMIT_HASH=${{ github.sha }}
BUILD_DATE=${{ steps.timestamp.outputs.BUILD_DATE }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Update Docker Hub description
if: github.ref_type == 'tag'
# Do not block a release on a Hub web-API hiccup or a
# transient PAT/permission issue. The image push has already succeeded
# by this point.
continue-on-error: true
uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5.0.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: ${{ env.IMAGE_NAME }}
readme-filepath: ./DOCKER.md
Loading