diff --git a/.circleci/config.yml b/.circleci/config.yml index 790cd6c7010e..80dc1c8cbc56 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2592,14 +2592,14 @@ jobs: bundle exec rspec no_output_timeout: 30m # Install Node.js directly from nodejs.org with SHA256 verification, - # instead of piping NodeSource's setup_18.x apt-repo installer into + # instead of piping NodeSource's setup_24.x apt-repo installer into # sudo bash (which runs a mutable upstream script unattended). - run: - name: Install Node.js 18.20.8 + name: Install Node.js 24.19.0 command: | - NODE_VERSION="18.20.8" + NODE_VERSION="24.19.0" NODE_TARBALL="node-v${NODE_VERSION}-linux-x64.tar.xz" - NODE_EXPECTED_SHA="5467ee62d6af1411d46b6a10e3fb5cacc92734dbcef465fea14e7b90993001c9" + NODE_EXPECTED_SHA="14b342e71204f811bde6153be8e04b62aef63c236fef92b55f9c83154b409647" curl -sSLf -o "/tmp/${NODE_TARBALL}" "https://nodejs.org/dist/v${NODE_VERSION}/${NODE_TARBALL}" echo "${NODE_EXPECTED_SHA} /tmp/${NODE_TARBALL}" | sha256sum -c - sudo tar -xJf "/tmp/${NODE_TARBALL}" -C /usr/local --strip-components=1 @@ -2734,7 +2734,7 @@ jobs: ui_build: docker: - - image: cimg/node:20.19@sha256:35e64883e8d21bc345b0a7b04c35ee46442c127607ed1d8d7d37d8a1ed76db81 + - image: cimg/node:24.19@sha256:8966565f07189a67d64d6808a2b127f31dafae566508e3547f55640e1070bfad auth: username: ${DOCKERHUB_USERNAME} password: ${DOCKERHUB_PASSWORD} @@ -2778,7 +2778,7 @@ jobs: ui_unit_tests: docker: - - image: cimg/node:20.19@sha256:35e64883e8d21bc345b0a7b04c35ee46442c127607ed1d8d7d37d8a1ed76db81 + - image: cimg/node:24.19@sha256:8966565f07189a67d64d6808a2b127f31dafae566508e3547f55640e1070bfad auth: username: ${DOCKERHUB_USERNAME} password: ${DOCKERHUB_PASSWORD} diff --git a/.github/workflows/check-ui-api-types.yml b/.github/workflows/check-ui-api-types.yml index 9c24bad00f1a..597daebd7205 100644 --- a/.github/workflows/check-ui-api-types.yml +++ b/.github/workflows/check-ui-api-types.yml @@ -56,7 +56,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: - node-version: "20" + node-version-file: ui/litellm-dashboard/.nvmrc cache: "npm" cache-dependency-path: ui/litellm-dashboard/package-lock.json diff --git a/.github/workflows/test-litellm-ui-build.yml b/.github/workflows/test-litellm-ui-build.yml index 525e2c5b949a..39f4bc1428a4 100644 --- a/.github/workflows/test-litellm-ui-build.yml +++ b/.github/workflows/test-litellm-ui-build.yml @@ -27,7 +27,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: - node-version: "20" + node-version-file: ui/litellm-dashboard/.nvmrc cache: "npm" cache-dependency-path: ui/litellm-dashboard/package-lock.json diff --git a/.github/workflows/test-litellm-ui-lint.yml b/.github/workflows/test-litellm-ui-lint.yml index 5173eb6da35c..2c31abc66097 100644 --- a/.github/workflows/test-litellm-ui-lint.yml +++ b/.github/workflows/test-litellm-ui-lint.yml @@ -61,7 +61,7 @@ jobs: if: steps.changed.outputs.has_files == 'true' uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: - node-version: "20" + node-version-file: ui/litellm-dashboard/.nvmrc cache: "npm" cache-dependency-path: ui/litellm-dashboard/package-lock.json diff --git a/.github/workflows/test-litellm-ui-unit.yml b/.github/workflows/test-litellm-ui-unit.yml index 5374a0059de8..20c3611eb384 100644 --- a/.github/workflows/test-litellm-ui-unit.yml +++ b/.github/workflows/test-litellm-ui-unit.yml @@ -35,7 +35,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: - node-version: "20" + node-version-file: ui/litellm-dashboard/.nvmrc cache: "npm" cache-dependency-path: ui/litellm-dashboard/package-lock.json diff --git a/Dockerfile b/Dockerfile index a127cdabd599..1fb34f6ebf96 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17 ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a # Pinned by digest like the other base images; bump explicitly on Node upgrades. -ARG UI_BUILD_IMAGE=node:20.18-alpine3.20@sha256:3488b10bf958af7125a176419d2d8a9937d895bf124012aae811651988d2ffe6 +ARG UI_BUILD_IMAGE=node:24.19-alpine3.24@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43 FROM $UV_IMAGE AS uvbin diff --git a/docker/Dockerfile.database b/docker/Dockerfile.database index 9ee076ce8256..c93a08409a2b 100644 --- a/docker/Dockerfile.database +++ b/docker/Dockerfile.database @@ -7,7 +7,7 @@ ARG LITELLM_BUILD_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17 ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b17a5ee8bc23b532600a44d705acef2409e0933c1251b45f ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a # Pinned by digest like the other base images; bump explicitly on Node upgrades. -ARG UI_BUILD_IMAGE=node:20.18-alpine3.20@sha256:3488b10bf958af7125a176419d2d8a9937d895bf124012aae811651988d2ffe6 +ARG UI_BUILD_IMAGE=node:24.19-alpine3.24@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43 FROM $UV_IMAGE AS uvbin diff --git a/docker/Dockerfile.non_root b/docker/Dockerfile.non_root index 946b4de6f5e6..1545a84d379c 100644 --- a/docker/Dockerfile.non_root +++ b/docker/Dockerfile.non_root @@ -6,7 +6,7 @@ ARG LITELLM_RUNTIME_IMAGE=cgr.dev/chainguard/wolfi-base@sha256:42df77a9974d6ec8b ARG PROXY_EXTRAS_SOURCE=published ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.11.7@sha256:240fb85ab0f263ef12f492d8476aa3a2e4e1e333f7d67fbdd923d00a506a516a # Pinned by digest like the other base images; bump explicitly on Node upgrades. -ARG UI_BUILD_IMAGE=node:20.18-alpine3.20@sha256:3488b10bf958af7125a176419d2d8a9937d895bf124012aae811651988d2ffe6 +ARG UI_BUILD_IMAGE=node:24.19-alpine3.24@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43 FROM $UV_IMAGE AS uvbin diff --git a/docker/build_admin_ui.sh b/docker/build_admin_ui.sh index 68acdd78e3e4..f84aba7c0537 100755 --- a/docker/build_admin_ui.sh +++ b/docker/build_admin_ui.sh @@ -54,8 +54,9 @@ else fi || { echo "nvm checksum verification failed"; exit 1; } bash "$NVM_SCRIPT" source ~/.nvm/nvm.sh -nvm install v18.17.0 -nvm use v18.17.0 +NODE_VERSION="$(cat ui/litellm-dashboard/.nvmrc)" +nvm install "v${NODE_VERSION}" +nvm use "v${NODE_VERSION}" # cd in to /ui/litellm-dashboard diff --git a/ui/Dockerfile b/ui/Dockerfile index b75c4d0a0c63..0d184b74493c 100644 --- a/ui/Dockerfile +++ b/ui/Dockerfile @@ -2,11 +2,11 @@ # UI container — Next.js static export served by nginx. -ARG NODE_VERSION=20.18-alpine3.20 +ARG UI_BUILD_IMAGE=node:24.19-alpine3.24@sha256:d32cdf619f63fe0471182d08996dd516c6275bb5fd31ae06e55a570bd9e1ad43 ARG NGINX_VERSION=1.27-alpine # ---------- builder ---------- -FROM node:${NODE_VERSION} AS builder +FROM ${UI_BUILD_IMAGE} AS builder ENV NEXT_TELEMETRY_DISABLED=1 \ npm_config_fund=false \ diff --git a/ui/litellm-dashboard/.npmrc b/ui/litellm-dashboard/.npmrc index 7999681cc351..8865c7b3c2eb 100644 --- a/ui/litellm-dashboard/.npmrc +++ b/ui/litellm-dashboard/.npmrc @@ -3,3 +3,4 @@ ignore-scripts=true # Protects local npm install only — npm ci (used in CI) ignores this min-release-age=3 +engine-strict=true diff --git a/ui/litellm-dashboard/.nvmrc b/ui/litellm-dashboard/.nvmrc index 209e3ef4b624..60ade1ae01e8 100644 --- a/ui/litellm-dashboard/.nvmrc +++ b/ui/litellm-dashboard/.nvmrc @@ -1 +1 @@ -20 +24.19.0 diff --git a/ui/litellm-dashboard/build_ui.sh b/ui/litellm-dashboard/build_ui.sh index a3ab475dc58d..3cdf4305d617 100755 --- a/ui/litellm-dashboard/build_ui.sh +++ b/ui/litellm-dashboard/build_ui.sh @@ -23,11 +23,12 @@ if ! command -v nvm &> /dev/null; then fi # Use nvm to set the required Node.js version -nvm use v20 +nvm install +nvm use # Check if nvm use was successful if [ $? -ne 0 ]; then - echo "Error: Failed to switch to Node.js v20. Deployment aborted." + echo "Error: Failed to switch to the Node.js version in .nvmrc. Deployment aborted." exit 1 fi diff --git a/ui/litellm-dashboard/build_ui_custom_path.sh b/ui/litellm-dashboard/build_ui_custom_path.sh index a92927f8ea7b..2f9905126d6a 100755 --- a/ui/litellm-dashboard/build_ui_custom_path.sh +++ b/ui/litellm-dashboard/build_ui_custom_path.sh @@ -33,11 +33,12 @@ if ! command -v nvm &> /dev/null; then fi # Use nvm to set the required Node.js version -nvm use v18.17.0 +nvm install +nvm use # Check if nvm use was successful if [ $? -ne 0 ]; then - echo "Error: Failed to switch to Node.js v18.17.0. Deployment aborted." + echo "Error: Failed to switch to the Node.js version in .nvmrc. Deployment aborted." exit 1 fi diff --git a/ui/litellm-dashboard/package-lock.json b/ui/litellm-dashboard/package-lock.json index a1bd63151b4e..1cc35329dd90 100644 --- a/ui/litellm-dashboard/package-lock.json +++ b/ui/litellm-dashboard/package-lock.json @@ -76,8 +76,8 @@ "vitest": "3.2.6" }, "engines": { - "node": ">=20.9.0", - "npm": ">=8.3.0" + "node": ">=24.0.0", + "npm": ">=11.10.0" } }, "node_modules/@acemir/cssom": { @@ -2718,23 +2718,6 @@ "win32" ] }, - "node_modules/@playwright/test": { - "version": "1.58.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.1.tgz", - "integrity": "sha512-6LdVIUERWxQMmUSSQi0I53GgCBYgM2RpGngCPY7hSeju+VrKjq3lvs7HpJoPbDiY5QM5EYRtRX5fvrinnMAz3w==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "playwright": "1.58.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@polka/url": { "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", @@ -7427,6 +7410,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -11009,40 +10993,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/playwright": { - "version": "1.58.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.1.tgz", - "integrity": "sha512-+2uTZHxSCcxjvGc5C891LrS1/NlxglGxzrC4seZiVjcYVQfUa87wBL6rTDqzGjuoWNjnBzRqKmF6zRYGMvQUaQ==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "playwright-core": "1.58.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.58.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.1.tgz", - "integrity": "sha512-bcWzOaTxcW+VOOGBCQgnaKToLJ65d6AqfLVKEWvexyS3AS6rbXl+xdpYRMGSRBClPvyj44njOWoxjNdL/H9UNg==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", diff --git a/ui/litellm-dashboard/package.json b/ui/litellm-dashboard/package.json index 4760b622f9bd..428027f9580e 100644 --- a/ui/litellm-dashboard/package.json +++ b/ui/litellm-dashboard/package.json @@ -102,7 +102,7 @@ "sharp": "^0.35.0" }, "engines": { - "node": ">=20.9.0", - "npm": ">=8.3.0" + "node": ">=24.0.0", + "npm": ">=11.10.0" } } diff --git a/ui/litellm-dashboard/tests/jsdomFetchEnv.ts b/ui/litellm-dashboard/tests/jsdomFetchEnv.ts new file mode 100644 index 000000000000..b3c0239d1085 --- /dev/null +++ b/ui/litellm-dashboard/tests/jsdomFetchEnv.ts @@ -0,0 +1,24 @@ +import { builtinEnvironments, type Environment } from "vitest/environments"; + +const env: Environment = { + name: "jsdom-fetch", + transformMode: "web", + async setup(global, options) { + const nativeAbortController = global.AbortController; + const nativeAbortSignal = global.AbortSignal; + const { teardown } = await builtinEnvironments.jsdom.setup(global, options); + Object.defineProperty(global, "AbortController", { + configurable: true, + writable: true, + value: nativeAbortController, + }); + Object.defineProperty(global, "AbortSignal", { + configurable: true, + writable: true, + value: nativeAbortSignal, + }); + return { teardown }; + }, +}; + +export default env; diff --git a/ui/litellm-dashboard/vitest.config.ts b/ui/litellm-dashboard/vitest.config.ts index da4734eeaf2c..19af41ab8edc 100644 --- a/ui/litellm-dashboard/vitest.config.ts +++ b/ui/litellm-dashboard/vitest.config.ts @@ -15,7 +15,7 @@ const staticImageData: Plugin = { const config: ViteUserConfig = { plugins: [staticImageData], test: { - environment: "jsdom", + environment: "./tests/jsdomFetchEnv.ts", setupFiles: ["tests/setupTests.ts"], globals: true, css: true, // lets you import CSS/modules without extra mocks