Skip to content

Conversation

MilesCranmer
Copy link
Member

@MilesCranmer MilesCranmer commented Aug 23, 2025

This PR adds a juliapkg binary shipped with juliaup that provides a shell CLI for Julia's Pkg manager. See JuliaLang/julia#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 MilesCranmer changed the title Create a CLI for Pkg.jl: jlpkg Ship a CLI for Pkg.jl: jlpkg Aug 24, 2025
@MilesCranmer MilesCranmer changed the title Ship a CLI for Pkg.jl: jlpkg Ship a CLI for Pkg.jl Aug 24, 2025
Copy link
Member Author

Choose a reason for hiding this comment

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

FYI - this file is 99% a git mv of the src/bin/julialauncher.rs. If you look in the git history you can see this, but for some reason, Github displays it as a new file.

@MilesCranmer
Copy link
Member Author

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

@IanButterworth
Copy link
Member

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

@cjdoris
Copy link

cjdoris commented Aug 25, 2025

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.

@MilesCranmer
Copy link
Member Author

MilesCranmer commented Aug 25, 2025

Yes please!

Note I also discuss the motivations in JuliaLang/julia#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:

let exit_code = juliaup::julia_launcher::run_julia_launcher(new_args, None)?;

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
Copy link
Member Author

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
Copy link
Member Author

MilesCranmer commented Aug 25, 2025

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

@MilesCranmer
Copy link
Member Author

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

@IanButterworth
Copy link
Member

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.

@MilesCranmer
Copy link
Member Author

I merged the latest changes from #1232 to this branch just FYI

@fredrikekre
Copy link
Member

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

@MilesCranmer
Copy link
Member Author

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
Copy link
Member Author

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 MilesCranmer mentioned this pull request Aug 27, 2025
@xgdgsc xgdgsc mentioned this pull request Aug 28, 2025
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.

4 participants