Skip to content

Upgrade Node.js from 22.17.0 to 24.14.0#232

Open
cteyton wants to merge 37 commits intomainfrom
migration-node-24-14
Open

Upgrade Node.js from 22.17.0 to 24.14.0#232
cteyton wants to merge 37 commits intomainfrom
migration-node-24-14

Conversation

@cteyton
Copy link
Copy Markdown
Contributor

@cteyton cteyton commented Mar 25, 2026

Explanation

Upgrade Node.js runtime from 22.17.0 to 24.14.0 across the entire monorepo: version pins, Docker images, CI workflows, and Dockerfiles.

Type of Change

  • Bug fix
  • New feature
  • Improvement/Enhancement
  • Refactoring
  • Documentation
  • Breaking change

Affected Components

  • Domain packages affected: none (runtime-only change)
  • Frontend / Backend / Both: Both + CI + Docker
  • Breaking changes (if any): Node 24 native TS stripping conflicts with Jest config loading — requires NODE_OPTIONS="--no-experimental-strip-types" for test runs

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing completed
  • Test coverage maintained or improved

Test Details:

Step Result
npm install (npm 11.11.0) Pass
nx build api Pass ([DEP0180] fs.Stats deprecated warning)
nx build frontend Pass ([MODULE_TYPELESS_PACKAGE_JSON] warning)
nx build packmind-cli Pass
nx build mcp-server Pass
Tests (24/25 projects) Pass with --no-experimental-strip-types
Lint (29 projects) Pass

Known issue: Node 24 enables --experimental-strip-types by default, which conflicts with Jest's TypeScript config loading. Workaround: NODE_OPTIONS="--no-experimental-strip-types". This needs to be added to CI test jobs.

TODO List

  • CHANGELOG Updated
  • Documentation Updated
  • Add --no-experimental-strip-types to CI test workflows
  • Update OpenSSL pin in Dockerfiles for Alpine 3.23

Reviewer Notes

  • migrate-24_11.md contains the backport guide for the proprietary repo
  • The package-lock.json diff is minimal (engine field + some optional peer dep cleanup by npm 11)
  • cli-e2e-tests were skipped (require external API connectivity)

🤖 Generated with Claude Code

Upgrade Node.js runtime across the monorepo: .nvmrc, package.json engines,
Docker Compose images (node:24.14.0-alpine3.23), Dockerfiles, and CI workflows.

Impact study results:
- npm install: clean (npm 11.11.0)
- All 4 app builds pass (api, frontend, cli, mcp-server)
- All tests pass with --no-experimental-strip-types
- All 29 lint targets pass
- Known issue: Node 24 native TS stripping conflicts with Jest config loading

Includes migrate-24_11.md with backport guide for proprietary repo.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 25, 2026

Greptile Summary

This PR upgrades Node.js from 22.17.0 to 24.14.0 across the entire monorepo — touching CI workflows, Dockerfiles, docker-compose files, NX configuration (21→22), Storybook (9→10), and all jest.config.ts files. The jest configs are migrated from ESM (import/export default) to CJS (require/module.exports) to work around Node 24's default --experimental-strip-types conflicting with Jest's TypeScript config loading, and NODE_OPTIONS='--no-experimental-strip-types' is propagated to every build and test step.

Key changes:

  • .nvmrc, package.json engines, and all CI node-version inputs bumped to 24.14.0
  • All 25+ jest.config.ts files converted to CJS require/module.exports pattern
  • NODE_OPTIONS extended with --no-experimental-strip-types in build.yml, package.json scripts, and publish-cli-release.yml
  • Docker images updated from node:22.17.0-alpine3.21node:24.14.0-alpine3.23 in both compose files
  • Dockerfiles updated similarly; apk removal moved into the initial install layer (tighter image)
  • NX bumped to 22.6.0; testTargetName ownership moved from @nx/vite plugin to new @nx/vitest plugin entry
  • UI component type fixes (Omit<…,'variant'> + casts) required by stricter typings in the new dependency set
  • docker job in main.yml now always called (hadolint Dockerfile linting runs on all branches); build-scan-push is still guarded inside docker.yml

