From a5e5c163c19809b7fbbda3cee1edc7b17dc34f5c Mon Sep 17 00:00:00 2001 From: iam-truongtrungnghia Date: Thu, 15 Oct 2026 12:38:40 +0700 Subject: [PATCH] fix(pnpm): pass settings as pnpm_config_*, the only prefix pnpm 11 reads --- adapters/nestjs/mise.toml | 4 ++-- adapters/nextjs/mise.toml | 4 ++-- common/docs/mise.toml | 4 ++-- common/packages-types/mise.toml | 4 ++-- ...017-supply-chain-defaults-in-generated-projects.md | 8 +++++++- lib/adapter.sh | 4 ++-- lib/pnpm.sh | 2 +- lib/service.sh | 4 ++-- tests/contract.bats | 11 +++++++++++ 9 files changed, 31 insertions(+), 14 deletions(-) diff --git a/adapters/nestjs/mise.toml b/adapters/nestjs/mise.toml index bcd5f44..a262705 100644 --- a/adapters/nestjs/mise.toml +++ b/adapters/nestjs/mise.toml @@ -1,7 +1,7 @@ [tasks.install] # The first frozen install after packages/types joins the workspace relinks # node_modules, and pnpm must not stop to ask. -env = { npm_config_confirm_modules_purge = "false" } +env = { pnpm_config_confirm_modules_purge = "false" } run = "pnpm install --frozen-lockfile" [tasks.format] @@ -39,7 +39,7 @@ run = [{ task = ":prisma" }, "pnpm exec nest build"] [tasks.ci-unit] # same reason as :install's env above — ci-unit calls :install too. -env = { npm_config_confirm_modules_purge = "false" } +env = { pnpm_config_confirm_modules_purge = "false" } run = [ { task = ":install" }, { task = ":format" }, diff --git a/adapters/nextjs/mise.toml b/adapters/nextjs/mise.toml index ae8cfaf..2f8cba5 100644 --- a/adapters/nextjs/mise.toml +++ b/adapters/nextjs/mise.toml @@ -1,7 +1,7 @@ [tasks.install] # The first frozen install after packages/types joins the workspace relinks # node_modules, and pnpm must not stop to ask. -env = { npm_config_confirm_modules_purge = "false" } +env = { pnpm_config_confirm_modules_purge = "false" } run = "pnpm install --frozen-lockfile" [tasks.format] @@ -28,7 +28,7 @@ run = "pnpm exec next build" [tasks.ci-unit] # same reason as :install's env above — ci-unit calls :install too. -env = { npm_config_confirm_modules_purge = "false" } +env = { pnpm_config_confirm_modules_purge = "false" } run = [ { task = ":install" }, { task = ":format" }, diff --git a/common/docs/mise.toml b/common/docs/mise.toml index 0453dd4..ea066f2 100644 --- a/common/docs/mise.toml +++ b/common/docs/mise.toml @@ -1,6 +1,6 @@ [tasks.install] # A frozen install that relinks node_modules must not stop to ask (toolbox ADR-0017). -env = { npm_config_confirm_modules_purge = "false" } +env = { pnpm_config_confirm_modules_purge = "false" } run = "pnpm install --frozen-lockfile" [tasks.format] @@ -24,7 +24,7 @@ run = "true" run = "pnpm exec vitepress build ." [tasks.ci-unit] -env = { npm_config_confirm_modules_purge = "false" } +env = { pnpm_config_confirm_modules_purge = "false" } run = [ { task = ":install" }, { task = ":format" }, diff --git a/common/packages-types/mise.toml b/common/packages-types/mise.toml index e30cf10..1c18a99 100644 --- a/common/packages-types/mise.toml +++ b/common/packages-types/mise.toml @@ -1,7 +1,7 @@ [tasks.install] # The first frozen install after this package joins the workspace relinks # node_modules, and pnpm must not stop to ask. -env = { npm_config_confirm_modules_purge = "false" } +env = { pnpm_config_confirm_modules_purge = "false" } run = "pnpm install --frozen-lockfile" [tasks.format] @@ -24,7 +24,7 @@ run = "true" [tasks.ci-unit] # same reason as :install's env above — ci-unit calls :install too. -env = { npm_config_confirm_modules_purge = "false" } +env = { pnpm_config_confirm_modules_purge = "false" } run = [{ task = ":install" }, { task = ":format" }, { task = ":check" }] [tasks.checklist] diff --git a/docs/decisions/0017-supply-chain-defaults-in-generated-projects.md b/docs/decisions/0017-supply-chain-defaults-in-generated-projects.md index 1799f33..cb9d1bf 100644 --- a/docs/decisions/0017-supply-chain-defaults-in-generated-projects.md +++ b/docs/decisions/0017-supply-chain-defaults-in-generated-projects.md @@ -83,7 +83,7 @@ whether it clears the immediate failure: > `join_typescript_workspace` deletes the `docs` pair instead. - **`confirmModulesPurge` does not ship in `common/pnpm-workspace.yaml`.** - It is set instead as `env = { npm_config_confirm_modules_purge = "false" }` + It is set instead as `env = { pnpm_config_confirm_modules_purge = "false" }` on the `install` and `ci-unit` mise tasks, in every adapter's `mise.toml` and in `common/packages-types/mise.toml`. Scoped this way it unblocks only the unattended contract tasks; a developer running @@ -261,3 +261,9 @@ whether it clears the immediate failure: requirement of a dead-link check failing the build (`ignoreDeadLinks: false`); trading that away to dodge one `allowBuilds` line is not a good trade. + +> **Amended 2026-10-15.** pnpm 11 no longer reads `npm_config_*` environment +> variables, only `pnpm_config_*`. Every override this toolbox sets through the +> environment now uses that prefix; under the old one, `CI=true`'s frozen +> lockfile failed the second app of `scaffold new --web vite-react --api nestjs`. +> `tests/contract.bats` rejects an `npm_config_` variable. diff --git a/lib/adapter.sh b/lib/adapter.sh index 324c007..0f5bcd9 100644 --- a/lib/adapter.sh +++ b/lib/adapter.sh @@ -162,7 +162,7 @@ generate_adapter_app() { step "generating ${rel} with ${name} (a framework generator, this takes a few minutes)" run_quietly "generating ${rel} with ${name}" \ - env APP_DIR="$(basename "$dest")" npm_config_frozen_lockfile=false \ + env APP_DIR="$(basename "$dest")" pnpm_config_frozen_lockfile=false \ bash -c "$ADAPTER_TOOLCHAIN_SCRIPT" _ "$parent" "$ADAPTER_GENERATOR" } @@ -175,7 +175,7 @@ configure_adapter_app() { step "configuring ${rel}" run_quietly "configuring ${rel} after its generator ran" \ - env npm_config_frozen_lockfile=false npm_config_verify_deps_before_run=false \ + env pnpm_config_frozen_lockfile=false pnpm_config_verify_deps_before_run=false \ bash -c "$ADAPTER_TOOLCHAIN_SCRIPT" _ "$dest" "$ADAPTER_POST_GENERATE" } diff --git a/lib/pnpm.sh b/lib/pnpm.sh index 9c3c996..c54eea9 100644 --- a/lib/pnpm.sh +++ b/lib/pnpm.sh @@ -12,7 +12,7 @@ RELEASE_AGE_BLOCK_START="# too fresh at generation time" RELEASE_AGE_BLOCK_END="# end minimumReleaseAgeExclude" # Relaxed while a generator runs inside an installed workspace, and stripped on -# both of cmd_add's exit paths. In the file, not the environment: npm_config_* +# both of cmd_add's exit paths. In the file, not the environment: pnpm_config_* # does not survive a generator's nested pnpm processes. # confirmModulesPurge relinking the shared node_modules otherwise wants a TTY # frozenLockfile CI=true turns it on, and the generator adds dependencies diff --git a/lib/service.sh b/lib/service.sh index 6c905f9..bb75d84 100644 --- a/lib/service.sh +++ b/lib/service.sh @@ -388,7 +388,7 @@ migrate_service_block() { # inside, and a driver's unchecked failure would vanish. # # Tools go in by PATH, not `mise exec -C`, which resolves PATH from scratch and -# loses yq. The npm_config_* pair is apply_adapter's. +# loses yq. The pnpm_config_* pair is apply_adapter's. run_driver_apply() { local -r app="$1" project="$2" family="$3" service="$4" driver="$5" local pnpm_bin node_bin uv_bin="" @@ -410,7 +410,7 @@ run_driver_apply() { step "wiring ${service} into $(app_service_key "$app")" run_quietly "wiring ${service} into $(app_service_key "$app") (the ${family} driver)" \ env PATH="${uv_bin:+${uv_bin}:}${pnpm_bin}:${node_bin}:${PATH}" \ - npm_config_frozen_lockfile=false npm_config_verify_deps_before_run=false \ + pnpm_config_frozen_lockfile=false pnpm_config_verify_deps_before_run=false \ SCAFFOLD_PROJECT_ROOT="$project" \ bash -euo pipefail -c "$driver_script" _ "$app" "$driver" } diff --git a/tests/contract.bats b/tests/contract.bats index 3e33e95..b60aa88 100644 --- a/tests/contract.bats +++ b/tests/contract.bats @@ -310,3 +310,14 @@ setup() { false } } + +@test "no pnpm setting is passed through an npm_config_ variable" { + # pnpm 11 reads only pnpm_config_*; an npm_config_ override is silently + # ignored, and CI=true's frozen lockfile then fails the second app of a + # `scaffold new`. + run git -C "$SCAFFOLD_ROOT" grep -nE '(^|[^p])npm_config_' -- scaffold lib adapters common services + [ "$status" -ne 0 ] || { + echo "$output" + false + } +}