pnpm not available in build-and-test when package_manager: pnpm #5

Closed
opened 2026-03-27 15:41:49 +00:00 by KofTwentyTwo · 3 comments
KofTwentyTwo commented 2026-03-27 15:41:49 +00:00 (Migrated from github.com)

Problem

The node-webapp pipeline supports package_manager: pnpm in .munitor.yml, but pnpm is not pre-installed on cimg/node Docker images. The test.setup parameter in run_test_setup.sh expects a file path to an executable script, so passing a shell command string like "corepack enable && corepack prepare pnpm@9 --activate" silently fails (file not found).

This means any pnpm project hits pnpm: command not found during build-and-test unless the user works around it.

Current workaround

Add npm run ci:setup as the first entry in test.commands, where ci:setup is defined in package.json:

"scripts": {
  "ci:setup": "corepack enable && corepack prepare pnpm@9 --activate"
}
test:
  commands:
    - "npm run ci:setup"
    - "pnpm install --frozen-lockfile"
    - "pnpm lint"
    - "pnpm build"
    - "pnpm test"

This works but is a leaky abstraction -- the orb knows the package manager, it should make sure it's available.

Relevant files

  • src/scripts/run_test_setup.sh -- validates test_setup as a file path, fails if not a file
  • src/scripts/npm_test.sh -- runs test commands, no pnpm setup step
  • src/jobs/npm_build_and_test.yml -- job definition with test_setup parameter
  • src/scripts/templates/node-webapp.yml.tpl -- template conditional for test_setup

Proposed fix

Option A (recommended): Auto-detect pnpm. When package_manager: pnpm, automatically run corepack enable && corepack prepare pnpm@<version> --activate before test commands in npm_test.sh. Read the version from engines.pnpm in package.json or default to latest.

Option B: Support inline commands in test.setup. If the test_setup value doesn't point to an existing file, treat it as a shell command string (bash -c). More flexible but less targeted.

Option C: Both. Auto-detect for pnpm/yarn (A) and support inline commands (B).

Option A is the cleanest -- the orb already knows the package manager and should be responsible for making it available.

Reproducer

Any .munitor.yml with package_manager: pnpm and no workaround in test.commands will fail with pnpm: command not found on the first pnpm command. See KofTwentyTwo/praesidium PR #1 job 280 for a real example.

## Problem The `node-webapp` pipeline supports `package_manager: pnpm` in `.munitor.yml`, but pnpm is not pre-installed on `cimg/node` Docker images. The `test.setup` parameter in `run_test_setup.sh` expects a **file path** to an executable script, so passing a shell command string like `"corepack enable && corepack prepare pnpm@9 --activate"` silently fails (file not found). This means any pnpm project hits `pnpm: command not found` during `build-and-test` unless the user works around it. ### Current workaround Add `npm run ci:setup` as the first entry in `test.commands`, where `ci:setup` is defined in `package.json`: ```json "scripts": { "ci:setup": "corepack enable && corepack prepare pnpm@9 --activate" } ``` ```yaml test: commands: - "npm run ci:setup" - "pnpm install --frozen-lockfile" - "pnpm lint" - "pnpm build" - "pnpm test" ``` This works but is a leaky abstraction -- the orb knows the package manager, it should make sure it's available. ## Relevant files - `src/scripts/run_test_setup.sh` -- validates test_setup as a file path, fails if not a file - `src/scripts/npm_test.sh` -- runs test commands, no pnpm setup step - `src/jobs/npm_build_and_test.yml` -- job definition with test_setup parameter - `src/scripts/templates/node-webapp.yml.tpl` -- template conditional for test_setup ## Proposed fix **Option A (recommended): Auto-detect pnpm.** When `package_manager: pnpm`, automatically run `corepack enable && corepack prepare pnpm@<version> --activate` before test commands in `npm_test.sh`. Read the version from `engines.pnpm` in `package.json` or default to latest. **Option B: Support inline commands in test.setup.** If the test_setup value doesn't point to an existing file, treat it as a shell command string (`bash -c`). More flexible but less targeted. **Option C: Both.** Auto-detect for pnpm/yarn (A) and support inline commands (B). Option A is the cleanest -- the orb already knows the package manager and should be responsible for making it available. ## Reproducer Any `.munitor.yml` with `package_manager: pnpm` and no workaround in `test.commands` will fail with `pnpm: command not found` on the first pnpm command. See `KofTwentyTwo/praesidium` PR #1 job 280 for a real example.
KofTwentyTwo commented 2026-03-27 15:56:21 +00:00 (Migrated from github.com)

