Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
99884d0
chore(build): move pnpm configuration to pnpm-workspace.yaml and pin …
oidacra Sep 15, 2026
6a19f22
chore(build): install pnpm with npm instead of corepack in the Maven …
oidacra Sep 15, 2026
3e0a772
ci: bump pnpm/action-setup to v6.1.0 and cover the postman pin
oidacra Sep 15, 2026
8ae0c2d
chore(build): regenerate both lockfiles with pnpm 12.4.2
oidacra Sep 15, 2026
d06298d
docs(core-web): record where pnpm configuration lives after the pnpm …
oidacra Sep 15, 2026
cd1a6d1
fix(build): put the build's own pnpm on PATH
oidacra Sep 16, 2026
0a6d2cb
chore(build): declare dotcms-postman's pnpm posture instead of inheri…
oidacra Sep 16, 2026
1069b4b
chore(build): pin Node and pnpm through devEngines
oidacra Sep 16, 2026
125dc57
ci: replace pnpm/action-setup with pnpm/setup
oidacra Sep 16, 2026
28c758c
ci: report every pnpm version mismatch at once
oidacra Sep 16, 2026
600cccc
fix(build): keep pnpm on PATH for Maven-spawned processes
nicobytes Sep 16, 2026
cfafbb6
fix(build): give dotcms-postman an explicit pnpm 12 configuration
nicobytes Sep 16, 2026
1fe9b97
chore(build): correct stale pnpm comments and report every version mi…
nicobytes Sep 16, 2026
ab2f008
Merge branch 'issue-37553-migrate-core-web-to-pnpm-12' of github.com:…
nicobytes Sep 16, 2026
c12174d
feat(build): enforce the pnpm maturity gate now that the mirror retur…
oidacra Sep 16, 2026
1bee367
Merge remote-tracking branch 'origin/main' into issue-37553-migrate-c…
oidacra Sep 16, 2026
5da1ed4
ci: check every pinned toolchain declaration, not just packageManager
nicobytes Sep 16, 2026
d10a2c1
Merge branch 'main' into issue-37553-migrate-core-web-to-pnpm-12
nicobytes Sep 16, 2026
1837d3c
Merge branch 'main' into issue-37553-migrate-core-web-to-pnpm-12
nicobytes Sep 17, 2026
0f5a8b9
Merge branch 'main' into issue-37553-migrate-core-web-to-pnpm-12
nicobytes Sep 17, 2026
8cb9c24
Merge branch 'main' into issue-37553-migrate-core-web-to-pnpm-12
nicobytes Sep 17, 2026
0c86ba8
Merge branch 'main' into issue-37553-migrate-core-web-to-pnpm-12
nicobytes Sep 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -149,28 +149,22 @@ runs:
echo "::endgroup::"
shell: bash

# pnpm/setup downloads pnpm's own standalone binary — no Node and no npm
# involved — and with `cache: true` restores and saves the pnpm store itself,
# keyed on core-web/pnpm-lock.yaml, replacing the `pnpm store path` probe and
# the paired restore/save cache steps this action used to carry.
#
# No `version` input: it comes from core-web/package.json
# (devEngines.packageManager, falling back to packageManager).
#
# install: false because the install is run explicitly in the next step, which
# also records the resolved pnpm and Node versions in the job log.
- name: Set up pnpm
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0
with:
package_json_file: core-web/package.json
run_install: false

- name: Get pnpm store directory path
id: pnpm-info
shell: bash
run: |
echo "dir=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
echo "version=$(pnpm -v)" >> $GITHUB_OUTPUT

