Skip to content

Instantly share code, notes, and snippets.

@feloy
Last active July 24, 2026 09:37
Show Gist options
  • Select an option

  • Save feloy/f04c3d5dd8c929f5c045a71a5bbf39a5 to your computer and use it in GitHub Desktop.

Select an option

Save feloy/f04c3d5dd8c929f5c045a71a5bbf39a5 to your computer and use it in GitHub Desktop.
Migrate pnpm to v11 for Podman Desktop extensions
name pnpm11-migration
description Step-by-step guide for migrating a Podman Desktop extension from pnpm v10 to v11. Covers config relocation, workspace YAML format, Containerfile updates, CI workflow fixes, and common pitfalls. Triggers when upgrading pnpm to v11 or fixing pnpm v11 compatibility issues.

pnpm v10 → v11 Migration Guide

pnpm v11 requires Node.js 22+ and moves all non-auth configuration out of .npmrc and package.json#pnpm into pnpm-workspace.yaml using camelCase keys.

Prerequisites

  • Node.js 22+
  • Existing pnpm v10 workspace with pnpm-workspace.yaml

Step 1 — Run the codemod

pnpx codemod run pnpm-v10-to-v11 --no-interactive --allow-child-process

This handles:

  • Moving pnpm.overrides from package.json → pnpm-workspace.yaml
  • Converting onlyBuiltDependencies (list) → allowBuilds (boolean map)
  • Moving .npmrc settings → pnpm-workspace.yaml (camelCase keys)
  • Deleting .npmrc if it only contained non-auth settings
  • Updating packageManager to the latest pnpm v11 version

Step 2 — Pin packageManager with a sha512 hash

The codemod writes packageManager without an integrity hash (e.g. pnpm@11.x.y). Corepack requires the hash for verification. Get it and update package.json:

