juliaup icon indicating copy to clipboard operation
juliaup copied to clipboard

Ship a CLI for Pkg.jl

Open MilesCranmer opened this issue 4 months ago • 12 comments

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 julia and juliaup commands

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!

MilesCranmer avatar Aug 23 '25 23:08 MilesCranmer

@davidanthoff Any idea what these CI errors might be from? It seems it is not deterministic

MilesCranmer avatar Aug 25 '25 13:08 MilesCranmer

@fredrikekre might be a good reviewer from a product perspective here?

IanButterworth avatar Aug 25 '25 14:08 IanButterworth

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 add or whatever.

I think I prefer the latter personally.

cjdoris avatar Aug 25 '25 14:08 cjdoris

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

MilesCranmer avatar Aug 25 '25 14:08 MilesCranmer

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.

MilesCranmer avatar Aug 25 '25 14:08 MilesCranmer

I renamed it to juliapkg for the time being, to match juliaup.

MilesCranmer avatar Aug 25 '25 14:08 MilesCranmer

Btw, regarding

Make it all a subcommand of juliaup, such as juliaup pkg add or 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

MilesCranmer avatar Aug 26 '25 12:08 MilesCranmer

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.

IanButterworth avatar Aug 26 '25 13:08 IanButterworth

I merged the latest changes from https://github.com/JuliaLang/juliaup/pull/1232 to this branch just FYI

MilesCranmer avatar Aug 26 '25 15:08 MilesCranmer

Shouldn't this just be an app in Pkg perhaps?

fredrikekre avatar Aug 26 '25 16:08 fredrikekre

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.

MilesCranmer avatar Aug 26 '25 16:08 MilesCranmer

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.

MilesCranmer avatar Aug 26 '25 17:08 MilesCranmer