Skip to content

build: generate the MCP dashboard assets into the build directory - #3384

Open
gwr0-0 wants to merge 3 commits into
springdoc:mainfrom
gwr0-0:build/untrack-mcp-ui-assets
Open

gwr0-0 wants to merge 3 commits into
springdoc:mainfrom
gwr0-0:build/untrack-mcp-ui-assets

Conversation

@gwr0-0

@gwr0-0 gwr0-0 commented Oct 10, 2026

Copy link
Copy Markdown

springdoc-openapi-starter-common-mcp/src/main/resources/mcp-ui/ is vite build output. It was
committed next to the sources, and it has drifted from them repeatedly. This stops tracking it and
fixes why it drifted.

The tracked copy keeps drifting

commit date state of the committed assets
fa4114d57 2026-03-18 assets present and consistent
7e649be61 2026-04-12 frontend sources changed; index.html was updated to reference index-CscgqDf2.js / index-Cq1GlPUU.css, but those files were not added — the previous pair was deleted instead
— 2026-04-12 → 2026-07-12 mcp-ui/assets absent from the tree entirely (verified at 9cc88cbab, 2026-06-21). The committed index.html pointed at files that do not exist, so a build skipping the frontend produced a dashboard whose JS and CSS both 404
b50ab9c1f 2026-07-12 an unrelated commit ("Add mechanism to disable nullable for Kotlin properties") added the two missing files as a side effect of a local build, restoring consistency with the April sources
354c98cba 2026-09-06 the OAuth2 pre-fill fix changed the sources and again did not regenerate the assets, so the tracked bundle predates a security fix

Rebuilding the sources as of 354c98cba^ reproduces the tracked blob byte for byte:

rebuilt from bytes sha256 of assets/index-*.js
354c98cba^ 651443 93700D36C967EBB7…
the tracked git blob 651443 93700D36C967EBB7…
current sources 651766 760D51C74EF97071…

Why it drifted

The assets were generated into src/main/resources, a source directory. So:

  • mvn clean never removed them — the module's clean plugin only adds src/main/frontend/node_modules
    to the default target/ deletion, and the generated files live outside target/;
  • a build with -Dskip.npm=true therefore packaged whatever happened to be lying there, with no
    signal that it was stale;
  • and after this PR stopped tracking them they would have been ignored as well, which is worse: a
    stale copy would sit in the working tree invisible to git status.