# Get the sha512 integrity string for the installed version
PNPM_VERSION=$(node -e "console.log(require('./package.json').packageManager.split('@')[1])")
INTEGRITY=$(npm view pnpm@${PNPM_VERSION} dist.integrity)
BASE64=${INTEGRITY#sha512-}

# Convert base64 to hex
HEX=$(python3 -c "import base64, sys; print(base64.b64decode(sys.argv[1]).hex())" "$BASE64")

echo "pnpm@${PNPM_VERSION}+sha512.${HEX}"

Update package.json:

{
  "packageManager": "pnpm@11.x.y+sha512.<hex>"
}

Activate via Corepack:

corepack enable && corepack install
pnpm --version  # should print 11.x

Step 3 — Update Containerfiles

Check all Containerfiles under build/ for the following issues.

Remove .npmrc COPY

# REMOVE this line — file no longer exists
COPY --chown=1001:root .npmrc /opt/app-root/extension-source/

Remove runtime .npmrc creation

# REMOVE — nodeLinker is now in pnpm-workspace.yaml
RUN echo "node-linker=hoisted" >> /opt/app-root/extension/.npmrc

Add CI=true to pnpm install

pnpm v11 prompts for confirmation before purging node_modules when the store format changes. Set CI=true to suppress this in non-TTY environments:

RUN CI=true pnpm install && \
    pnpm build

GitHub Actions sets CI=true by default, so workflow files do not need this.

Builder Containerfiles: install pnpm via Corepack

Builder images (e.g. Containerfile.builder) that explicitly install pnpm with npm install --global pnpm@10 must be updated. Do not replace with npm install --global pnpm@11 — use Corepack instead so the version is always read from packageManager in package.json.

UBI / RHEL images: registry.access.redhat.com/ubi*/nodejs-* images do not ship corepack. Install it via npm in the root setup step before calling corepack enable.

Before:

USER root
RUN dnf install -y jq
USER default
# ...
COPY package.json .
COPY pnpm-lock.yaml .
RUN npm install --global pnpm@10 && \
    pnpm --frozen-lockfile install

After:

USER root
RUN dnf install -y jq && npm i -g corepack && corepack enable
USER default
# ...
COPY package.json .
COPY pnpm-lock.yaml .
COPY pnpm-workspace.yaml .
RUN corepack install && \
    CI=true pnpm --frozen-lockfile install

corepack install reads the packageManager field (including the sha512 hash set in Step 2) and installs exactly that version. The corepack enable call in the root step makes the corepack shim available system-wide so the non-root user can invoke it.

pnpm-workspace.yaml must be copied alongside package.json and pnpm-lock.yaml — it now holds overrides, and pnpm v11 will reject a frozen install with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH if the file is absent.

Step 4 — Update GitHub Actions workflows

Search all workflow files for a hardcoded pnpm version in pnpm/action-setup:

grep -rn 'version:' .github/workflows/

When packageManager is set in package.json, the pnpm/action-setup action reads the version from there automatically — remove the version: field so the workflow stays in sync without further edits:

Before:

- uses: pnpm/action-setup@...
  with:
    version: 10
    run_install: false

After:

- uses: pnpm/action-setup@...
  with:
    run_install: false

Step 5 — Fix pnpm list calls

pnpm v11 changed how -C (change directory) resolves in workspaces — it may return the root workspace package instead of the target. Replace with --filter:

Before:

pnpm list -C packages/extension/ --json | jq -r '.[0].devDependencies."@podman-desktop/api".version'

After:

pnpm list --filter <package-name> --json --depth 0 | jq -r '.[0].devDependencies."@podman-desktop/api".version'

Where <package-name> is the name field from the target package's package.json.

Search for all occurrences:

grep -rn 'pnpm list.*-C\|pnpm list.*--dir' .github/ build/ scripts/

Step 6 — Regenerate the lockfile

pnpm install

This regenerates pnpm-lock.yaml in v11 format. Review the diff — the overrides section in the lockfile should match pnpm-workspace.yaml.

Step 7 — Verify

Run the full verification suite:

pnpm build
pnpm test
pnpm typecheck
podman build -t local_image -f build/Containerfile ./

Common Pitfalls

confirmModulesPurge in non-TTY

pnpm v11 prompts before purging node_modules when upgrading from v10's store format. In containers and CI, set CI=true as an environment variable. Do NOT set confirmModulesPurge: false in pnpm-workspace.yaml — that would also suppress the prompt for developers.

npm_config_* environment variables

pnpm v11 no longer reads npm_config_* environment variables. Rename them to pnpm_config_* in CI configs, shell profiles, and Docker images.

Missing packageManager hash

The codemod writes packageManager without a sha512 hash. Corepack will not verify the binary without it. Always complete Step 2 after running the codemod.

ERR_PNPM_LOCKFILE_CONFIG_MISMATCH in builder images

If a Containerfile only copies package.json and pnpm-lock.yaml, pnpm v11 will reject --frozen-lockfile with a config mismatch error because pnpm-workspace.yaml (which now holds overrides) is missing. Always copy all three files:

COPY package.json .
COPY pnpm-lock.yaml .
COPY pnpm-workspace.yaml .

Review Checklist

  • Codemod ran without errors
  • pnpm.overrides removed from package.json
  • overrides present in pnpm-workspace.yaml
  • onlyBuiltDependencies converted to allowBuilds map
  • .npmrc settings moved to pnpm-workspace.yaml (camelCase)
  • .npmrc deleted or trimmed to auth-only
  • packageManager updated with sha512 hash
  • Containerfile: no .npmrc COPY
  • Containerfile: no runtime .npmrc creation
  • Containerfile: CI=true on pnpm install
  • Builder Containerfile: npm install --global pnpm@10 replaced with Corepack pattern (see Step 3)
  • Builder Containerfile (UBI images): npm i -g corepack && corepack enable added to the root setup RUN step
  • Builder Containerfile: pnpm-workspace.yaml added to COPY statements
  • GitHub Actions: version: removed from all pnpm/action-setup steps
  • All pnpm list -C replaced with --filter
  • No npm_config_* env vars in CI workflows
  • pnpm install succeeds
  • pnpm build succeeds
  • pnpm test passes
  • Container image builds successfully
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment