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

Package management CLI, part 1 #2146

Merged
merged 5 commits into from
Feb 19, 2025
Merged

Package management CLI, part 1 #2146

merged 5 commits into from
Feb 19, 2025

Conversation

jneem
Copy link
Member

@jneem jneem commented Jan 24, 2025

This is the first part of the package manager CLI, hidden behind a "experimental-package" feature flag. It initially supports only git and path dependencies.

  • Allow the git repo realization to check for up-to-date git repos using an existing lock file.
  • Thoroughly test the manifest deserialization
  • Test the is-lock-file-up-to-date logic
  • Integration tests involving the cli
  • Progress messages during git operations

@jneem jneem force-pushed the npm-without-index branch 2 times, most recently from ca59874 to 19b1cb1 Compare January 28, 2025 06:46
@jneem jneem marked this pull request as ready for review January 30, 2025 12:05
@jneem
Copy link
Member Author

jneem commented Jan 30, 2025

I haven't added a progress bar to the git downloads, but I think this is ready for a first look.

This does auto-updating of the lock-file, like cargo but unlike npm. I haven't yet implemented re-using the standard library across evaluation of multiple manifests, but I think it shouldn't be too hard (certainly easier than the general case of #2140 where you want to re-use work across nickel invocations).

@jneem jneem requested a review from yannham January 30, 2025 12:08
Copy link
Member

@yannham yannham left a comment

Choose a reason for hiding this comment

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

Just a partial review for now. Note to self: next item on the stack is package/src/lib.rs.

cli/src/input.rs Outdated Show resolved Hide resolved
cli/src/input.rs Outdated Show resolved Hide resolved
cli/src/input.rs Show resolved Hide resolved
Lock {
/// The path at which to write the lock file.
///
/// Defaults to the filename `package.ncl.lock` in the same directory
Copy link
Member

Choose a reason for hiding this comment

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

It's a detail, but I think usually package managers don't keep the .ncl equivalent in the lock file: flake.nix -> flake.lock, Cargo.toml -> Cargo.lock, Pipefile -> Pipefile.lock, foo_bar.gemspec -> Gemfile.lock, etc.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right, but I was worried about colliding with npm's (the other npm 😉) package.lock. Maybe it's better to avoid a collision by having a more nickel-specific name than "package", though...

Copy link
Member

Choose a reason for hiding this comment

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

I see. Yes, maybe having a more specific name than "package" could be helpful in general, if only there are projects with both an NPM manifest an a Nickel manifest (or even imagine that you handle everything with Nickel, so you would like to write your "package.json" as a "package.ncl" instead and generate it).

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok, I realized while fiddling with the website that actually npm uses "package-lock.json", not "package.lock". So there would be no conflict to go with "package.lock", but maybe a more distinctive name is better anyway. What do you think about Nickel.ncl/Nickel.lock? Or Nickelfile.ncl/Nickelfile.lock?

cli/src/package.rs Outdated Show resolved Hide resolved
[package]
name = "nickel-lang-package"
description = "The Nickel Package Manager (npm)"
version = "0.1.0"
Copy link
Member

Choose a reason for hiding this comment

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

It's not very important for this PR, but I wonder if we shouldn't make it target either nickel-lang-core or the Nickel version (nickel-lang-cli) (probably the latter, since it does provide new CLI commands), to avoid the proliferation of uncorrelated versions. On the other hand, this removes the possibility of updating those crates independently.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it makes some sense to have the version in sync with nickel-lang-cli. It does require a little bit of manual intervention to keep them in sync: I can put version.workspace = true in package/Cargo.toml, but I need to repeat the "1.9.0" in the workspace's dependencies list.

Copy link
Member

Choose a reason for hiding this comment

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

The release scripts is capable of handling that (we already do it for other crates like NLS). Though we should update the script as part of this PR, to make sure everything is consistent.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right, but none of the other non-"independent" crates are a dependency of other crates in the workspace. Naively adding package to the list of crates gives

 -- Updating cross-dependencies...
  -- Patching cross-dependency lsp-harness in ./Cargo.toml to version 0.1.0
  -- Patching cross-dependency nickel-lang-core in ./Cargo.toml to version 0.11.0
  -- Patching cross-dependency nickel-lang-package in ./Cargo.toml to version {
  "workspace": true
}
sed: -e expression #1, char 87: unterminated `s' command
++ Unexpected exit. Cleaning up...

(Also, I guess we should add git and vector)

package/src/error.rs Show resolved Hide resolved
package/src/error.rs Outdated Show resolved Hide resolved
package/src/error.rs Outdated Show resolved Hide resolved
package/src/error.rs Outdated Show resolved Hide resolved
@jneem jneem force-pushed the npm-without-index branch from 8fdcef9 to 32a68a0 Compare February 11, 2025 10:23
Copy link
Member

@yannham yannham left a comment

Choose a reason for hiding this comment

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

I can't pretend I followed all the details, but it looks good after a pass. The extensive tests are also great and reassuring. I just wonder if all the tests using git commands explicitly wouldn't break tests on Windows ?

package/src/lib.rs Outdated Show resolved Hide resolved
package/src/lib.rs Outdated Show resolved Hide resolved
package/src/lib.rs Outdated Show resolved Hide resolved
if self.id == 0 {
write!(f, "{}", self.name)
} else {
write!(f, "{} {}", self.name, self.id)
Copy link
Member

Choose a reason for hiding this comment

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

Naive question: is it possible that a package name has a space in the name?

Copy link
Member Author

Choose a reason for hiding this comment

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

You can't use such a package because the import syntax for packages requires the package name to be an identifier ("identifier" in the sense of the nickel lexer). But it's a good point, nickel_lang_package should check this.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've added this check to std.package.Manifest

package/src/lock.rs Outdated Show resolved Hide resolved
package/src/manifest.rs Outdated Show resolved Hide resolved
package/src/snapshot.rs Outdated Show resolved Hide resolved
package/src/snapshot.rs Outdated Show resolved Hide resolved
package/src/version.rs Show resolved Hide resolved

// The rust stdlib doesn't have anything for recursively copying a directory. There are
// some crates for that, but it's easier just to shell out.
run(Command::new("cp")
Copy link
Member

Choose a reason for hiding this comment

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

Wouldn't that break the CI on Windows?

jneem and others added 2 commits February 18, 2025 11:40
Enables package management for git and path packages, when the
package-experimental feature is enabled.

Co-authored-by: Yann Hamdaoui <[email protected]>
@jneem jneem force-pushed the npm-without-index branch from 3196a84 to d4f86e2 Compare February 18, 2025 05:35
@jneem
Copy link
Member Author

jneem commented Feb 18, 2025

Ok, I think the main outstanding thing is to settle on a name for the manifest and lock files.

I'm pretty sure the windows CI is working ok with git and cp -- the CI already caught various small differences between git on windows and linux (e.g. it doesn't auto-detect the username on windows, so you need to provide it), so I'm pretty sure that it's running and passing in CI.

in
let
semver_req_re = "(%{partial_semver_re})|(%{semver_equals_req_re})",
IdentKeys =
Copy link
Member

Choose a reason for hiding this comment

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

Should we expose IdentKeys as well? While it feels like a small self-explanatory contract, exposing it makes it discoverable, re-usable, and queri-able (if we add documentation). Though it also makes it part of the backward-compatibility. So I don't have a strong opinion either way.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've been playing with json schema lately, and it seems pretty common to have a dict with a regex for the key names. What if we exposed, say, std.record.KeysMatch which could get used like

{
  foo = 1
} | std.record.KeysMatch "^[a-z]+*"

(basically, I'm worried that IdentKeys is a bit too specific to be worth making public)

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I even think CUE provide built-in syntax for that. Nitpick: should we use FieldMatches instead to follow Nickel's jargon? Although the issue with field is that it designates both the content and the name. Could use FieldNameMatches, but it's longer. That being said, we have std.record.fields which return field names, so FieldMatches might be reasonable.

Fair for IdentKeys, we can keep it local for now.

@yannham
Copy link
Member

yannham commented Feb 18, 2025

I'm pretty sure the windows CI is working ok with git and cp -- the CI already caught various small differences between git on windows and linux (e.g. it doesn't auto-detect the username on windows, so you need to provide it), so I'm pretty sure that it's running and passing in CI.

I see, this makes our life simpler (it was just a random thought - I haven't used Windows in ages so I wondered if the CLI argument syntax was different, I remember using /).

As per the bikeshedding for the file name:

  • nickel.ncl/nickel.lock: clear, no risk of conflict with other tools, small name. The only disadvantage is that we tied it to the name of the language, so it might not be 100% clear for a newcomer that this is related to package management? For example if we ever implement file-based configuration for the interpreter itself, maybe it could also be nickel.ncl?
  • nickelfile.ncl / nickelfile.lock: I'm not sure why Pip and Gem adds the file prefix, because it doesn't seem to add much meaning.

Maybe something with both nickel and another word that indicates it's related to package management would work as well? What do yo think about e.g. nickel-pkg{.ncl,.lock}, nickel-package{.ncl, .lock}, nickel-manifest{.ncl,.lock} or something along those lines? Also, we can post-pone the bikeshedding and merge this first: it's an experimental feature, and 1.10 has just been released, so we have time.

@jneem
Copy link
Member Author

jneem commented Feb 18, 2025

nickel-pkg.ncl sounds good to me, but I wonder if we should capitalize it? It seems to be common, and at least cargo explicitly explains their rationale.

@yannham
Copy link
Member

yannham commented Feb 18, 2025

I initially didn't like the inconsistency of upper casing this specific file but I think their rationale makes sense. Let's start with Nickel-pkg.%{extension} and review later if needed?

@jneem jneem added this pull request to the merge queue Feb 19, 2025
Merged via the queue into master with commit 02439ec Feb 19, 2025
5 checks passed
@jneem jneem deleted the npm-without-index branch February 19, 2025 01:40
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.

2 participants