Outstanding items flagged: The openssl=3.5.5-r0 pin in both Dockerfiles is unverified for Alpine 3.23 (explicit PR TODO), and the SHA256 digest pin that was present in the old FROM lines was not re-added for the new image, which is a mild supply-chain hardening regression.

Confidence Score: 4/5

Safe to merge for CI/build paths; Docker image builds may fail due to the unverified openssl pin, which should be validated before merging.

All CI test jobs, lint, and application builds are correctly updated and verified by the author. The one outstanding P1 concern is the openssl=3.5.5-r0 pin in both Dockerfiles, which is an acknowledged TODO and was not covered by the testing matrix — if that exact package revision does not exist in Alpine 3.23's repos the Docker build pipeline will hard-fail. The P2 findings (digest pin removal, npm version inconsistency) are minor. Once the OpenSSL pin is confirmed or corrected this is ready to merge.

dockerfile/Dockerfile.api and dockerfile/Dockerfile.mcp — openssl version pin needs verification against Alpine 3.23 package index before Docker builds are run

Important Files Changed

Filename Overview
dockerfile/Dockerfile.api Upgraded to node:24.14.0-alpine3.23; apk removal moved to initial layer (good); SHA digest pin removed and openssl=3.5.5-r0 pin unverified for Alpine 3.23 (acknowledged TODO)
dockerfile/Dockerfile.mcp Same changes as Dockerfile.api: Alpine 3.23 base, digest pin removed, openssl=3.5.5-r0 pin potentially invalid for Alpine 3.23
.github/workflows/build.yml NODE_OPTIONS updated to include --no-experimental-strip-types for all build and test jobs — necessary fix for Node 24's TS stripping conflict with Jest
.github/workflows/main.yml node-version bumped to 24.14.0; if-guard on docker job removed intentionally so hadolint linting runs on all branches (build-scan-push still guarded inside docker.yml)
package.json Node/npm engines updated, --no-experimental-strip-types added to test scripts, NX upgraded to 22.6.0, Storybook 10 pinned; npm engine (11.9.0) differs from docker-package.json (11.11.0)
apps/api/docker-package.json Node/npm engines updated to 24.14.0/11.11.0 — npm version (11.11.0) is inconsistent with root package.json (11.9.0)
apps/api/jest.config.ts Converted from ESM imports to CJS require/module.exports to avoid Node 24 experimental TS stripping conflict during Jest config loading
packages/ui/src/lib/components/form/PMButton/PMButton.tsx Omits conflicting variant prop from ButtonProps via Omit<>, casts internal value — required for Storybook 10 / stricter TS types
nx.json testTargetName removed from @nx/vite plugin (was duplicate) and correctly assigned to new @nx/vitest plugin entry
docker-compose.yml All service images updated from node:22.17.0-alpine3.21 to node:24.14.0-alpine3.23 consistently

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Push / PR] --> B[build.yml]
    A --> C[quality.yml]
    A --> D[docker.yml - hadolint]

    B --> B1["nx build api\nNODE_OPTIONS: --max-old-space-size + --no-experimental-strip-types"]
    B --> B2["nx test api/frontend/mcp/packages/cli\nNODE_OPTIONS: --max-old-space-size + --no-experimental-strip-types"]

    C --> C1[lint-and-format]
    C --> C2[sonarqube]

    D --> D1["hadolint\n(all branches)"]
    D --> D2["build-scan-push\n(main + release tags only)"]

    B & C --> E[docker.yml - build-scan-push]
    E -->|main/release only| F["Build Docker images\nFROM node:24.14.0-alpine3.23\napk add openssl=3.5.5-r0"]
    F --> G[Scan and Push to DockerHub]

    style F fill:#ffe0b2,stroke:#e65100
    style D1 fill:#e8f5e9,stroke:#2e7d32
Loading

Reviews (11): Last reviewed commit: "Upgrade esbuild-loader dep" | Re-trigger Greptile

cteyton and others added 4 commits March 25, 2026 15:58
… workflow

