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

javascript: package yarn v2 #20540

Merged
merged 7 commits into from
Feb 21, 2024

Conversation

kevinobruno
Copy link
Contributor

Add support for pants package using yarn@v2.

Currently when trying to package a yarn@v2 project, the following error occurs:

Engine traceback:
  in `package` goal

ProcessExecutionFailure: Process 'Installing [email protected].' failed with exit code 1.
stdout:
➤ YN0050: The --frozen-lockfile option is deprecated; use --immutable and/or --immutable-cache instead

stderr:

Thats because the option --frozen-lockfile was replaced with --immutable on yarn >= 2.

This behavior can be reproducible at https://github.com/kevinobruno/pants-yarn running pants package //yarnv2

@huonw huonw added the category:bugfix Bug fixes for released features label Feb 14, 2024
Copy link
Member

@kaos kaos left a comment

Choose a reason for hiding this comment

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

thanks!

Comment on lines 80 to 87
def immutable_install_args(self) -> tuple[str, ...]:
if self.package_manager == "npm":
return ("clean-install",)
if self.package_manager == "yarn":
if nodesemver.satisfies(self.package_manager_version, "1.x"):
return ("install", "--frozen-lockfile")
return ("install", "--immutable")
return ("install", "--frozen-lockfile")
Copy link
Member

Choose a reason for hiding this comment

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

The trailing return here doesn't make sense to me. Do we expect any other package managers than "npm" or "yarn"? And if so, do we expect them to use intall --frozen-lockfile in that case?
How about raising an error to avoid proceeding with unknown results?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

iiuc, pants also support pnpm. I kept the trailing return to not break existing uses of this backend, I also tried to follow the pattern of all other properties returning a default. Maybe we could check if the package manager is supported prior to the use of this property?

Copy link
Member

Choose a reason for hiding this comment

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

Ah, right. OK, so I'd prefer it if this method would be explicit about it then (regardless of how it was before, I think this could be improved from status quo) by not silently accepting "any" backend.

i.e. something along the lines of:

Suggested change
def immutable_install_args(self) -> tuple[str, ...]:
if self.package_manager == "npm":
return ("clean-install",)
if self.package_manager == "yarn":
if nodesemver.satisfies(self.package_manager_version, "1.x"):
return ("install", "--frozen-lockfile")
return ("install", "--immutable")
return ("install", "--frozen-lockfile")
def immutable_install_args(self) -> tuple[str, ...]:
if self.package_manager == "npm":
return ("clean-install",)
if self.package_manager == "yarn":
if nodesemver.satisfies(self.package_manager_version, "1.x"):
return ("install", "--frozen-lockfile")
return ("install", "--immutable")
if self.package_manager == "pnpm":
return ("install", "--frozen-lockfile")
raise E(f"Unsupported package manager: {self.package_manager}")

Copy link
Member

Choose a reason for hiding this comment

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

[..] Maybe we could check if the package manager is supported prior to the use of this property?

That's probably a good idea to give a nicer error message in one place. But I don't think I agree with returning default values unchecked. Imagine down the line adding a new package manager, then you have no guards that you've covered all bases as the default may not be appropriate.

Copy link
Contributor Author

@kevinobruno kevinobruno Feb 19, 2024

Choose a reason for hiding this comment

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

Got it, changed to raise when an unsupported package manager is provided: 3fe7c6e.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think there's abstractly a bit of tension here with being Pants being overly constraining.

For instance, what if someone publishes a pnpm-CLI-compatible cool-new-package-manager (similar to how uv pip is supposedly a drop-in replacement for pip https://github.com/astral-sh/uv)?

In theory, if we were less strict, we could have that drop in here and start working without needing pants changes... but, in practice, for this particular example, being strict sounds good: there's already 3 different styles of package manager, how will pants know that cool-new-package-manager should use pnpm vs. npm vs. yarn CLI? So yeah, exceptions plus dedicated pants support seems like the way forward 👍

Taking it one step further, there seems to be a lot of if self.package_manager == "<specific string>" calls here. Maybe this NodeJsProject type should validate the project_manager as an enum, on construction, to give errors eagerly and not require each separate method to handle it. (I don't think we should block this targeted bugfix on making that improvement, though.)

@kevinobruno kevinobruno force-pushed the yarn-v2-immutable-install branch from 3a732f8 to 3fe7c6e Compare February 19, 2024 14:50
Copy link
Contributor

@huonw huonw left a comment

Choose a reason for hiding this comment

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

Thanks for this!

}
)
projects = rule_runner.request(AllNodeJSProjects, [])
assert {project.immutable_install_args for project in projects} == {
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for writing some tests for this. For this particular test, I think it'd be good to match each particular package manager to the expected args, a bit different to the other tests in this file.

In particular, because this is a set, I think the old code would pass this this test, as the LHS and RHS will be {("clean-install,), ("install", "--frozen-lockfile"), ("install", "--immutable")} in for both old and new (just the counts of will change, but that's not captured).

A different way to phrase it might be by parametrizing for each of the package mangers:

@pytest.mark.parametrize(
    ("package_manager", "expected_args"),
    [ 
        (None, ("clean-install",)),
        ("[email protected]", ("install", "--frozen-lockfile"))
        ...
    ]
)
def test_immutable_install_args_property(package_manager: None | str, expected_args: tuple[str, ...], rule_runner: RuleRunner) -> None:
   # construct one package and check its args specifically

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thats an awesome suggestion, thanks.
Changed to use parametrize: 5177a9b

Comment on lines 80 to 87
def immutable_install_args(self) -> tuple[str, ...]:
if self.package_manager == "npm":
return ("clean-install",)
if self.package_manager == "yarn":
if nodesemver.satisfies(self.package_manager_version, "1.x"):
return ("install", "--frozen-lockfile")
return ("install", "--immutable")
return ("install", "--frozen-lockfile")
Copy link
Contributor

Choose a reason for hiding this comment

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

I think there's abstractly a bit of tension here with being Pants being overly constraining.

For instance, what if someone publishes a pnpm-CLI-compatible cool-new-package-manager (similar to how uv pip is supposedly a drop-in replacement for pip https://github.com/astral-sh/uv)?

In theory, if we were less strict, we could have that drop in here and start working without needing pants changes... but, in practice, for this particular example, being strict sounds good: there's already 3 different styles of package manager, how will pants know that cool-new-package-manager should use pnpm vs. npm vs. yarn CLI? So yeah, exceptions plus dedicated pants support seems like the way forward 👍

Taking it one step further, there seems to be a lot of if self.package_manager == "<specific string>" calls here. Maybe this NodeJsProject type should validate the project_manager as an enum, on construction, to give errors eagerly and not require each separate method to handle it. (I don't think we should block this targeted bugfix on making that improvement, though.)

@huonw huonw requested a review from tobni February 20, 2024 05:34
@kevinobruno kevinobruno requested review from kaos and huonw February 21, 2024 15:27
Copy link
Member

@kaos kaos left a comment

Choose a reason for hiding this comment

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

nice

Copy link
Contributor

@huonw huonw 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!

@huonw huonw merged commit 5e27ae9 into pantsbuild:main Feb 21, 2024
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
category:bugfix Bug fixes for released features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants