Skip to content

feat: per-package node version#8277

Merged
zkochan merged 49 commits intomainfrom
per-pkg-node-version-6720
Jul 13, 2024
Merged

feat: per-package node version#8277
zkochan merged 49 commits intomainfrom
per-pkg-node-version-6720

Conversation

@KSXGitHub
Copy link
Copy Markdown
Contributor

@KSXGitHub KSXGitHub commented Jul 4, 2024

Fixes #6720


TODO:

  • pnpm run
  • pnpm install, pnpm add, pnpm update
  • pnpm publish

@zkochan
Copy link
Copy Markdown
Member

zkochan commented Jul 4, 2024

In the previous PR #6724 I have suggested to declare the config (useNodeVersion) in the package.json files instead of .npmrc. I think it is easier to implement this way. However, all the current options in the "pnpm" field of package.json are meant to be used in the root of the workspace only. cc @pnpm/collaborators

@gluxon
Copy link
Copy Markdown
Member

gluxon commented Jul 4, 2024

Having this new setting be in per-project package.json files seems okay to me. I think it'll work as long as the docs are clear about the root pnpm key and per-project pnpm key.

@KSXGitHub
Copy link
Copy Markdown
Contributor Author

KSXGitHub commented Jul 5, 2024

Maybe we should add warnings as well for the use of root-only keys in local workspace package in another PR?

Comment on lines -1063 to -1099
// TODO: fix the related implementation then remove `.skip`
test.skip('recursive run when some packages define node version, config does not define use-node-version', async () => {
preparePackages([
{
name: 'node-version-undefined',
scripts: {
'assert-node-version': `node -e "assert.equal(process.version, '${process.version}')"`,
},
},
{
name: 'node-version-18',
scripts: {
'assert-node-version': 'node -e "assert.equal(process.version, \'v18.0.0\')"',
},
pnpm: {
useNodeVersion: '18.0.0',
},
},
{
name: 'node-version-20',
scripts: {
'assert-node-version': 'node -e "assert.equal(process.version, \'v20.0.0\')"',
},
pnpm: {
useNodeVersion: '20.0.0',
},
},
])
await run.handler({
...DEFAULT_OPTS,
...await filterPackagesFromDir(process.cwd(), [{ namePattern: '*' }]),
dir: process.cwd(),
recursive: true,
workspaceDir: process.cwd(),
workspaceConcurrency: 3, // if this step fails, change this value to 1 to see error message.
}, ['assert-node-version'])
})
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This test doesn't work when placed here but it work when moved to /pnpm/test. Not sure why.

@zkochan
Copy link
Copy Markdown
Member

zkochan commented Jul 11, 2024

I am wondering about the location of this setting. We might want to move it to a subfield. Like pnpm.executionEnv.nodeVersion. In that case in the future we might add some more settings. Like env variables for instance.

@KSXGitHub
Copy link
Copy Markdown
Contributor Author

@zkochan

I am wondering about the location of this setting. We might want to move it to a subfield. Like pnpm.executionEnv.nodeVersion. In that case in the future we might add some more settings. Like env variables for instance.

I have implemented this suggestion in c96153e. You may revert the change if you don't like it.

Aesthetically speaking, it makes certain part of the code more long-winded, but could it be fixed by reusing createPrepareExecutionEnv?


Regarding the "future", I wonder how would they live alongside nodeVersion within pnpm.executionEnv. Could it be pnpm.executionEnv.env like this?

{
  "pnpm": {
    "executionEnv": {
      "nodeVersion": "20.0.0",
      "env": {
        "USERNAME": "username",
        "TOKEN": "d94f9468de245385b5204d1554e248db"
      }
    }
  }
}

Or should this be a bit flatter (with intermediate executionEnv removed)?

@zkochan
Copy link
Copy Markdown
Member

zkochan commented Jul 11, 2024

Regarding the "future", I wonder how would they live alongside nodeVersion within pnpm.executionEnv. Could it be pnpm.executionEnv.env like this?

I think it would be inside pnpm.executionEnv.variables.

Although, I am not sure it would make sense at all. Usually variables are either system specific or they contain secrets. Also, I am not sure you'd want to set the variables for all the scripts. Usually they are only needed for one specific command.

Copy link
Copy Markdown
Contributor Author

@KSXGitHub KSXGitHub left a comment