Changes

  1. build: generate the assets into the module output directory
    (target/classes/mcp-ui) instead of the source tree, exclude mcp-ui/** from
    src/main/resources so a leftover copy can never be packaged in place of freshly built assets,
    drop the old copy on mvn clean, and stop tracking the committed files.
    vite.config.ts takes the output directory from MCP_UI_OUT_DIR when the Maven build sets it,
    and keeps writing next to the sources for a standalone npm run build.
  2. docs: correct three outdated statements in CONTRIBUTING.adoc (build.gradle → pom.xml,
    JDK 1.8 → 21, ./mvn install → mvn install), and replace the dashboard note with what the build
    actually does. Fixes Outdated project import and build instructions in CONTRIBUTING.adoc #3375.
  3. build: fail the build when the assets are missing, in two places:
    • an enforcer rule in springdoc-openapi-starter-common-mcp requiring the generated
      mcp-ui/index.html to exist. It is declared in the project rather than in a workflow, so it
      covers every path that builds the artifact — including release:perform, which builds in its
      own target/checkout. A workflow-level path check could not gate that deploy: the release step
      runs after the artifacts have been published (autoPublish is true and waitUntil is
      published), so it would only ever be a detector. The rule is skipped when skip.npm is set,
      so -Dskip.npm=true keeps working.
    • release.yml forces -Dskip.npm=false into the release build via -Darguments. The root
      pom records that a profile change once deactivated the activeByDefault ci profile and silently
      skipped the npm frontend build — exactly the release:perform path that releaseProfiles
      guards. Pinning the property means a published release cannot lose the dashboard without the
      build failing first.

What this does and does not guarantee

  • Published artifacts are unaffected either way and stay guaranteed: release.yml and
    snapshot.yml build the frontend (-Pci, plus the forced -Dskip.npm=false above), and the
    released jar is built from a fresh checkout of the tag.
  • mvn clean ... -Dskip.npm=true now deterministically produces a jar with no dashboard, which
    serves HTTP 404 for the UI while /api/mcp-admin/** is unaffected.
  • mvn ... -Dskip.npm=true without clean still reuses whatever is in target/classes/mcp-ui
    from an earlier build. That is ordinary Maven behaviour for a skipped step, it only affects local
    jars, and CONTRIBUTING.adoc now says so explicitly instead of claiming otherwise.

Verification

check result
mvn clean package (frontend enabled) src/main/resources/mcp-ui deleted by clean and not recreated; assets land in target/classes/mcp-ui; RequireFilesExist passed; jar contains the dashboard whose JS hashes to 760D51C74EF97071…
mvn clean package -Dskip.npm=true Skipping Rule Enforcement. · jar has no mcp-ui entry at all
decoy src/main/resources/mcp-ui/index.html + package -Dskip.npm=true (no clean) jar has no mcp-ui entry — the resource exclusion holds, a source-tree copy is never packaged
assets removed from target/classes + enforcer:enforce@… -Dskip.npm=false RequireFilesExist failed, listing the missing file
full chain clean install BUILD SUCCESS, 5 of 5 modules
running dashboard OAuth2 panel shows empty credential fields with hint placeholders; a non-loopback plaintext endpoint raises the "not HTTPS" warning

Notes

  • Not for backport to spring-boot-3. That line (2.9.2-SNAPSHOT) has no MCP module at all — no
    path in its tree contains mcp — so there is nothing to untrack there. Only the docs: commit
    applies to that line, and it does: that branch's CONTRIBUTING.adoc has the same three stale
    statements.
  • This does not shrink the clone. The 4.85 MB stays in history; the change stops future churn.

gwr0-0 added 3 commits October 11, 2026 00:16
springdoc-openapi-starter-common-mcp/src/main/resources/mcp-ui is vite build
output that was committed alongside the frontend sources. The tracked copy had
drifted: it was last regenerated on 2026-07-12, while the frontend sources last
changed on 2026-09-06 (the OAuth2 credential pre-fill fix), so a build passing
-Dskip.npm=true packaged a dashboard without that fix. Before that, the assets
were missing from the tree for three months while the committed index.html
referenced files that did not exist.

Fix the reason they drifted as well as the symptom. Build output was written
into src/main/resources, a source directory, so mvn clean never removed it and a
build skipping the frontend silently packaged whatever happened to be lying
there. Write the assets into the module output directory instead, exclude
mcp-ui/** from src/main/resources so a stale copy cannot be packaged, and let
mvn clean drop the old copy in the source tree.

Every build that runs the frontend step now produces assets matching the
sources. Published artifacts were already unaffected: the release and snapshot
pipelines pass -Pci precisely so that the frontend is built, and the released
jar is built from a fresh checkout.

Record the behaviour in CONTRIBUTING, including that -Dskip.npm=true reuses the
build directory as-is rather than producing a guaranteed-empty one.
The working-with-the-code guide still described the pre-3.x project layout and
toolchain:

- the IntelliJ import step pointed at a root build.gradle, but the project is a
  Maven build with a root pom.xml, and there is no Gradle build to open;
- the build section claimed JDK 1.8 or above, while the project requires the
  JDK 21 that CI uses;
- the build command was shown as ./mvn install, but there is no mvn wrapper
  script in the repository root, so the command does not run as written.

Fixes springdoc#3375
The dashboard assets are no longer tracked, so a build that skips the frontend
step packages a jar without the UI and with no other symptom: the application
still starts and /api/mcp-admin/** still works, only the dashboard assets 404.

Add an enforcer rule to springdoc-openapi-starter-common-mcp requiring the
generated mcp-ui/index.html to exist. It is declared in the project rather than
in a workflow, so it covers every path that builds the artifact, including
release:perform, which builds in its own target/checkout where a workflow-level
path check could not gate the deploy. The rule is skipped when skip.npm is set,
so -Dskip.npm=true keeps working as the documented way to build without Node.

release.yml additionally forces -Dskip.npm=false into the release build. The
root pom records that a profile change once deactivated the activeByDefault ci
profile and silently skipped the npm frontend build, and that is exactly the
release:perform path; pinning the property means a published release can no
longer lose the dashboard without the build failing first.

This branch has not been deployed

No deployments
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.

Outdated project import and build instructions in CONTRIBUTING.adoc

1 participant