# Installables CLI design

**URL:** https://discourse.nixosstag.fcio.net/t/installables-cli-design/38601
**Category:** Nix
**Created:** [January 21, 2024, 12:22pm UTC](https://discourse.nixosstag.fcio.net/t/installables-cli-design/38601 "2024-01-21T12:22:45Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![fricklerhandwerk](https://discourse.nixosstag.fcio.net/user_avatar/discourse.nixosstag.fcio.net/fricklerhandwerk/32/2029_2.png) [@fricklerhandwerk](https://discourse.nixosstag.fcio.net/u/fricklerhandwerk)
#### Post date: [January 21, 2024, 12:22pm UTC](https://discourse.nixosstag.fcio.net/t/installables-cli-design/38601/1 "2024-01-21T12:22:45Z")

</div>

@thufschmitt drafted this set of proposals based on [2024-01-15 Nix team meeting minutes #115](https://discourse.nixosstag.fcio.net/t/2024-01-15-nix-team-meeting-minutes-115/38297), and it was edited by maintainers. Posting it here to continue the discussion in the open.

> **This is a [Wiki post](https://meta.discourse.org/t/what-is-a-wiki-post/30801) that anyone can edit to add alternatives and considerations** (or just fix typos)

# #1: global `--file` argument

Current semantics: If the command-line contains `--file foo.nix`, then all the installables are interpreted as attribute paths in `foo.nix` (+ auto-calling as needed).  
Same for `--expr someNixExpression`.

## Advantages

- No change
- Lightweight when using several packages from the same file or expression (`--file '<nixpkgs>' hello gcc firefox`)

## Drawbacks

- Doesn’t allow mixing the source: If some installables come from a file/expr, then all the installables must come from that file/expr
- Not really intuitive

# #2: “stateful” `--file` and `--flake` modifiers

`--file foo.nix` on the command-line causes all the subsequent arguments to be interpreted as attribute paths in `foo.nix`, until another option (`--`-ed argument is encountered). Likewise for `--expr someNixExpression`. A third flag `--flake` is added for consistency and to negate the effect of a previous `--file` or `--expr`.

## Advantages

- Partially backwards-compatible: `--file '<nixpkgs>' hello gcc firefox` keeps the same meaning
- Rather terse in most cases
- Adds some symmetry between the flake and non-flake installables
- Can combine e.g. `--expr` and `--flake` in one invocation. Could be relevant for possible `--apply` or `--let` flags (scripts)

## Drawbacks

- Stateful cli parsing. Both annoying to implement and hard to reason about for the user (cf `find`)
  - @roberth: why?
  - @fricklerhandwerk: It’s actually fairly easy to implement and potential for confusion is low. It’s even easy to document: `--<type> <source> [<attr>]...` where the sequence of attrs is terminated by `--<flag>` or EOL

- The symmetry between flake and non-flake installables is only superficial because they are still handled quite differently
  - @roberth: I suppose `--flake` uses the registry as its “root”, `--expr` uses an expression as its root
    - @fricklerhandwerk: the registry is just a set of aliases for full flake references (which are the actual root), so not really different from channels if we finally abandon the global registry idea.

# #3: Double hash separator

Interprets `foo.nix##hello` as getting the `hello` attribute from the file `foo.nix`.

### Advantages

- Terse
- Puts the file installables (nearly) at the same level as flakes

### Drawbacks

- Hard to discover and document
- Confusing (“why do I need one hash here and two there?”)
- Doesn’t work for expr installables

# #4: one-shot `--file` modifier

Make `--expr` a 2-arguments flag: `--expr foo.nix hello` is the installable corresponding to the value at path `hello` in `foo.nix` (similar to #2, but allowing only a single attribute path).

### Advantages

- Simple and stupid
- Partially compatible with the current behavior (similar to #2, but more restricted)
- Conservative choice: can be made more flexible later if deemed worth it

### Drawbacks

- Verbose when getting several installables from a single file/expression

# #5: URI-like prefixes

URI-like in the sense of having a “type”, followed by a colon, followed by a payload of sorts. Attribute syntax is `#`, except for `expr:`, which uses the expression language for this purpose. `expr:` can return multiple installables by returning a list.

Example: `nix build expr:'(import ./. {}).foo' file:./docs.nix#docsHtml flake:nixpkgs#hello`

### Advantages

- Each argument is self-contained; parsing not dependent on preceding options
- No ambiguity as to whether e.g. `--expr` affects one or many upcoming args
- Single strings are easy to pass around in bash, without breaking whitespace
- Also easy to accept them in options such as `--arg <name> <installable>`
- File syntax can use `#` without ambiguity

### Drawbacks

- Similarity to flakerefs might be confusing.
  - Make them disjoint?
    - e.g. `expr:`, `nixfile:`, `flake:` are not flakerefs

- It’s yet another language within the command line arguments

# Open questions

- How will these interact with possible `--apply`/`--let` flags? Currently `--apply` is like `builtins.map`, but that is not suitable for heterogenous inputs. Example use case: image generation with three inputs: a NixOS configuration, an image builder, and parameters for the image, such as disk size, format version, etc.

- Rename “installables”? Some arguments that we now call “installables” are arguably not installable. Examples: a flake, a store path that’s a tarball file, a `.drv` file. Furthermore, “installable” distorts the meaning of information retrieval commands that need precise meaning, such as path-info. We might want to name them for their role in the system, which is not as a “domain object” but merely as an “application” syntax for referencing things.

---

<div class="post-metadata">

### Author: ![RaitoBezarius](https://discourse.nixosstag.fcio.net/user_avatar/discourse.nixosstag.fcio.net/raitobezarius/32/1835_2.png) [@RaitoBezarius](https://discourse.nixosstag.fcio.net/u/RaitoBezarius)
#### Post date: [January 21, 2024, 3:21pm UTC](https://discourse.nixosstag.fcio.net/t/installables-cli-design/38601/2 "2024-01-21T15:21:27Z")

</div>

> [@fricklerhandwerk](#):
>
> # #3: Double hash separator

Just a note here to say using `#` would force anyone on zsh to quote the string all the time (which is already the case with Flakes), if the goal is to be terser, it seems to me better to pay attention to unexpected meaning in popular shells.

---

<div class="post-metadata">

### Author: ![fricklerhandwerk](https://discourse.nixosstag.fcio.net/user_avatar/discourse.nixosstag.fcio.net/fricklerhandwerk/32/2029_2.png) [@fricklerhandwerk](https://discourse.nixosstag.fcio.net/u/fricklerhandwerk)
#### Post date: [January 21, 2024, 3:25pm UTC](https://discourse.nixosstag.fcio.net/t/installables-cli-design/38601/3 "2024-01-21T15:25:24Z")

</div>

I just tested this in zsh, and `nix run nixpkgs#hello` works just fine. Regardless, I find the `##` proposal rather suboptimal.

---

<div class="post-metadata">

### Author: ![RaitoBezarius](https://discourse.nixosstag.fcio.net/user_avatar/discourse.nixosstag.fcio.net/raitobezarius/32/1835_2.png) [@RaitoBezarius](https://discourse.nixosstag.fcio.net/u/RaitoBezarius)
#### Post date: [January 21, 2024, 3:42pm UTC](https://discourse.nixosstag.fcio.net/t/installables-cli-design/38601/4 "2024-01-21T15:42:44Z")

</div>

```auto
❯ nix run nixpkgs#hello
zsh: no matches found: nixpkgs#hello

```

I suppose you didn’t enable any of the classical fancy glob options of zsh.

---

<div class="post-metadata">

### Author: ![fricklerhandwerk](https://discourse.nixosstag.fcio.net/user_avatar/discourse.nixosstag.fcio.net/fricklerhandwerk/32/2029_2.png) [@fricklerhandwerk](https://discourse.nixosstag.fcio.net/u/fricklerhandwerk)
#### Post date: [January 21, 2024, 4:04pm UTC](https://discourse.nixosstag.fcio.net/t/installables-cli-design/38601/5 "2024-01-21T16:04:57Z")

</div>

Yes, it was just vanilla `zsh` as it comes out of `nix-shell -p`.

---

<div class="post-metadata">

### Author: ![Ericson2314](https://discourse.nixosstag.fcio.net/user_avatar/discourse.nixosstag.fcio.net/ericson2314/32/316_2.png) [@Ericson2314](https://discourse.nixosstag.fcio.net/u/Ericson2314)
#### Post date: [January 21, 2024, 6:48pm UTC](https://discourse.nixosstag.fcio.net/t/installables-cli-design/38601/6 "2024-01-21T18:48:58Z")

</div>

It could perhaps even be a single `#`, and just check for `default.nix` if no `flake.nix` is found.

What I want to avoid is non-flakes being much worse ergonomically than flakes.

---

<div class="post-metadata">

### Author: ![bew](https://discourse.nixosstag.fcio.net/user_avatar/discourse.nixosstag.fcio.net/bew/32/2091_2.png) [@bew](https://discourse.nixosstag.fcio.net/u/bew)
#### Post date: [January 23, 2024, 10:40am UTC](https://discourse.nixosstag.fcio.net/t/installables-cli-design/38601/7 "2024-01-23T10:40:15Z")

</div>

You can have fancy globs while allowing `foo#bar` using `setopt no_nomatch` to disable the ‘no match’ error when there are no matches and pass the argument verbatim
