Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(lambda-nodejs): unable to use nodeModules with pnpm #21911

Merged
merged 2 commits into from
Dec 28, 2022
Merged

fix(lambda-nodejs): unable to use nodeModules with pnpm #21911

merged 2 commits into from
Dec 28, 2022

Conversation

cecheta
Copy link
Contributor

@cecheta cecheta commented Sep 3, 2022

Fixes #21910

By default, pnpm uses symlinks when installing dependencies, and modules only have access to dependencies in their package.json. This means when trying to bundle, dependencies of depedencies are not installed. This PR fixes this by using the --config.node_linker=hoisted flag to create a flat node_modules structure. Docs.

The second problem this PR fixes is when using pnpm workspaces. With workspaces, modules are installed within the workspace the command is run in. This means that when installing as part of bundling in a nested directory, no node_modules directory is produced at all. By creating a new pnpm-workspace.yaml file locally before installing, this essentially creates a new (nested) workspace, and node_modules is installed here. An empty file is enough for this to suffice.

The PR also removes a .modules.yaml file from the node_modules after installing. This file contains a datetime of the last install, therefore it would cause the lambda code to change on each deployment if it was included in the bundle.

I have tested this fix locally on both Mac and Windows. I have also included an integration test which failed before these changes, however I am not sure if it should be included due to the node_modules in the assets.


All Submissions:

Adding new Unconventional Dependencies:

  • This PR adds new unconventional dependencies following the process described here

New Features

  • Have you added the new feature to an integration test?
    • Did you use yarn integ to deploy the infrastructure and generate the snapshot (i.e. yarn integ without --dry-run)?

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

@gitpod-io
Copy link

gitpod-io bot commented Sep 3, 2022

@aws-cdk-automation aws-cdk-automation requested a review from a team September 3, 2022 21:39
@github-actions github-actions bot added bug This issue is a bug. p2 labels Sep 3, 2022
Copy link
Contributor

@TheRealAmazonKendra TheRealAmazonKendra left a comment

Choose a reason for hiding this comment

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

Thank you for your contribution! I don't think we can review this effectively at present. With 395 changes files, this almost certainly contains some unintended changes. I can't tell now many of these would be intentionally added to this PR.

@mergify mergify bot dismissed TheRealAmazonKendra’s stale review September 12, 2022 16:07

Pull request has been modified.

@cecheta
Copy link
Contributor Author

cecheta commented Sep 12, 2022

Thank you for your contribution! I don't think we can review this effectively at present. With 395 changes files, this almost certainly contains some unintended changes. I can't tell now many of these would be intentionally added to this PR.

The large number of files comes from the assets in the integration tests, as I tried to write a test for a scenario which wouldn't have worked prior to this fix.

I've removed the integ test for now

@TheRealAmazonKendra
Copy link
Contributor

Thank you for your contribution! I don't think we can review this effectively at present. With 395 changes files, this almost certainly contains some unintended changes. I can't tell now many of these would be intentionally added to this PR.

The large number of files comes from the assets in the integration tests, as I tried to write a test for a scenario which wouldn't have worked prior to this fix.

I've removed the integ test for now

In that case, I'll want that test back in once we've done a review of this. Apologies for assuming it was an unintentional addition.