Scope question

The issue specifically targets build-and-test (making pnpm available before test commands run). But MUNITOR_PACKAGE_MANAGER is parsed in extract_munitor_vars.sh and never actually used anywhere downstream. There are several places that hardcode npm:

File Hardcoded behavior
npm_install.sh Runs npm ci, checks for package-lock.json
npm_test.sh Falls back to npx jest
restore_npm_cache.yml / save_npm_cache.yml Checksum on package-lock.json, caches ~/.npm
generate_dockerfile.sh All node templates use npm ci / npm run build

Narrow fix: Just activate pnpm via corepack before test commands (as the issue proposes). pnpm projects would still need pnpm install --frozen-lockfile as a custom test command since npm_install.sh would still run npm ci.

Broad fix: Make npm_install.sh, cache keys, and Dockerfile generation all package-manager-aware. The orb would handle the full lifecycle natively for npm, pnpm, and yarn.

Which scope is intended here?

### Scope question The issue specifically targets **build-and-test** (making pnpm available before test commands run). But `MUNITOR_PACKAGE_MANAGER` is parsed in `extract_munitor_vars.sh` and never actually used anywhere downstream. There are several places that hardcode npm: | File | Hardcoded behavior | |---|---| | `npm_install.sh` | Runs `npm ci`, checks for `package-lock.json` | | `npm_test.sh` | Falls back to `npx jest` | | `restore_npm_cache.yml` / `save_npm_cache.yml` | Checksum on `package-lock.json`, caches `~/.npm` | | `generate_dockerfile.sh` | All node templates use `npm ci` / `npm run build` | **Narrow fix:** Just activate pnpm via corepack before test commands (as the issue proposes). pnpm projects would still need `pnpm install --frozen-lockfile` as a custom test command since `npm_install.sh` would still run `npm ci`. **Broad fix:** Make `npm_install.sh`, cache keys, and Dockerfile generation all package-manager-aware. The orb would handle the full lifecycle natively for npm, pnpm, and yarn. Which scope is intended here?
KofTwentyTwo commented 2026-03-27 15:57:14 +00:00 (Migrated from github.com)

Broad fix. The orb already accepts package_manager: pnpm — it should follow through on that contract across the full lifecycle.

Specifically:

  1. Install step (npm_install.sh): Use pnpm install --frozen-lockfile when MUNITOR_PACKAGE_MANAGER=pnpm, npm ci for npm. Enable corepack before pnpm commands.
  2. Cache (restore_npm_cache.yml / save_npm_cache.yml): Checksum pnpm-lock.yaml and cache ~/.local/share/pnpm/store for pnpm projects instead of package-lock.json / ~/.npm.
  3. Dockerfile generation (generate_dockerfile.sh): Use pnpm install --frozen-lockfile / pnpm run build in generated Dockerfiles for pnpm projects.
  4. Test fallback (npm_test.sh): Fall back to pnpm test instead of npx jest when package manager is pnpm.
  5. Corepack activation: Add a setup step that runs corepack enable && corepack prepare pnpm@<version> --activate before any pnpm command. Read version from engines.pnpm in package.json, or use latest.

The narrow fix would just shift the workaround from the consumer to the orb. If we're going to touch this, do it right — make package_manager a first-class config that actually drives behavior everywhere. Same pattern should extend to yarn if/when needed.