Choose a reason for hiding this comment

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

@zkochan What do you think about this?

Comment on lines +127 to +129
if (pkg.package.manifest.pnpm?.executionEnv?.nodeVersion) {
lifecycleOpts.extraBinPaths = (await node.prepareExecutionEnv(opts, { executionEnv: pkg.package.manifest.pnpm.executionEnv })).extraBinPaths
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
if (pkg.package.manifest.pnpm?.executionEnv?.nodeVersion) {
lifecycleOpts.extraBinPaths = (await node.prepareExecutionEnv(opts, { executionEnv: pkg.package.manifest.pnpm.executionEnv })).extraBinPaths
}
const { executionEnv } = pkg.package.manifest.pnpm ?? {}
if (executionEnv) {
lifecycleOpts.extraBinPaths = (await node.prepareExecutionEnv(opts, { executionEnv })).extraBinPaths
}

Comment on lines +254 to +256
if (manifest.pnpm?.executionEnv?.nodeVersion) {
lifecycleOpts.extraBinPaths = (await node.prepareExecutionEnv(opts, { executionEnv: manifest.pnpm.executionEnv })).extraBinPaths
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
if (manifest.pnpm?.executionEnv?.nodeVersion) {
lifecycleOpts.extraBinPaths = (await node.prepareExecutionEnv(opts, { executionEnv: manifest.pnpm.executionEnv })).extraBinPaths
}
const { executionEnv } = manifest.pnpm ?? {}
if (manifest.pnpm?.executionEnv?.nodeVersion) {
lifecycleOpts.extraBinPaths = (await node.prepareExecutionEnv(opts, { executionEnv })).extraBinPaths
}

} else {
return [...binPaths.slice(0, index), nodePath, ...binPaths.slice(index + 1)]
}
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am not sure why this complex function is needed at all. The node location should be just appended to the array. Replacing is not good because we need the specified node path to have the highest priority. What if the replaced directory didn't have the highest priority? Also, even if it will be duplicated, I don't think it is an issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@KSXGitHub KSXGitHub requested a review from zkochan July 12, 2024 13:19
}

return {
extraBinPaths: [await nodePathPromise, ...extraBinPaths ?? []],
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it should be added to the end

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it should be added to the end

It would allow extraBinPaths to override node. Do you still want this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

isn't that the whole point? To override node with the version specified in the setting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It would allow the tail of extraBinPaths to override the node provided by the nodePathPromise. The tail of extraBinPaths may or may not include node from use-node-version of .npmrc. It would defeat the whole point.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, you are correct. The directories at the beginning have bigger priority. It is correct as it is now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have added a test that would fail should extraBinPaths was handled incorrectly (leading to use-node-version overrides executionEnv): 2721d44

You may revert if you find it unnecessary.

@KSXGitHub KSXGitHub requested a review from zkochan July 12, 2024 14:56
"@pnpm/plugin-commands-installation": major
"@pnpm/plugin-commands-publishing": major
"@pnpm/plugin-commands-script-runners": major
"@pnpm/plugin-commands-env": minor
Copy link
Copy Markdown
Contributor Author

@KSXGitHub KSXGitHub Jul 13, 2024

Choose a reason for hiding this comment

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

@zkochan Since 7e7c412 breaks the API,

Suggested change
"@pnpm/plugin-commands-env": minor
"@pnpm/plugin-commands-env": major

@zkochan zkochan merged commit 0ef168b into main Jul 13, 2024
@zkochan zkochan deleted the per-pkg-node-version-6720 branch July 13, 2024 12:08
@oyvinmar oyvinmar mentioned this pull request Aug 6, 2024
4 tasks
@antitoxic
Copy link
Copy Markdown
Contributor

OMG! amazing! Thank you @KSXGitHub @zkochan ❤️

PS: Does that also execute the post.... hooks like postinstall using the specific node version?

@zkochan
Copy link
Copy Markdown
Member

zkochan commented Aug 26, 2024

No, this is somewhat more complex as the dependency may be used by several different projects in the workspace, which all use different node.js versions. So, which node.js version should be used to build the dependency?

We can add a field for the dependency itself to tell which node.js version it requires for being build. See related RFC: pnpm/rfcs#7

@antitoxic antitoxic mentioned this pull request Aug 28, 2024
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

support per-package node version specification in workspaces

4 participants