Skip to content

build(ui): pin trailingSlash=true and verify Node v20 in build_ui.sh#29259

Open
oss-agent-shin wants to merge 4 commits into
BerriAI:litellm_oss_agent_shin_daily_branchfrom
oss-agent-shin:fix/lit-2723-pin-ui-build-output-and-node-v20
Open

build(ui): pin trailingSlash=true and verify Node v20 in build_ui.sh#29259
oss-agent-shin wants to merge 4 commits into
BerriAI:litellm_oss_agent_shin_daily_branchfrom
oss-agent-shin:fix/lit-2723-pin-ui-build-output-and-node-v20

Conversation

@oss-agent-shin

@oss-agent-shin oss-agent-shin commented May 29, 2026

Copy link
Copy Markdown
Contributor

Summary

LIT-2723. Two small build-time locks for the admin UI:

  1. ui/litellm-dashboard/next.config.mjs — set trailingSlash: true. The static export then consistently emits <route>/index.html (directory-index). Different Next.js patch versions and stale .next/ caches were flipping the export between foo.html and foo/index.html, producing massive rename-only diffs in litellm/proxy/_experimental/out and breaking deployments that rely on the directory-index form (e.g. nested OAuth callback routes — see fix(admin-ui): emit nested routes as <dir>/index.html so /ui/mcp/oauth/callback works #28106 / chore(admin-ui): regenerate static export with trailingSlash: true #28112).
  2. ui/litellm-dashboard/build_ui.shnvm install v20 first (the bare nvm use v20 silently no-ops when v20 isn't already installed in non-interactive shells, e.g. CI), error-check both nvm steps, then verify node -v actually reports v20.* before running npm run build. Without this guard, committed artifacts can slip in built against Node v22 (which has happened on recent commits per the ticket).

The ticket also mentions docker/Dockerfile.custom_ui — that file no longer exists in this tree. The Dockerfile.non_root path was already updated in #28112, so there's nothing else to touch on the Docker side here.

Note on review diff

The fork's GITHUB_TOKEN lacks repo + workflow scopes, so I pushed via the PUT /repos/{owner}/{repo}/contents/{path} REST API rather than git push. The merge-base diff is exactly the three files below; see "Files changed" for the canonical view.

Evidence

1. trailingSlash: true flips the export from <route>.html to <route>/index.html

Built a minimal Next.js 15 project mirroring the dashboard's nested route structure (/, /login, /settings/admin-settings, /tools/mcp-servers) and ran next build with output: "export" both ways.

Before — output: "export" only (current state of ui/litellm-dashboard/next.config.mjs):

out/404.html
out/index.html
out/login.html
out/settings/admin-settings.html
out/tools/mcp-servers.html

After — output: "export" + trailingSlash: true (this PR):

out/404.html
out/404/index.html
out/index.html
out/login/index.html
out/settings/admin-settings/index.html
out/tools/mcp-servers/index.html

Every nested route now lives at <route>/index.html. The existing committed litellm/proxy/_experimental/out/ on litellm_oss_agent_shin_daily_branch currently has the bare-.html form for nested routes (e.g. organizations.html, settings/admin-settings.html, tools/mcp-servers.html), which is exactly the form the customer fix in #28106 / #28112 needed to move away from. Without trailingSlash: true, the very next local rebuild flips it right back.

2. build_ui.sh node-version guard fires on the wrong Node

Stubbed node to return v22.16.0 (the version that was sneaking into recent committed artifacts per the ticket), then exercised the new guard:

[stub nvm] install v20
[stub nvm] use v20
Error: expected Node.js v20.* but got 'v22.16.0'. Deployment aborted.

Same fragment with a stub returning v20.18.0:

[stub nvm] install v20
[stub nvm] use v20
Confirmed Node.js v20.18.0
(would now run npm build — guard passed)

Same fragment with node missing (the silent-no-op-on-non-interactive-shell scenario):

Error: expected Node.js v20.* but got ''. Deployment aborted.

The previous code in build_ui.sh was nvm use v20 followed by a check of $?. That check fires only when nvm use actually returned non-zero — which it doesn't when nvm isn't loaded in the shell at all (the nvm function simply isn't defined, the nvm use v20 line itself errors with "command not found" but set -e is not in effect, so the script just walks past it and npm run build runs against whatever node is on PATH). The new nvm install v20 + nvm use v20 + node -v triple closes that hole.

Tests

tests/test_litellm/ui/test_ui_build_pinning_lit_2723.py:

tests/test_litellm/ui/test_ui_build_pinning_lit_2723.py::test_build_ui_sh_installs_node_v20_and_verifies PASSED [ 50%]
tests/test_litellm/ui/test_ui_build_pinning_lit_2723.py::test_next_config_pins_trailing_slash_true PASSED [100%]
2 passed in 0.99s

The tests assert on the locking lines — trailingSlash: true in next.config.mjs, and the nvm install v20 / Failed to switch to Node.js v20 / node -v / v20. markers in build_ui.sh — so a later cleanup can't silently regress either piece without breaking the test.

Out of scope

  • docker/Dockerfile.custom_ui was named in the ticket but no longer exists in this tree (Dockerfile.non_root was updated in chore(admin-ui): regenerate static export with trailingSlash: true #28112).
  • docker/build_admin_ui.sh separately uses nvm install v18.17.0 before invoking build_ui.sh. That's the enterprise UI build path; touching it would expand scope. Left as-is.

Linear

LIT-2723.

Verification (ship-pr)

  • Base branch is litellm_oss_agent_shin_daily_branch (required for fork PRs).
  • CI 44/44 green at HEAD f0fa7d1. mergeable_state=clean.
  • Greptile: 5/5 — "Safe to merge — all three files contain targeted, well-scoped changes with no logic regressions introduced." (initial 4/5 with one P1 about the pre-existing npm run build else-branch missing exit 1; addressed in f0fa7d1, re-review bumped to 5/5).
  • Veria AI - PR Review: completed/success (low risk).
  • Runtime evidence captured in the ### Evidence section above (Next.js export layout before/after + build_ui.sh node-version guard fires on v22 / empty / passes on v20).
  • Unit tests pass locally (pytest tests/test_litellm/ui/test_ui_build_pinning_lit_2723.py — 2 passed). Tests are in addition to runtime evidence, not in place of.
  • No customer name in PR title or body.
  • No secrets in PR title or body (token redacted on every push).
  • Diff scope = 3 files (the two locking lines + the regression test). No vendored/generated noise.

@greptile-apps

greptile-apps Bot commented May 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds two build-time locks to prevent committed UI artifacts from drifting: trailingSlash: true in next.config.mjs pins the static-export layout to <route>/index.html, and build_ui.sh replaces the silent nvm use v20 with an install-then-use sequence followed by a hard node -v version assertion. It also adds the exit 1 that was missing from the npm run build failure branch (the previously flagged issue).

  • next.config.mjstrailingSlash: true eliminates the cache/version-driven flip between <route>.html and <route>/index.html that caused large rename-only diffs in _experimental/out.
  • build_ui.shnvm install v20 before nvm use v20, explicit exit checks on both steps, a node -v pattern guard, and exit 1 on build failure all close the hole where a non-interactive shell could silently build against the wrong Node version.
  • tests/test_litellm/ui/test_ui_build_pinning_lit_2723.py — file-level regression tests assert both locking lines are present so future cleanup cannot silently regress either guard.

Confidence Score: 5/5

Safe to merge — all three files contain targeted, well-scoped changes with no logic regressions introduced.

The previously flagged build-failure exit-code issue is now fixed with exit 1 in the else branch. The nvm/Node version guard is correct: nvm install before nvm use, exit checks on both, and a case pattern-match on node -v output. The trailingSlash: true addition is a standard Next.js config option with a clear, well-documented rationale. No new network calls, no auth changes, no schema changes.

No files require special attention.

Important Files Changed

Filename Overview
ui/litellm-dashboard/build_ui.sh Replaces fragile nvm use v20 with nvm install v20 + nvm use v20 + node -v version guard; adds exit 1 to the npm build failure branch (fixing the previously flagged P1).
ui/litellm-dashboard/next.config.mjs Adds trailingSlash: true to pin static export layout to <route>/index.html, preventing cache/version-driven flips between that form and <route>.html.
tests/test_litellm/ui/test_ui_build_pinning_lit_2723.py New regression tests asserting the presence of trailingSlash: true and the nvm/node version guard markers; file-level assertions only, no network calls.

Reviews (2): Last reviewed commit: "fix(build_ui): exit 1 when npm run build..." | Re-trigger Greptile

@oss-agent-shin

Copy link
Copy Markdown
Contributor Author

@greptileai please re-review — addressed the P1 in commit f0fa7d1: the npm run build failure else branch now calls exit 1, matching the error-handling pattern of the new nvm install / nvm use / node -v guards above it.

@codecov

codecov Bot commented May 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

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.

2 participants