@@ -298,13 +299,17 @@ interface BundlingCommandOptions {
class OsCommand {
constructor(private readonly osPlatform: NodeJS.Platform) {}

public writeJson(filePath: string, data: any): string {
const stringifiedData = JSON.stringify(data);
public write(filePath: string, data: any): string {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you provide further context on why this additional function is needed?

Copy link
Contributor Author

@cecheta cecheta Oct 29, 2022

Choose a reason for hiding this comment

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

I have created write() which takes a string and outputs it to a file. This is required for the pnpm-workspace.yaml file.

writeJson() receives any input, stringifies it, then calls write().

This way I have avoided duplicate code.

return `echo '${data}' > "${filePath}"`;
}

public writeJson(filePath: string, data: any): string {
Copy link
Contributor

Choose a reason for hiding this comment

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

And why this doesn't need the extra check for OS?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The OS check has been moved to the write() function

@@ -39,7 +39,7 @@ export class PackageManager {
case LockFile.PNPM:
return new PackageManager({
lockFile: LockFile.PNPM,
installCommand: logLevel && logLevel !== LogLevel.INFO ? ['pnpm', 'install', '--reporter', 'silent'] : ['pnpm', 'install'],
installCommand: logLevel && logLevel !== LogLevel.INFO ? ['pnpm', 'install', '--reporter', 'silent', '--shamefully-hoist'] : ['pnpm', 'install', '--shamefully-hoist'],
Copy link
Contributor

Choose a reason for hiding this comment

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

Pretend I don't know shit about pnpm here (because I don't). What is --shamefully-hoist doing here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm now using --config.node_linker=hoisted, as explained in the PR description

@@ -219,6 +219,7 @@ export class Bundling implements cdk.BundlingOptions {

// Create dummy package.json, copy lock file if any and then install
depsCommand = chain([
this.packageManager.lockFile === LockFile.PNPM ? osCommand.write(pathJoin(options.outputDir, 'pnpm-workspace.yaml'), ''): '',
Copy link
Contributor

Choose a reason for hiding this comment

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

If the only instance where you're using write has no value for data, I'm not sure I get why that function takes in two values and why it's being used here. Are you just creating an empty file titled pnpm-workspace.yaml? Also, why use an empty string instead of undefined for this.packageManager.lockFile if it's not pnpm?

Copy link
Contributor Author

@cecheta cecheta Oct 29, 2022

Choose a reason for hiding this comment

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

Yes creating an empty pnpm-workspace.yaml file avoids a problem when using workspaces. I have updated the PR description to explain.

I have created a general write() function so it could be used by someone else in the future if they need to write a (non-empty) file, rather than creating a writeEmptyFile() function which seems a bit bespoke.

I am using an empty string as chain() takes an array of strings. It will be removed then all the commands are joined together into a single command.

@cecheta cecheta marked this pull request as draft October 28, 2022 19:24
@mergify mergify bot dismissed TheRealAmazonKendra’s stale review October 28, 2022 19:25

Pull request has been modified.

Copy link
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.

The pull request linter has failed. See the aws-cdk-automation comment below for failure reasons. If you believe this pull request should receive an exemption, please comment and provide a justification.

@aws-cdk-automation aws-cdk-automation dismissed their stale review October 28, 2022 23:37

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

@github-actions github-actions bot added the effort/medium Medium work item – several days of effort label Oct 29, 2022
@cecheta cecheta marked this pull request as ready for review October 29, 2022 00:30
@cecheta
Copy link
Contributor Author

cecheta commented Oct 29, 2022

Thank you for your contribution! I don't think we can review this effectively at present. With 395 changes files, this almost certainly contains some unintended changes. I can't tell now many of these would be intentionally added to this PR.

The large number of files comes from the assets in the integration tests, as I tried to write a test for a scenario which wouldn't have worked prior to this fix.
I've removed the integ test for now

In that case, I'll want that test back in once we've done a review of this. Apologies for assuming it was an unintentional addition.

I have added the test back, again the large number of files is due to the node_modules included in the snapshot.

I have chosen axios for the test as this package has a dependency, therefore demonstrating the issue, however it only has one dependency to hopefully keep the asset size low

@cecheta cecheta marked this pull request as draft October 29, 2022 17:32
@cecheta cecheta marked this pull request as ready for review October 29, 2022 19:05
@aws-cdk-automation
Copy link
Collaborator

This PR has been in the MERGE CONFLICTS state for 3 weeks, and looks abandoned. To keep this PR from being closed, please continue work on it. If not, it will automatically be closed in a week.

Copy link
Contributor

@corymhall corymhall left a comment

Choose a reason for hiding this comment

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

@cecheta this looks great! I just have a couple of minor comments.

@@ -16,6 +16,9 @@ phases:
# Install yarn if it wasn't already present in the image
- yarn --version || npm -g install yarn

# Install pnpm if it wasn't already present in the image
- pnpm --version || npm -g install pnpm
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't need this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I get bash: pnpm: command not found if I remove it, although I'm not sure if it's needed for just one of the files or both

image

buildspec.yaml Outdated
@@ -16,6 +16,9 @@ phases:
# Install yarn if it wasn't already present in the image
- yarn --version || npm -g install yarn

# Install pnpm if it wasn't already present in the image
- pnpm --version || npm -g install pnpm
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't need this.

});

new integ.IntegTest(app, 'PnpmTest', {
testCases: [stack],
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
testCases: [stack],
testCases: [stack],
stackUpdateWorkflow: false, // this will tell the runner to not check in assets.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, thank you

Copy link
Contributor Author

Choose a reason for hiding this comment

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

FYI I ran into a bug when updating the tests, raised #23407

yarn.lock Outdated
@@ -2668,6 +2668,13 @@ aws4@^1.8.0:
resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==

axios@0.26.1:
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this file should have been updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed the version of axios to use a version already in the lock file

@corymhall corymhall self-assigned this Dec 19, 2022
@mergify mergify bot dismissed corymhall’s stale review December 20, 2022 14:20

Pull request has been modified.

// Will be installed, not bundled
// (axios is a package with sub-dependencies,
// will be used to ensure pnpm bundling works as expected)
nodeModules: ['axios'],
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add forceDockerBundling: true? I think if we force it to bundle inside docker it removes the need to add any of the dependencies in the repo.

Copy link
Contributor Author

@cecheta cecheta Dec 22, 2022

Choose a reason for hiding this comment

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

I switched to Docker bundling, but doing so demonstrated another issue, which is that when you use Docker bundling, the pnpm store is included in the asset by default. This is also the case on main.

I've added a tmp directory for the store into the Dockerfile, and added --config.package-import-method=clone-or-copy when running pnpm install which resolves this issue

@mergify mergify bot dismissed corymhall’s stale review December 22, 2022 19:47

Pull request has been modified.

Copy link
Contributor

@corymhall corymhall left a comment

Choose a reason for hiding this comment

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

Just one minor thing and then this is ready to go! 🚀

"@types/jest": "^27.5.2",
"axios": "^0.27.2",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"axios": "^0.27.2",

I don't think this is needed anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For some reason, the tests in test/docker.test.js are now failing

I can see in the logs:

time="2022-12-27T14:51:42.954645757Z" level=info msg="Attempting next endpoint for pull after error: denied: User: arn:aws:sts::464831875156:assumed-role/Ops-cdk-autobuild-prs-AutoBuildv2ProjectRoleC688F4-CY3ZTUL6CY8Q/AWSCodeBuild-b78b1055-51c8-4066-8449-8e563d561d94 is not authorized to perform: ecr:BatchGetImage on resource: arn:aws:ecr:us-east-1:464831875156:repository/library/esbuild because no identity-based policy allows the ecr:BatchGetImage action"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Squashing commits seemed to fix it, not sure why

@mergify mergify bot dismissed corymhall’s stale review December 27, 2022 14:36

Pull request has been modified.

@mergify
Copy link
Contributor

mergify bot commented Dec 28, 2022

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildv2Project1C6BFA3F-wQm2hXv2jqQv
  • Commit ID: 199cdd2
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@mergify mergify bot merged commit 7c752db into aws:main Dec 28, 2022
@mergify
Copy link
Contributor

mergify bot commented Dec 28, 2022

Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

brennanho pushed a commit to brennanho/aws-cdk that referenced this pull request Jan 20, 2023
Fixes aws#21910

By default, pnpm uses symlinks when installing dependencies, and modules only have access to dependencies in their `package.json`. This means when trying to bundle, dependencies of depedencies are not installed. This PR fixes this by using the ` --config.node_linker=hoisted` flag to create a flat `node_modules` structure. [Docs](https://pnpm.io/npmrc#node-linker).

The second problem this PR fixes is when using pnpm workspaces. With workspaces, modules are installed within the workspace the command is run in. This means that when installing as part of bundling in a nested directory, no `node_modules` directory is produced at all. By creating a new `pnpm-workspace.yaml` file locally before installing, this essentially creates a new (nested) workspace, and `node_modules` is installed here. An empty file is enough for this to suffice.

The PR also removes a `.modules.yaml` file from the `node_modules` after installing.  This file contains a datetime of the last install, therefore it would cause the lambda code to change on each deployment if it was included in the bundle.

I have tested this fix locally on both Mac and Windows. I have also included an integration test which failed before these changes, however I am not sure if it should be included due to the `node_modules` in the assets.

----

### All Submissions:

* [X] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [X] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [X] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
brennanho pushed a commit to brennanho/aws-cdk that referenced this pull request Feb 22, 2023
Fixes aws#21910

By default, pnpm uses symlinks when installing dependencies, and modules only have access to dependencies in their `package.json`. This means when trying to bundle, dependencies of depedencies are not installed. This PR fixes this by using the ` --config.node_linker=hoisted` flag to create a flat `node_modules` structure. [Docs](https://pnpm.io/npmrc#node-linker).

The second problem this PR fixes is when using pnpm workspaces. With workspaces, modules are installed within the workspace the command is run in. This means that when installing as part of bundling in a nested directory, no `node_modules` directory is produced at all. By creating a new `pnpm-workspace.yaml` file locally before installing, this essentially creates a new (nested) workspace, and `node_modules` is installed here. An empty file is enough for this to suffice.

The PR also removes a `.modules.yaml` file from the `node_modules` after installing.  This file contains a datetime of the last install, therefore it would cause the lambda code to change on each deployment if it was included in the bundle.

I have tested this fix locally on both Mac and Windows. I have also included an integration test which failed before these changes, however I am not sure if it should be included due to the `node_modules` in the assets.

----

### All Submissions:

* [X] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies)

### New Features

* [X] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [X] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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): Unable to use nodeModules with pnpm
4 participants