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

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open

Package management CLI, part 1 #2146

wants to merge 26 commits into from

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 added 3 commits January 24, 2025 16:01
Copy over the cli changes

Fix docs

Update tests for the manifest changes

Switch to libtest-mimic for package tests

Remove an unwrap

Knock out some more easy TODOs

Remove the index
@jneem jneem force-pushed the npm-without-index branch from 0cf4b4c to 336b132 Compare January 28, 2025 03:02
@jneem jneem force-pushed the npm-without-index branch 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
Comment on lines +92 to +93
// If the manifest path isn't given, where should we start looking
// for it? If there's only one file, we start with its parent directory.
Copy link
Member

Choose a reason for hiding this comment

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

When you say the parent directory, this means the directory where this file is located, not one the one above that, right?

// If there are multiple files, finding a good heuristic
// is harder. For now, we take the parent directory if it's
// unique and otherwise we require an explicit manifest
// path.
Copy link
Member

Choose a reason for hiding this comment

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

One possibility would be to look for the common ancestor. But I can see how it might be wrong in some cases (if the files are far apart), so playing safe by explicitly requiring a manifest path sounds good 👍

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

}
}

fn print_package_map(map: &PackageMap) {
Copy link
Member

Choose a reason for hiding this comment

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

Would that make sense to make this a Display instance instead, or are there other considerations against this idea?

[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.

}
}

impl std::fmt::Display for Error {
Copy link
Member

Choose a reason for hiding this comment

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

I haven't tried, but I suppose this means that package management errors are displayed in a way that doesn't follow the other errors look-and-feel? It doesn't have to be done now, but would implementing IntoDiagnostics instead and re-use the Nickel error reporting infrastructure work as well?

Error::InternalManifestError { path, msg } => {
write!(
f,
"internal error reading the manifest at {}; this is a bug in nickel: {msg}",
Copy link
Member

Choose a reason for hiding this comment

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

TODO: reuse the same internal error message that is used in Nickel-lang core. I think there is also a note saying to report the error to the issue tracker.

}
Error::NoPackageRoot { path } => write!(
f,
"tried to import a relative path ({}), but we have no reference",
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I would understand this error message without context.

Copy link
Member

Choose a reason for hiding this comment

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

Addendum: I think "reference" wasn't clear initially. I find "root" better, or "base" maybe, for what it's worth

Error::LockFileDeserialization { path, error } => {
write!(f, "lock file {} is invalid: {error}", path.display())
}
Error::NoManifestParent => {
Copy link
Member

Choose a reason for hiding this comment

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

What does this error correspond to? Is that possible for a regular file to have no parent?

jneem and others added 2 commits February 6, 2025 18:07
Co-authored-by: Yann Hamdaoui <[email protected]>
Co-authored-by: Yann Hamdaoui <[email protected]>
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