Node 24 enables native TypeScript stripping by default, which conflicts
with Jest's own TS config loading (require() in .ts files, extensionless
imports). Append --no-experimental-strip-types to all NODE_OPTIONS in
build.yml to restore previous behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Docker image node:24.14.1-alpine3.23 does not exist
- Align all config files with the available 24.14.0 image

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The pinned version openssl=3.5.5-r0 does not exist in Alpine 3.23,
causing Docker builds to fail at the apk add step.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove branch gate on docker job in main.yml
- Remove job-level if condition in docker.yml
- Push steps retain their own ref guards (main/release only)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cteyton cteyton changed the title Upgrade Node.js from 22.17.0 to 24.14.1 Upgrade Node.js from 22.17.0 to 24.14.0 Mar 25, 2026
cteyton and others added 10 commits March 25, 2026 16:29
Remove phantom workspace references to non-existent
packages/linter-ast/parsers/* and regenerate lockfile with npm 11.9.0
(Node 24.14.0) to fix "invalid or damaged lockfile" CI errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename SiNuxtdotjs to SiNuxt (react-icons API change)
- Fix Chakra UI variant type mismatches in PMLink, PMButton,
  PMIconButton, and PMHeading using Omit + cast pattern for
  custom variants not in Chakra's base types

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Pin openssl=3.5.5-r0, bash=5.3.3-r1, ca-certificates=20251003-r0
- Fixes hadolint DL3018 warnings in Dockerfile.api and Dockerfile.mcp

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tore Docker hardening (#271)

* 📦 deps(nx): upgrade Nx from 21.6.8 to 22.6.0

- Bump all @nx/* packages and nx to 22.6.0
- Upgrade storybook from v9 to v10 (10.3.3)
- Add @nx/vitest plugin; move vitest testTargetName from @nx/vite
- Convert all jest.config.ts from ESM to CJS (require/module.exports)
- Fix require('../../jest-utils') → require('../../jest-utils.ts') for Node.js resolution
- Update story files to import from @storybook/react-vite instead of @storybook/react (Storybook v10 rule)
- Allow require() in jest.config.ts via ESLint override
- Fix getAbsolutePath return type any → string in .storybook/main.ts
- Bump @nx/devkit in tools/packmind-plugin/package.json to 22.6.0
- Add .claude/worktrees and .claude/settings.local.json to .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* 📦 deps: deduplicate workspace dependencies across packages (#270)

Remove external dependencies from child package.json files that are
already declared in root, fixing version drift and reducing maintenance
burden. Key changes:
- Removed ~48 redundant dependency declarations across 21 packages
- Fixed version mismatches (uuid, bullmq, slug, codemirror in skills/node-utils/ui)
- Standardized @packmind/* internal deps from pinned 0.0.1 to wildcard *
- Removed unnecessary devDependencies already provided by root

Co-Authored-By: Claude <noreply@anthropic.com>

https://claude.ai/code/session_018K74TaQrsXvK3qnvR9t2Ma

Co-authored-by: Claude <noreply@anthropic.com>

* 🔒️ security(docker): restore apk hardening and fix misleading CI comment

- Restore rm -rf /sbin/apk in Dockerfile.api and Dockerfile.mcp
  after apk add to prevent runtime package installation (P1 Greptile fix)
- Fix misleading comment in docker.yml: build-scan-push runs on main
  and release branches only, not all branches (P2 Greptile fix)
- Add missing newline at end of .gitignore (P2 Greptile fix)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@cteyton
Copy link
Copy Markdown
Contributor Author

cteyton commented Apr 1, 2026

Update your review @greptile

cteyton and others added 2 commits April 1, 2026 10:14
* 📦 deps(vite): upgrade Vite 6 → 8.0.3 with ecosystem updates

- Bump vite ^6.3.6 → ^8.0.3
- Bump @vitejs/plugin-react ^4.2.0 → ^5.2.0 (Vite 8 support)
- Bump all @nx/* packages 22.6.0 → 22.6.3 (adds Vite 8 peer dep)
- Bump nx 22.6.0 → 22.6.3
- Bump @react-router/* packages ^7.12.0 → ^7.14.0-pre.0 (first release with Vite 8 support)
- Install @emotion/react (missing peer dep of @chakra-ui/react, previously silently ignored by Rollup; Rolldown now errors)
- Remove build.commonjsOptions (no-op in Vite 8) from frontend and ui configs
- Rename build.rollupOptions → build.rolldownOptions in packages/ui config
- Fix tsconfigPaths plugin call syntax in packages/ui config (tsconfigPaths → tsconfigPaths())

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* 🐛 fix(frontend): align MemoryRouter import with react-router v7

- RuleDetails uses useSearchParams from 'react-router' but test imported
  MemoryRouter from 'react-router-dom' (different package version),
  causing missing router context in tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* 🔧 chore(frontend): remove deprecated ts-jest isolatedModules option

- Remove isolatedModules from jest.config.ts as it's already set in tsconfig.spec.json
- Eliminates ts-jest deprecation warning about isolatedModules being removed in v30

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* ⚡ perf(frontend): decouple frontend build from backend packages

Move test factories used by frontend specs into @packmind/test-utils
to break the transitive dependency chain (frontend → deployments → node-utils).
This reduces frontend build dependencies from 16 to 5 packages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* 🔧 chore(ci): trigger build on all PRs regardless of target branch

Remove the `branches: ['main']` filter on `pull_request` so the CI
pipeline runs on PRs targeting any branch, not just main.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* 📦 deps: update package-lock.json for Vite 8 upgrade

* 🔧 chore: fix all lint errors across the monorepo

Disable @nx/enforce-module-boundaries for jest.config.ts files,
allow @packmind/test-utils in browser spec files, update @nx/devkit
version, and remove stale eslint-disable directives.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* 💄 style: format jest configs and UI tooling with prettier

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* 🐛 fix(frontend): deduplicate react-router to fix invalid hook call

Align react-router-dom to ^7.14.0-pre.0 across root and frontend
package.json to prevent npm from nesting a second react-router copy.
Add resolve.dedupe for React packages in Vite config as safety net.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* 🐛 fix(frontend): remove react-router-dom to fix duplicate router context

react-router-dom was causing @react-router/dev to pre-bundle it as a
separate optimized dep from react-router, creating two copies of the
router context. Since all imports use react-router directly (v7 pattern),
react-router-dom is unnecessary.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* 🐛 fix(frontend): enable unstable_optimizeDeps to prevent mid-session re-optimization

Vite 8 lazily discovers new deps when navigating to routes, causing the
optimizer to re-run and create chunks with different version hashes.
Old and new chunks coexist, splitting react-router context. Enabling
unstable_optimizeDeps adds all route files as optimizeDeps entries so
all deps are discovered upfront at startup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add migration script

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 2, 2026

Too many files changed for review. (126 files found, 100 file limit)

cteyton and others added 8 commits April 2, 2026 15:33
- Update all Docker images to match .nvmrc/package.json/CI pin (24.14.1)
- Prevent mixed 24.14.0/24.14.1 runtime inconsistencies

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reorder nx reset before .nx removal, add cleanup for coverage, test-results,
.swc, .docusaurus, docs, tmp, vite/vitest timestamp files, graph.json,
tsconfig.base.effective.json, and package-lock.json.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
cteyton and others added 12 commits April 3, 2026 14:22
…24 compat

@sinonjs/fake-timers in Node 24 no longer supports string parsing in
the faked Date constructor. Pass pre-computed milliseconds instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ensures containers are fully removed during workspace cleanup, not just stopped.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove caret range to prevent npm from resolving 1.59.1, which mismatches
the Docker image pinned at v1.58.2-jammy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- .nvmrc, package.json, package-lock.json
- docker-compose.yml, docker-compose.production.yml
- Dockerfile.api, Dockerfile.mcp
- GitHub Actions workflows (main, publish-cli-release, tmp-cli-lint-windows)
- migrate_node24.sh
- apps/api/docker-package.json

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…leak

Chakra UI v3 Dialog needs proper open/close lifecycle to clean up its
portal and pointer-events on <html>. Conditional unmount via
{inviteUserOpened && ...} prevented cleanup, leaving the backdrop stuck.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
  - exec NX_DAEMON=false is invalid sh syntax (exec interprets it as command name)
  - Replace with export NX_DAEMON=false; exec across frontend, backend, mcp-server

  Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 3, 2026

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