- id: restore-cache-pnpm
name: Restore pnpm Store Cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.pnpm-info.outputs.dir }}
key: ${{ runner.os }}-pnpm-${{ steps.pnpm-info.outputs.version }}-${{ hashFiles('core-web/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-${{ steps.pnpm-info.outputs.version }}
${{ runner.os }}-pnpm-
working-directory: core-web
install: false
cache: true

- name: 'Install project'
working-directory: ${{ github.workspace }}/core-web/
Expand All @@ -181,14 +175,6 @@ runs:
node --version
shell: bash

- id: save-cache-pnpm
name: Save pnpm Store Cache
if: ${{ steps.restore-cache-pnpm.outputs.cache-hit != 'true' }}
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.pnpm-info.outputs.dir }}
key: ${{ steps.restore-cache-pnpm.outputs.cache-primary-key }}

- name: 'Build SDK packages'
working-directory: ${{ github.workspace }}/core-web/
run: |
Expand Down
98 changes: 55 additions & 43 deletions .github/actions/core-cicd/maven-job/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -205,50 +205,70 @@ runs:
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: installs
# hash nodejs-parent/pom.xml so the pnpm-era layout (install-node-and-npm
# + corepack shim) is not mixed with the yarn-era layout from cached
# builds of the previous toolchain.
# hash nodejs-parent/pom.xml because that pom is what decides the contents
# of installs/: it holds <pnpm.version> and installs pnpm into the same
# prefix as Node, so bumping the pinned pnpm (or Node) invalidates this key
# instead of restoring a cache built for the previous versions.
key: node-binary-${{ hashFiles('core-web/.nvmrc', 'nodejs-parent/pom.xml') }}

- name: Verify pnpm version is in sync between package.json and nodejs-parent/pom.xml
- name: Verify the pinned toolchain versions are in sync
if: ${{ inputs.requires-node == 'true' }}
shell: bash
run: |
set -euo pipefail
pkg_json_pm=$(jq -r '.packageManager' core-web/package.json)
pkg_json_version="${pkg_json_pm#pnpm@}"
pom_version=$(grep -oE '<pnpm\.version>[^<]+</pnpm\.version>' nodejs-parent/pom.xml | sed -E 's|</?pnpm\.version>||g')
if [ "$pkg_json_version" != "$pom_version" ]; then
echo "::error::pnpm version mismatch: core-web/package.json=$pkg_json_version vs nodejs-parent/pom.xml=$pom_version. Keep these in sync — Maven uses the pom value while the pnpm store cache key uses the package.json value." >&2
exit 1
fi
echo "pnpm version in sync: $pom_version"

# nodejs-parent/pom.xml is the reference: Maven installs those versions and every
# module runs them. Every other declaration has to agree, including the ones that
# only the workflow reads — pnpm/setup resolves its version from
# devEngines.packageManager *before* falling back to packageManager, so checking
# packageManager alone would let a devEngines-only bump install a different pnpm
# than Maven's, with nothing failing. Same for devEngines.runtime and Node.
# Collect every mismatch before failing, so a contributor who forgot to bump one
# declaration does not fix it, re-run CI, and only then learn about the next.
pom_pnpm=$(grep -oE '<pnpm\.version>[^<]+</pnpm\.version>' nodejs-parent/pom.xml | sed -E 's|</?pnpm\.version>||g')
pom_node=$(grep -oE '<node\.js\.version>[^<]+</node\.js\.version>' nodejs-parent/pom.xml | sed -E 's|</?node\.js\.version>||g')
pom_node=${pom_node#v}
fail=0

# An absent declaration is fine; a present one must match. Written as a plain `if`
# rather than a `&&` chain because under `set -e` a short-circuited chain returns
# non-zero and would abort the step instead of recording the mismatch.
check() { # <file> <label> <declared> <expected>
if [ -n "$3" ] && [ "$3" != "$4" ]; then
echo "::error file=$1::$2 declares $3, nodejs-parent/pom.xml pins $4. Maven installs the pom version and every module runs it, so all declarations must agree." >&2
fail=1
fi
}

for manifest in core-web/package.json dotcms-postman/package.json; do
declared=$(jq -r '.packageManager // ""' "$manifest")
check "$manifest" "$manifest packageManager" "${declared#pnpm@}" "$pom_pnpm"
check "$manifest" "$manifest devEngines.packageManager" \
"$(jq -r 'if .devEngines.packageManager.name == "pnpm" then .devEngines.packageManager.version else "" end' "$manifest")" "$pom_pnpm"
check "$manifest" "$manifest devEngines.runtime" \
"$(jq -r 'if .devEngines.runtime.name == "node" then .devEngines.runtime.version else "" end' "$manifest")" "$pom_node"
done

[ "$fail" -eq 0 ] || exit 1
echo "toolchain in sync: pnpm $pom_pnpm, node $pom_node"

# pnpm/setup downloads pnpm's own standalone binary — no Node and no npm
# involved — and with `cache: true` restores and saves the pnpm store itself,
# keyed on core-web/pnpm-lock.yaml. That replaces the `pnpm store path` probe
# and the paired restore/save cache steps this action used to carry.
#
# No `version` input: the action reads it from core-web/package.json
# (devEngines.packageManager, falling back to packageManager), which the sync
# check above already holds against nodejs-parent/pom.xml.
#
# install: false — the Maven build runs the install itself, in the phase and
# with the flags the poms decide.
- name: Set up pnpm
if: ${{ inputs.requires-node == 'true' }}
uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0
with:
package_json_file: core-web/package.json
run_install: false

- name: Get pnpm store directory path
id: pnpm-info
if: ${{ inputs.requires-node == 'true' }}
shell: bash
run: |
echo "dir=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
echo "version=$(pnpm -v)" >> $GITHUB_OUTPUT

- id: restore-cache-pnpm
name: Restore pnpm Store Cache
if: ${{ inputs.requires-node == 'true' }}
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.pnpm-info.outputs.dir }}
key: ${{ runner.os }}-pnpm-${{ steps.pnpm-info.outputs.version }}-${{ hashFiles('core-web/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-${{ steps.pnpm-info.outputs.version }}
${{ runner.os }}-pnpm-
working-directory: core-web
install: false
cache: true
Comment thread
nicobytes marked this conversation as resolved.


- id: cache-sonar
Expand Down Expand Up @@ -445,14 +465,6 @@ runs:
path: installs
key: ${{ steps.restore-cache-node.outputs.cache-primary-key }}

- id: save-cache-pnpm
name: Save pnpm Store Cache
if: ${{ inputs.requires-node == 'true' && inputs.generate-artifacts == 'true' && steps.restore-cache-pnpm.outputs.cache-hit != 'true' }}
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.pnpm-info.outputs.dir }}
key: ${{ steps.restore-cache-pnpm.outputs.cache-primary-key }}

- id: save-cache-sonar
name: Save SonarQube Cache
if: ${{ inputs.cache-sonar == 'true' && steps.cache-sonar.outputs.cache-hit != 'true' }}
Expand Down
10 changes: 2 additions & 8 deletions core-web/.npmrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
# From pnpm 11 on this file is read for registry and auth only; every other setting
# is ignored in silence. See pnpm-workspace.yaml for the rest of the configuration.
registry=https://dotcms-npm.b-cdn.net
Comment thread
nicobytes marked this conversation as resolved.
Comment thread
oidacra marked this conversation as resolved.
engine-strict=true
# Keep strict-peer-dependencies=false (pnpm default would block the install on
# pre-existing peer conflicts: codelyzer, ng-mocks, ngx-markdown, etc.).
# These mirror what yarn 1 warned about silently. Dropping this flag requires
# fixing each conflict first, tracked as separate follow-up work.
strict-peer-dependencies=false
# Mirrors `yarn config set network-timeout 6000000` from the previous toolchain.
fetch-timeout=6000000
20 changes: 20 additions & 0 deletions core-web/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,26 @@ This file provides guidance to Claude Code when working with code in this reposi

DotCMS Core-Web monorepo — Angular + Nx. Uses **pnpm** as package manager. Nx is not installed globally — always use `pnpm nx`.

### pnpm Configuration

All of it lives in **`pnpm-workspace.yaml`** — `overrides`, `peerDependencyRules`,
`ignoredOptionalDependencies`, `engineStrict`, `strictPeerDependencies`, `fetchTimeout`,
`allowBuilds` (replaces the removed `onlyBuiltDependencies`) and the supply-chain settings
(`trustPolicy`, `trustPolicyExclude`, `trustPolicyExcludePrune`, `blockExoticSubdeps`,
`strictDepBuilds`, `verifyDepsBeforeRun`). The `pnpm` block in `package.json` is **not read at
all**, and `.npmrc` is consulted for registry and auth only — don't add settings to either.

Two installs that now fail where they used to warn:

- **`ERR_PNPM_IGNORED_BUILDS`** (`strictDepBuilds`) — a dependency with a build script that is not
in `allowBuilds` now aborts the install; pnpm 10 only printed `Ignored build scripts: ...` and
carried on. Add the package to `allowBuilds` with `true` to let it build or `false` to deny it
explicitly.
- **`trustPolicy: no-downgrade`** — fails when a version carries weaker publish-trust evidence than
earlier releases of the same package. Eight known older versions sit in `trustPolicyExclude`, each
with its reason and publish date. A new one is not automatically an attack: check that version's
publish date and provenance history before adding a line.

### MCP Servers

Configured in `/.mcp.json`. Use these instead of guessing:
Expand Down
6 changes: 3 additions & 3 deletions core-web/apps/dotcms-ui-e2e/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<configuration>
<executable>${node.install.dir}/pnpm</executable>
<executable>${pnpm.executable}</executable>
<workingDirectory>${e2e.frontend.dir}</workingDirectory>
<environmentVariables>
<PATH>${node.install.dir}:${env.PATH}</PATH>
<PATH>${pnpm.bin.dir}:${node.install.dir}:${env.PATH}</PATH>
</environmentVariables>
</configuration>
<executions>
Expand Down Expand Up @@ -103,7 +103,7 @@
<CI>true</CI>
<CURRENT_ENV>${e2e.test.env}</CURRENT_ENV>
<NODE_OPTIONS>${e2e.node.options}</NODE_OPTIONS>
<PATH>${node.install.dir}:${env.PATH}</PATH>
<PATH>${pnpm.bin.dir}:${node.install.dir}:${env.PATH}</PATH>
</environmentVariables>
</configuration>
</execution>
Expand Down
5 changes: 3 additions & 2 deletions core-web/libs/sdk/create-app/scripts/verify-package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ fi
#
# Both packers are tried because neither is present everywhere. `npm` is missing from a
# pnpm-managed local toolchain (`pnpm env` installs node without it), which is what made
# `npm pack` report a false failure on a dev machine. `pnpm` — the corepack shim the Maven
# build puts on PATH — is the one that returned nothing on the CI runner. Whichever answers
# `npm pack` report a false failure on a dev machine. `pnpm` — installed with npm into
# installs/node by the Maven build, which puts installs/node/bin on PATH — is the one that
# returned nothing on the CI runner. Whichever answers
# first decides the check; if neither does, each packer's stderr is printed, because a
# silent packer is exactly what made this failure unreadable the first time.
PACKED=""
Expand Down
54 changes: 12 additions & 42 deletions core-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -326,47 +326,17 @@
"nx": {
"includedScripts": []
},
"pnpm": {
"overrides": {
"@babel/helper-define-polyfill-provider": "0.6.6",
"@tiptap/core": "3.22.2",
"@vue/shared": "3.5.29",
"http-cache-semantics": "4.1.1",
"pdfjs-dist": "4.2.67",
"stylus": "github:stylus/stylus#0.59.0"
"packageManager": "pnpm@12.4.2",
"devEngines": {
"runtime": {
"name": "node",
"version": "22.22.3",
"onFail": "download"
},
"peerDependencyRules": {
"allowedVersions": {
"@angular/core": "22",
"@angular/common": "22",
"@angular/forms": "22",
"@angular/router": "22",
"@angular/platform-browser": "22",
"@angular/cdk": "22",
"primeng>@angular/core": "22",
"primeng>@angular/common": "22",
"primeng>@angular/forms": "22",
"primeng>@angular/router": "22",
"primeng>@angular/platform-browser": "22",
"@ngrx/signals>@angular/core": "22",
"@ngrx/component-store>@angular/core": "22"
}
},
"ignoredOptionalDependencies": [
"canvas"
],
"onlyBuiltDependencies": [
"canvas",
"esbuild",
"@swc/core",
"nx",
"@parcel/watcher",
"core-js",
"document-register-element",
"lmdb",
"msgpackr-extract",
"unrs-resolver"
]
},
"packageManager": "pnpm@10.17.1"
"packageManager": {
"name": "pnpm",
"version": "12.4.2",
"onFail": "download"
}
}
}
Loading
Loading