Skip to content

Embed OpenAPI spec and Swagger UI in main Wave container#1043

Merged
pditommaso merged 3 commits into
masterfrom
openapi-embed-in-app
May 13, 2026
Merged

Embed OpenAPI spec and Swagger UI in main Wave container#1043
pditommaso merged 3 commits into
masterfrom
openapi-embed-in-app

Conversation

@pditommaso
Copy link
Copy Markdown
Collaborator

@pditommaso pditommaso commented May 13, 2026

Why

Until now the Wave OpenAPI spec was shipped as a separate wave/openapi nginx image, built and pushed by typespec.yml on every [release] commit. That meant two images to deploy, two lifecycles to keep in sync, and a docs endpoint that lived outside the main Wave service.

This PR follows the Sched project pattern: the spec is generated as part of the main Gradle build and served directly from the Wave application container at /openapi/.

What changes

Before / after

Aspect Before After
OpenAPI docs hosting dedicated wave/openapi nginx image bundled in wave/app at /openapi/
Spec generation typespec.yml workflow on [release] Gradle task chain on every build
Swagger UI assets inline CDN scripts in typespec/index.html served from org.webjars:swagger-ui:5.17.14
Release pipeline extra ECR push step none (folded into main image build)

Files

Added

  • src/main/resources/swagger-ui-template.html — Swagger UI page, rendered at build time with ${PROJECT_VERSION}.
  • src/main/resources/public/openapi/swagger-ui-init.js — Swagger UI bootstrap (externalised for CSP).
  • src/main/resources/public/openapi/swagger-ui-overrides.css — minor Swagger UI styling (externalised for CSP).

Modified

  • build.gradle — adds swagger-ui webjar dependency and three new Gradle tasks: installTypeSpecgenerateOpenApigenerateSwaggerUI, wired into processResources.
  • src/main/resources/application.yml — adds static-resource mappings for /openapi/** and /webjars/**.
  • src/main/groovy/io/seqera/wave/controller/ServiceInfoController.groovy — drops the @Get("/openapi") redirect (it caused a 301 loop because Micronaut resolves /openapi and /openapi/ to the same route).
  • .github/workflows/typespec.yml — keeps TypeSpec validation only; drops AWS/ECR config and the docker build/push step.

Deleted

  • typespec/Dockerfile
  • typespec/index.html
  • typespec/tag-and-push-openapi.sh

How it hangs together

./gradlew processResources
└── generateSwaggerUI         # renders index.html from the template
    └── generateOpenApi       # npx tsp compile .  →  typespec/tsp-output/.../openapi.yaml
        └── installTypeSpec   # npm install

The generated openapi.yaml and index.html, together with the static CSS/JS, end up under build/resources/main/public/openapi/ and are bundled into the JIB image. Micronaut serves them via the /openapi/** static-resource mapping; Swagger UI's JS/CSS comes from the swagger-ui webjar at /webjars/swagger-ui/5.17.14/....

Note on CSP

Wave's response filter sets a strict default-src 'self'; style-src 'self' https://fonts.googleapis.com; ... — no 'unsafe-inline'. The Swagger UI template therefore loads its init script and overrides from same-origin URLs rather than inline <script>/<style> blocks, so no CSP relaxation was needed.

Test plan

  • ./gradlew processResources produces build/resources/main/public/openapi/{openapi.yaml,index.html,swagger-ui-init.js,swagger-ui-overrides.css} with the correct version interpolated into index.html.
  • ./run.sh, then visit http://localhost:9090/openapi/ — Swagger UI renders, endpoints list loads, /openapi/openapi.yaml returns the spec.
  • Browser DevTools Console shows no CSP violations on /openapi/.
  • CI: PR build succeeds (no Node setup is added — ubuntu-latest ships with Node, enough for npm install + npx tsp compile .).
  • typespec.yml still validates TypeSpec changes on PRs.

🤖 Generated with Claude Code

Previously the OpenAPI spec was shipped as a separate `wave/openapi`
nginx image, built and pushed independently on every `[release]` commit
via the typespec.yml workflow. Now the spec is compiled at Gradle build
time and bundled into the main Wave app container, served at /openapi/
alongside Swagger UI assets from the swagger-ui webjar. The previously
unused HTML/CSS/JS bits are externalised so the strict Wave CSP
(no `unsafe-inline`) does not break the docs page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pditommaso pditommaso requested a review from munishchouhan May 13, 2026 08:10
pditommaso and others added 2 commits May 13, 2026 10:14
It was failing the repo's SHA-pinning policy and provides little value.
The @get("/openapi") redirect was removed in baa6e27; the
corresponding test still asserted a 301 and was failing CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pditommaso pditommaso merged commit b9c0daf into master May 13, 2026
4 checks passed
@pditommaso pditommaso deleted the openapi-embed-in-app branch May 13, 2026 11:38
pditommaso added a commit that referenced this pull request May 13, 2026
Previously the release steps used `contains(head_commit.message, '[release]')`,
which matched the substring anywhere in the message — subject or body. PR #1043
mentioned `[release]` in its body and triggered a full release/publish/ECR push
on merge.

Add a `detect-release` step that sets `is_release=true` only when the first
line of the head commit message contains `[release]` and is not a squash-merge
subject (ending with `(#NNN)`) or merge-commit subject (`Merge pull request #NNN`).
All 9 release-gated steps now key off that output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant