Skip to content

fix: wake the authorization server before deploying the mcp demo #15

fix: wake the authorization server before deploying the mcp demo

fix: wake the authorization server before deploying the mcp demo #15

Workflow file for this run

# Builds every demo image with jib and deploys the public ones to Cloud Run.
name: Demos
on:
push:
branches: [master]
paths-ignore: ['**.md']
workflow_dispatch:
# One deploy at a time per branch; a superseded build is not worth finishing.
concurrency:
group: demos-${{ github.ref }}
cancel-in-progress: true
env:
MAVEN_ARGS: -B --no-transfer-progress -Dmaven.artifact.threads=16
MAVEN_OPTS: -Xmx3g
REGISTRY: docker.io/springdocdemos
REGION: europe-west1
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 40
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 1
- uses: actions/setup-java@v5
with:
java-version: '17'
distribution: temurin
cache: maven
# jib picks the credentials up from the docker config this action writes.
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# The extra sha tag is what the deploy job pulls, so a redeploy is immutable.
- name: Build and push the images
run: mvn $MAVEN_ARGS -Pjib package jib:build -T1C -Djib.to.tags=${{ github.sha }}
deploy:
needs: build
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
id-token: write
strategy:
fail-fast: false
matrix:
include:
- service: demo-spring-boot-webmvc
# A demo with a "definition" needs more than one container, which only a whole
# service definition can express; the rest deploy from flags. See .github/cloudrun.
- service: demo-spring-boot-webflux
definition: mongo-sidecar.yaml
- service: demo-spring-boot-webmvc-scalar
- service: demo-spring-boot-webflux-scalar
definition: mongo-sidecar.yaml
- service: demo-spring-boot-webflux-functional
- service: demo-spring-hateoas
- service: spring-cloud-function-webmvc
- service: spring-cloud-function-webflux
- service: demo-microservices
definition: microservices.yaml
# The mcp demo validates tokens against its companion authorization server,
# which used to sit on localhost:9000 next to it in docker compose. It resolves
# the issuer while building its filter chain, so the server has to be awake
# before the new revision starts or the demo fails to boot at all.
- service: demo-spring-boot-mcp
env: OAUTH2_ISSUER_URI=https://demos.springdoc.org/demo-spring-boot-mcp-authorization-server
warmup: demo-spring-boot-mcp-authorization-server/.well-known/oauth-authorization-server
- service: demo-spring-boot-mcp-authorization-server
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 1
# Keyless auth through workload identity federation; no service account key.
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
# Cloud Run fails to import a multi-platform manifest list, and jib publishes
# amd64 plus arm64. Pick the amd64 child digest rather than drop arm64 support.
# The multi-container demos do the same thing inside render-service.sh.
- name: Resolve the amd64 digest
id: image
if: ${{ !matrix.definition }}
run: |
repo=springdocdemos/${{ matrix.service }}
token=$(curl -fsSL "https://auth.docker.io/token?service=registry.docker.io&scope=repository:$repo:pull" | jq -r .token)
digest=$(curl -fsSL -H "Authorization: Bearer $token" \
-H 'Accept: application/vnd.docker.distribution.manifest.list.v2+json' \
"https://registry-1.docker.io/v2/$repo/manifests/${{ github.sha }}" \
| jq -r '.manifests[] | select(.platform.architecture == "amd64" and .platform.os == "linux") | .digest')
test -n "$digest"
echo "ref=${{ env.REGISTRY }}/${{ matrix.service }}@$digest" >> "$GITHUB_OUTPUT"
# A demo that talks to another one at boot cannot wait for it to cold start, and
# here the dependency is being redeployed by its own job at the same time. Ask for
# it through the public host, which is the address the demo itself will use, until
# it answers. No credentials: the Cloudflare worker holds the only ones.
- name: Wake the dependency
if: ${{ matrix.warmup }}
run: |
url=https://demos.springdoc.org/${{ matrix.warmup }}
for _ in $(seq 1 40); do
code=$(curl -sS -o /dev/null -w '%{http_code}' -m 30 "$url" || true)
echo "$url -> ${code:-no answer}"
[ "$code" = 200 ] && exit 0
sleep 5
done
echo "::error::$url never answered; the demo would fail to boot against it"
exit 1
# SERVER_PORT overrides the port each demo hardcodes in application.yml.
# no-allow-unauthenticated keeps the run.app URL closed: only the Cloudflare
# worker holds a service account with run.invoker, so there is no way around it.
- uses: google-github-actions/deploy-cloudrun@v2
if: ${{ !matrix.definition }}
with:
service: ${{ matrix.service }}
image: ${{ steps.image.outputs.ref }}
region: ${{ env.REGION }}
env_vars: |
SERVER_PORT=8080
${{ matrix.env }}
# Startup CPU boost cuts the Spring Boot cold start roughly in half, which
# matters when every demo scales to zero between visits.
flags: >-
--no-allow-unauthenticated --port=8080 --cpu=1 --memory=512Mi --cpu-boost
--min-instances=0 --max-instances=2 --concurrency=80 --timeout=60s
--service-account=cloudrun-runtime@springdoc.iam.gserviceaccount.com
# Sidecars cannot be expressed as deploy flags, so the demos that need one go
# through the service definition instead. Replacing a service carries no IAM,
# so they stay closed to anyone but the Cloudflare worker.
- uses: google-github-actions/setup-gcloud@v2
if: ${{ matrix.definition }}
- name: Deploy from a service definition
if: ${{ matrix.definition }}
run: |
.github/scripts/cloudrun/render-service.sh \
".github/cloudrun/${{ matrix.definition }}" '${{ matrix.service }}' '${{ github.sha }}' > /tmp/service.yaml
gcloud run services replace /tmp/service.yaml --region="$REGION" --quiet