fix: wake the authorization server before deploying the mcp demo #15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 |