**Broad fix.** The orb already accepts `package_manager: pnpm` — it should follow through on that contract across the full lifecycle. Specifically: 1. **Install step** (`npm_install.sh`): Use `pnpm install --frozen-lockfile` when `MUNITOR_PACKAGE_MANAGER=pnpm`, `npm ci` for npm. Enable corepack before pnpm commands. 2. **Cache** (`restore_npm_cache.yml` / `save_npm_cache.yml`): Checksum `pnpm-lock.yaml` and cache `~/.local/share/pnpm/store` for pnpm projects instead of `package-lock.json` / `~/.npm`. 3. **Dockerfile generation** (`generate_dockerfile.sh`): Use `pnpm install --frozen-lockfile` / `pnpm run build` in generated Dockerfiles for pnpm projects. 4. **Test fallback** (`npm_test.sh`): Fall back to `pnpm test` instead of `npx jest` when package manager is pnpm. 5. **Corepack activation**: Add a setup step that runs `corepack enable && corepack prepare pnpm@<version> --activate` before any pnpm command. Read version from `engines.pnpm` in `package.json`, or use `latest`. The narrow fix would just shift the workaround from the consumer to the orb. If we're going to touch this, do it right — make `package_manager` a first-class config that actually drives behavior everywhere. Same pattern should extend to yarn if/when needed.
KofTwentyTwo commented 2026-03-27 15:59:56 +00:00 (Migrated from github.com)

Update: after fixing the pnpm install issue (by adding npm run ci:setup as the first test command), the build still failed because Prisma client wasn't generated. In a pnpm monorepo, the Prisma postinstall hook can't find the schema in the default location -- it needs an explicit prisma generate --schema=./packages/api/prisma/schema.prisma.

Our .munitor.yml now has three workarounds that should all be the orb's responsibility:

test:
  commands:
    - "npm run ci:setup"              # 1. Workaround: install pnpm
    - "pnpm install --frozen-lockfile" # 2. Workaround: orb ran npm ci, not pnpm install
    - "pnpm db:generate"              # 3. Workaround: Prisma client not generated
    - "pnpm lint"
    - "pnpm build"
    - "pnpm test"

What we want .munitor.yml to look like:

test:
  commands:
    - "pnpm lint"
    - "pnpm build"
    - "pnpm test"

The orb should handle:

  1. Package manager activation -- already discussed above
  2. Dependency install -- pnpm install --frozen-lockfile when package_manager: pnpm, not npm ci
  3. Prisma detection -- if a prisma/schema.prisma exists anywhere in the project (including monorepo subdirs), run prisma generate automatically after install. This isn't pnpm-specific -- it would fail the same way with npm in a monorepo.

The goal: package_manager: pnpm in .munitor.yml should mean the orb handles the full lifecycle. The consumer's test.commands should only contain their actual test/lint/build steps, not infrastructure setup.

Update: after fixing the pnpm install issue (by adding `npm run ci:setup` as the first test command), the build still failed because Prisma client wasn't generated. In a pnpm monorepo, the Prisma `postinstall` hook can't find the schema in the default location -- it needs an explicit `prisma generate --schema=./packages/api/prisma/schema.prisma`. Our `.munitor.yml` now has **three workarounds** that should all be the orb's responsibility: ```yaml test: commands: - "npm run ci:setup" # 1. Workaround: install pnpm - "pnpm install --frozen-lockfile" # 2. Workaround: orb ran npm ci, not pnpm install - "pnpm db:generate" # 3. Workaround: Prisma client not generated - "pnpm lint" - "pnpm build" - "pnpm test" ``` What we want `.munitor.yml` to look like: ```yaml test: commands: - "pnpm lint" - "pnpm build" - "pnpm test" ``` The orb should handle: 1. **Package manager activation** -- already discussed above 2. **Dependency install** -- `pnpm install --frozen-lockfile` when `package_manager: pnpm`, not `npm ci` 3. **Prisma detection** -- if a `prisma/schema.prisma` exists anywhere in the project (including monorepo subdirs), run `prisma generate` automatically after install. This isn't pnpm-specific -- it would fail the same way with npm in a monorepo. The goal: `package_manager: pnpm` in `.munitor.yml` should mean the orb handles the full lifecycle. The consumer's `test.commands` should only contain their actual test/lint/build steps, not infrastructure setup.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: KofTwentyTwo/Munitor#5