Ship a CLI for Pkg.jl
This PR adds a juliapkg binary shipped with juliaup that provides a shell CLI for Julia's Pkg manager. See https://github.com/JuliaLang/julia/issues/59370 for the motivation and proposal.
I realized it's actually fairly simple to implement this in juliaup, because you get:
- Shared logic with the julialauncher.rs code
- Nice clap.rs help menu and shell completions
- Automatic Julia version management (
+1.11,+release, etc.) - No separate installation needed for users who already are installing with juliaup
- Consistent CLI experience with
juliaandjuliaupcommands
To try it out, run the following:
cargo install --git https://github.com/MilesCranmer/juliaup.git --branch jlpkg \
--features juliapkg \
--bin juliapkg
Here are some usage examples (again, this is just Pkg.jl REPLMode):
# Basic usage
juliapkg add DataFrames
juliapkg test
juliapkg instantiate
# With specific Julia version
juliapkg +1.11 add LinearAlgebra
juliapkg +release test
Here's the full clap.rs help menu:
> Julia package manager
Usage: juliapkg [OPTIONS] [COMMAND]
juliapkg [OPTIONS] [COMMAND] [ARGS]...
Julia options can be passed before the command:
+<channel> Select Julia channel (e.g., +1.10, +release)
--project[=<path>] Set project directory
[...] Other Julia flags are also supported
Commands:
add Add packages to project
build Run the build script for packages
compat Edit compat entries in the current Project
develop Clone the full package repo locally for development [aliases: dev]
free Free pinned or developed packages
generate Generate files for packages
gc Garbage collect packages not used for a significant time
instantiate Download and install all artifacts in the manifest
pin Pin packages
precompile Precompile packages
remove Remove packages from project [aliases: rm]
registry Registry operations
resolve Resolve versions in the manifest
status Show project status [aliases: st]
test Run tests for packages
update Update packages in manifest [aliases: up]
why Explains why a package is in the dependency graph
completions Generate shell completion scripts
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
The implementation is quite simple since it borrows much of the logic from julialauncher.rs, and then forwards the user-provided commands to Pkg.REPLMode.pkgstr. This means there is minimal maintenance burden and it automatically stays in sync with Pkg.jl!
@davidanthoff Any idea what these CI errors might be from? It seems it is not deterministic
@fredrikekre might be a good reviewer from a product perspective here?
Cool! Maybe too early for bike shedding but I'm not personally a fan of the name jlpkg. Can I suggest either:
- Call it juliapkg.
- Make it all a subcommand of juliaup, such as
juliaup pkg addor whatever.
I think I prefer the latter personally.
Yes please!
Note I also discuss the motivations in https://github.com/JuliaLang/julia/issues/59370. I think the main benefit of putting jlpkg into juliaup is that you get to use the same code for managing the underlying Julia binary and channel, and don't need a separate install step. For example:
juliapkg +1.10 test
Is basically identical to julia +1.10 --startup-file=no --project=. -e 'using Pkg; pkg"test"'
This is equivalent at a low level, because this juliapkg call is fundamentally using the same code as src/bin/julialauncher.rs:
https://github.com/JuliaLang/juliaup/blob/f2836469fe921effeafb4b3fdb770de43f44082f/src/bin/jlpkg.rs#L519
P.S., we also get shell completions for every shell which is an added benefit of putting the command spec in clap:
> juliapkg completions --help
Generate shell completion scripts
Usage: juliapkg completions <SHELL>
Arguments:
<SHELL> [possible values: bash, elvish, fish, nushell, power-shell, zsh]
Options:
-h, --help Print help
I am a fan of juliapkg, I like that it fits the pattern of "julia update" => juliaup; "julia package" => juliapkg. Though I think juliaup pkg might be a tad long. Especially comparing to the other package managers. For example, rust is not rustup pkg add, even though rustup is the equivalent. In rust it is cargo add.
I renamed it to juliapkg for the time being, to match juliaup.
Btw, regarding
Make it all a subcommand of
juliaup, such asjuliaup pkg addor whatever.
I think the naming semantics aren't quite right: juliaup ~ julia update, therefore juliaup pkg add ~ julia update package add. Which is strange.
I prefer your juliapkg suggestion due of this, because juliapkg add ~ julia package add. Much nicer in my view
juliapkg sounds good to me. Then perhaps we could recommend or even optionally prompt to link it to something shorter like pkg if the user wants.
I merged the latest changes from https://github.com/JuliaLang/juliaup/pull/1232 to this branch just FYI
Shouldn't this just be an app in Pkg perhaps?
Shouldn't this just be an app in Pkg perhaps?
Maybe?
However, note that there are a few julia flag defaults that are inconvenient for a Pkg.jl session. The juliapkg binary here alters the default flags: --project=. --startup-file=no --threads=auto which is an advantage of having a layer of code sitting in front of julia.
The other drawback is a Pkg app would miss out on clap.rs-generated shell completions.
If Pkg apps eventually reach feature parity for these things, then a juliapkg CLI could just be a shell script that is aliased to use the app instead.
As the Pkg apps interface matures, one possibility could be for juliapkg to forward to julia (flags...) -m Pkg ... instead of julia (flags...) -e 'using Pkg; Pkg.REPLMode.pkgstr("...")'. At the moment a module invoked with -m can't adjust default flags like --project=., so I'm not sure how far a julia -m Pkg-by-default approach could go.
It also seems there might still be advantages to having a dedicated command installed by juliaup, since that would give shell completions (via clap.rs) and the ergonomics of a single entry point, even if the forwarding layer becomes thinner.