Skip to content

fix(lambda-nodejs): run beforeInstall hook after workspace files are written#37899

Open
Zelys-DFKH wants to merge 2 commits into
aws:mainfrom
Zelys-DFKH:fix-37898
Open

fix(lambda-nodejs): run beforeInstall hook after workspace files are written#37899
Zelys-DFKH wants to merge 2 commits into
aws:mainfrom
Zelys-DFKH:fix-37898

Conversation

@Zelys-DFKH
Copy link
Copy Markdown

@Zelys-DFKH Zelys-DFKH commented May 17, 2026

Fixes #37898.

Credit to @cmodijk: the issue report traces the execution order exactly, identifies the root cause, and spells out what the fix should look like. This PR implements what you described.


The problem

On pnpm v11, strictDepBuilds is the effective default. Any NodejsFunction that bundles nodeModules with a native-build dependency (e.g. ssh2-sftp-clientcpu-features) hits this at deploy time:

ERR_PNPM_INSTALL_SCRIPTS_NOT_ALLOWED  cpu-features@1.x.x is not allowed to run install scripts

The beforeInstall hook was designed to handle this: append allowBuilds entries to pnpm-workspace.yaml before the install runs. It silently doesn't work, which is worse than not existing.

Why

CDK writes an empty pnpm-workspace.yaml to prevent pnpm from walking up to a monorepo root (introduced in #21910, still the right call). The bug was ordering. Before this fix:

beforeBundling → esbuild → [beforeInstall] → write pnpm-workspace.yaml + install → afterBundling
                                ↑ hook fires here        ↑ empty yaml written here

Whatever the user wrote in beforeInstall got overwritten a step later. The hook's name is a lie.

Fix

Splits node-modules file-ops into two phases via a new NodeModuleFileOps interface:

  • prepareSteps: writes pnpm-workspace.yaml, package.json, and the lockfile
  • installSteps: runs the package manager and post-install cleanup

createBundlingSteps puts beforeInstall between them, for both Docker and local bundling.

beforeBundling → esbuild → write pnpm-workspace.yaml → [beforeInstall] → install → afterBundling
                                        ↑ files written here    ↑ hook fires here

No behavior change for callers where beforeInstall returns [] (the default).

The refactor makes createBundlingSteps slightly more involved. dockerFileOps and localFileOps are both private, so there's no API surface change.

Validation

Unit test (bundling.test.ts): beforeInstall hook fires after pnpm workspace files are written (Docker) — asserts the Docker shell command orders pnpm-workspace.yaml write → beforeInstall output → pnpm install. This is the test that would have caught the bug.

E2E behavioral test (bundling-e2e.test.ts): pnpm-specific / beforeInstall writes to pnpm-workspace.yaml survive CDK workspace setup — runs CDK synthesis with a minimal pnpm project (local and Docker where available), writes allowBuilds: to pnpm-workspace.yaml in beforeInstall, then reads the output file and asserts the content survived. Skipped when pnpm is unavailable locally and Docker bundling is off.

Integration test (integ.dependencies-pnpm-before-install.ts): deploys a real Lambda with a delay dependency via forceDockerBundling: true, where beforeInstall writes allowBuilds: delay: true to pnpm-workspace.yaml. The snapshot ships with zeroed hashes: Docker + full CDK build aren't available in this environment. Regenerate with:

yarn integ test/aws-lambda-nodejs/test/integ.dependencies-pnpm-before-install.js --update-on-failed

Checklist


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

…written

Splits the node-modules file-ops step into two phases — prepareSteps (writes
pnpm-workspace.yaml, package.json, and the lockfile) and installSteps (runs
the package manager and cleanup) — so that the beforeInstall command hook fires
between them. Previously, beforeInstall ran before depsCommand, which meant CDK's
empty pnpm-workspace.yaml write always silently overrode any allowBuilds entries a
user appended there. This made the hook useless for pnpm v11 workspaces that need
allowBuilds to permit native build scripts (e.g. cpu-features via ssh2-sftp-client).

Fixes aws#37898.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added bug This issue is a bug. p2 beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK labels May 17, 2026
Copy link
Copy Markdown
Collaborator

@aws-cdk-automation aws-cdk-automation left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This review is outdated)

…rdering

Adds two layers of test coverage for the ordering fix from the previous commit:

1. E2e behavioral test in bundling-e2e.test.ts — asserts that allowBuilds
   written to pnpm-workspace.yaml by beforeInstall survives CDK's own workspace
   file setup. Runs under both local (skipped if pnpm absent) and Docker
   bundling. Closes the test gap that allowed the bug to exist.

2. CDK integration test (integ.dependencies-pnpm-before-install.ts) — deploys
   a Lambda that uses delay@5.0.0 with a beforeInstall hook writing to
   pnpm-workspace.yaml, exercising the full bundling pipeline. Snapshot hashes
   are placeholder (all-zeros); run `yarn integ` in framework-integ to
   regenerate from the Docker bundled output.

Fixes aws#37898

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added the effort/medium Medium work item – several days of effort label May 18, 2026
@aws-cdk-automation aws-cdk-automation dismissed their stale review May 18, 2026 20:20

✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

beginning-contributor [Pilot] contributed between 0-2 PRs to the CDK bug This issue is a bug. effort/medium Medium work item – several days of effort p2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(lambda-nodejs): nodeModules + pnpm v11 empty pnpm-workspace.yaml blocks native module builds, beforeInstall cannot override it

2 participants