Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/.vitepress/cli_commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,9 @@ export const commands: { [key: string]: Command } = {
system: {
hide: false,
subcommands: {
brew: {
hide: false,
},
install: {
hide: false,
},
Expand Down
3 changes: 3 additions & 0 deletions docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ Can also use `MISE_NO_HOOKS=1`
- [`mise sync python [--pyenv] [--uv]`](/cli/sync/python.md)
- [`mise sync ruby [--brew]`](/cli/sync/ruby.md)
- [`mise system <SUBCOMMAND>`](/cli/system.md)
- [`mise system brew <SUBCOMMAND>`](/cli/system/brew.md)
- [`mise system brew tap [-n --dry-run] <TAP> [URL]`](/cli/system/brew/tap.md)
- [`mise system brew untap [-n --dry-run] <TAPS>…`](/cli/system/brew/untap.md)
- [`mise system install [FLAGS] [PACKAGE]…`](/cli/system/install.md)
- [`mise system status [-J --json] [--missing]`](/cli/system/status.md)
- [`mise system upgrade [FLAGS] [PACKAGE]…`](/cli/system/upgrade.md)
Expand Down
1 change: 1 addition & 0 deletions docs/cli/system.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ ever acted on when explicitly requested with `mise system install` (or

## Subcommands

- [`mise system brew <SUBCOMMAND>`](/cli/system/brew.md)
- [`mise system install [FLAGS] [PACKAGE]…`](/cli/system/install.md)
- [`mise system status [-J --json] [--missing]`](/cli/system/status.md)
- [`mise system upgrade [FLAGS] [PACKAGE]…`](/cli/system/upgrade.md)
Expand Down
15 changes: 15 additions & 0 deletions docs/cli/system/brew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!-- @generated by usage-cli from usage spec -->
# `mise system brew`

- **Usage**: `mise system brew <SUBCOMMAND>`
- **Source code**: [`src/cli/system/mod.rs`](https://github.com/jdx/mise/blob/main/src/cli/system/mod.rs)

Manage Homebrew taps used by system packages

These commands shell out to Homebrew and do not modify `mise.toml`. Use
`[system.brew.taps]` when you want tap sources shared in config.

## Subcommands

- [`mise system brew tap [-n --dry-run] <TAP> [URL]`](/cli/system/brew/tap.md)
- [`mise system brew untap [-n --dry-run] <TAPS>…`](/cli/system/brew/untap.md)
30 changes: 30 additions & 0 deletions docs/cli/system/brew/tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!-- @generated by usage-cli from usage spec -->
# `mise system brew tap`

- **Usage**: `mise system brew tap [-n --dry-run] <TAP> [URL]`
- **Source code**: [`src/cli/system/brew/tap.rs`](https://github.com/jdx/mise/blob/main/src/cli/system/brew/tap.rs)

Tap a Homebrew formula repository

## Arguments

### `<TAP>`

Tap name, e.g. `owner/repo`

### `[URL]`

Git URL for non-GitHub or otherwise custom taps

## Flags

### `-n --dry-run`

Print the command that would run without running it

Examples:

```
mise system brew tap railwaycat/emacsmacport
mise system brew tap acme/tools https://git.example.com/acme/homebrew-tools.git
```
26 changes: 26 additions & 0 deletions docs/cli/system/brew/untap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!-- @generated by usage-cli from usage spec -->
# `mise system brew untap`

- **Usage**: `mise system brew untap [-n --dry-run] <TAPS>…`
- **Aliases**: `remove`, `rm`
- **Source code**: [`src/cli/system/brew/untap.rs`](https://github.com/jdx/mise/blob/main/src/cli/system/brew/untap.rs)

Untap Homebrew formula repositories

## Arguments

### `<TAPS>…`

Tap name(s), e.g. `owner/repo`

## Flags

### `-n --dry-run`

Print the command that would run without running it

Examples:

```
mise system brew untap railwaycat/emacsmacport
```
44 changes: 40 additions & 4 deletions docs/system-packages/brew.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# brew <Badge type="warning" text="experimental" />

Homebrew formulae — **without requiring Homebrew to be installed**.
Homebrew formulae from `homebrew/core` — **without requiring Homebrew to be
installed**.

```toml
[system.packages]
Expand All @@ -17,7 +18,41 @@ prebuilt bottles from ghcr.io (verifying sha256 checksums), and performs the
same relocation, code-signing, and linking work `brew` does when pouring a
bottle. Formulae without a usable bottle are built from source, also without
Homebrew (see [Source formulae](#source-formulae)). mise never shells out to
`brew`.
`brew` for homebrew/core formulae.

Third-party taps are supported when Homebrew itself is installed. Tapped
formulae are delegated to a real `brew` command; use the same
fully-qualified formula name you would pass to `brew install`:

```toml
[system.packages]
"brew:railwaycat/emacsmacport/emacs-mac" = "latest"
```

For non-GitHub taps, or taps whose URL cannot be inferred by Homebrew, add a
tap source. This mirrors `[plugins]`: the key is the tap name and the value
is the git URL.

```toml
[system.brew.taps]
"acme/tools" = "https://git.example.com/acme/homebrew-tools.git"

[system.packages]
"brew:acme/tools/widget" = "latest"
```

Before installing or upgrading tapped formulae, mise runs `brew tap` for any
configured tap URL and then `brew update-if-needed` so the tap is current.

You can also manage taps imperatively, matching `mise plugins install` /
`mise plugins uninstall`: these commands shell out to Homebrew and do not
modify `mise.toml`.

```sh
mise system brew tap railwaycat/emacsmacport
mise system brew tap acme/tools https://git.example.com/acme/homebrew-tools.git
mise system brew untap acme/tools
```

This exists because shared-library packages — postgres, ffmpeg, imagemagick,
php — fundamentally can't be served by mise's per-project backends like
Expand Down Expand Up @@ -131,8 +166,9 @@ operation.

- **Formulae only.** Casks (GUI apps) and `brew services` are not
implemented.
- **No taps.** Third-party taps are Ruby code that requires Homebrew to
evaluate; only homebrew/core is supported.
- **Tapped formulae require Homebrew.** mise's direct bottle/source installer
is only for homebrew/core. Fully-qualified third-party tap formulae are
delegated to a real `brew` command.
- **Source builds cover the common formula shapes.** mise's formula shim
implements the widely-used subset of the DSL (see
[Source formulae](#source-formulae)); formulae that reach beyond it fail
Expand Down
45 changes: 45 additions & 0 deletions man/man1/mise.1
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,18 @@ Symlinks all ruby tool versions from an external tool into mise
\fBsystem\fR
[experimental] Manage system packages from `[system.packages]`, files
.TP
\fBsystem brew\fR
Manage Homebrew taps used by system packages
.TP
\fBsystem brew tap\fR
Tap a Homebrew formula repository
.TP
\fBsystem brew untap\fR
Untap Homebrew formula repositories
.RS
\fIAliases: \fRremove, rm
.RE
.TP
\fBsystem install\fR
Install missing system packages from `[system.packages]`, apply files
.RS
Expand Down Expand Up @@ -2777,6 +2789,39 @@ Symlinks all ruby tool versions from an external tool into mise
.TP
\fB\-\-brew\fR
Get tool versions from Homebrew
.SH "MISE SYSTEM BREW TAP"
Tap a Homebrew formula repository
.PP
\fBUsage:\fR mise system brew tap [OPTIONS] <TAP> [<URL>]
.PP
\fBOptions:\fR
.PP
.TP
\fB\-n, \-\-dry\-run\fR
Print the command that would run without running it
\fBArguments:\fR
.PP
.TP
\fB<TAP>\fR
Tap name, e.g. `owner/repo`
.TP
\fB<URL>\fR
Git URL for non\-GitHub or otherwise custom taps
.SH "MISE SYSTEM BREW UNTAP"
Untap Homebrew formula repositories
.PP
\fBUsage:\fR mise system brew untap [OPTIONS] <TAPS> ...
.PP
\fBOptions:\fR
.PP
.TP
\fB\-n, \-\-dry\-run\fR
Print the command that would run without running it
\fBArguments:\fR
.PP
.TP
\fB<TAPS>\fR
Tap name(s), e.g. `owner/repo`
.SH "MISE SYSTEM INSTALL"
Install missing system packages from `[system.packages]`, apply files
from `[system.files]` and edits from `[system.edits]`, and write macOS
Expand Down
31 changes: 31 additions & 0 deletions mise.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -2897,6 +2897,37 @@ defaults are user preferences written with `defaults write`. Unlike
ever acted on when explicitly requested with `mise system install` (or
`mise bootstrap`).
"""#
cmd brew subcommand_required=#true help="Manage Homebrew taps used by system packages" {
long_help #"""
Manage Homebrew taps used by system packages

These commands shell out to Homebrew and do not modify `mise.toml`. Use
`[system.brew.taps]` when you want tap sources shared in config.
"""#
cmd tap help="Tap a Homebrew formula repository" {
after_long_help #"""
Examples:

$ mise system brew tap railwaycat/emacsmacport
$ mise system brew tap acme/tools https://git.example.com/acme/homebrew-tools.git

"""#
flag "-n --dry-run" help="Print the command that would run without running it"
arg <TAP> help="Tap name, e.g. `owner/repo`"
arg "[URL]" help="Git URL for non-GitHub or otherwise custom taps" required=#false
}
cmd untap help="Untap Homebrew formula repositories" {
alias remove rm
after_long_help #"""
Examples:

$ mise system brew untap railwaycat/emacsmacport

"""#
flag "-n --dry-run" help="Print the command that would run without running it"
arg <TAPS>… help="Tap name(s), e.g. `owner/repo`" var=#true
}
}
cmd install help=#"""
Install missing system packages from `[system.packages]`, apply files
from `[system.files]` and edits from `[system.edits]`, and write macOS
Expand Down
32 changes: 32 additions & 0 deletions src/cli/system/brew/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use clap::Subcommand;
use eyre::Result;

mod tap;
mod untap;

/// Manage Homebrew taps used by system packages
///
/// These commands shell out to Homebrew and do not modify `mise.toml`. Use
/// `[system.brew.taps]` when you want tap sources shared in config.
#[derive(Debug, clap::Args)]
#[clap(verbatim_doc_comment)]
pub struct SystemBrew {
#[clap(subcommand)]
command: Commands,
}

#[derive(Debug, Subcommand)]
enum Commands {
Tap(tap::SystemBrewTap),
Untap(untap::SystemBrewUntap),
}

impl SystemBrew {
pub async fn run(self) -> Result<()> {
crate::config::Settings::get().ensure_experimental("mise system")?;
match self.command {
Commands::Tap(cmd) => cmd.run().await,
Commands::Untap(cmd) => cmd.run().await,
}
}
}
31 changes: 31 additions & 0 deletions src/cli/system/brew/tap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use eyre::Result;

/// Tap a Homebrew formula repository
#[derive(Debug, clap::Args)]
#[clap(verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)]
pub struct SystemBrewTap {
/// Tap name, e.g. `owner/repo`
tap: String,

/// Git URL for non-GitHub or otherwise custom taps
#[clap(value_hint = clap::ValueHint::Url)]
url: Option<String>,

/// Print the command that would run without running it
#[clap(long, short = 'n')]
dry_run: bool,
}

impl SystemBrewTap {
pub async fn run(self) -> Result<()> {
crate::system::packages::brew::tap(&self.tap, self.url.as_deref(), self.dry_run).await
}
}

static AFTER_LONG_HELP: &str = color_print::cstr!(
r#"<bold><underline>Examples:</underline></bold>

$ <bold>mise system brew tap railwaycat/emacsmacport</bold>
$ <bold>mise system brew tap acme/tools https://git.example.com/acme/homebrew-tools.git</bold>
"#
);
27 changes: 27 additions & 0 deletions src/cli/system/brew/untap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use eyre::Result;

/// Untap Homebrew formula repositories
#[derive(Debug, clap::Args)]
#[clap(verbatim_doc_comment, visible_aliases = ["remove", "rm"], after_long_help = AFTER_LONG_HELP)]
pub struct SystemBrewUntap {
/// Tap name(s), e.g. `owner/repo`
#[clap(required = true)]
taps: Vec<String>,

/// Print the command that would run without running it
#[clap(long, short = 'n')]
dry_run: bool,
}

impl SystemBrewUntap {
pub async fn run(self) -> Result<()> {
crate::system::packages::brew::untap(&self.taps, self.dry_run).await
}
}

static AFTER_LONG_HELP: &str = color_print::cstr!(
r#"<bold><underline>Examples:</underline></bold>

$ <bold>mise system brew untap railwaycat/emacsmacport</bold>
"#
);
3 changes: 2 additions & 1 deletion src/cli/system/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ impl SystemInstall {
}
system::packages_from_config(&config)
} else {
system::packages_from_specs(&self.packages)?
let config = Config::get().await?;
system::packages_from_specs_with_config(&self.packages, Some(&config))?
};
// explicit packages or a --manager filter narrow the run to those
// packages; files, edits, and defaults are part of the "apply
Expand Down
6 changes: 6 additions & 0 deletions src/cli/system/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use clap::Subcommand;
use eyre::Result;

#[cfg(unix)]
mod brew;
pub(super) mod driver;
pub(super) mod install;
mod status;
Expand Down Expand Up @@ -30,6 +32,8 @@ pub struct System {

#[derive(Debug, Subcommand)]
enum Commands {
#[cfg(unix)]
Brew(brew::SystemBrew),
Install(install::SystemInstall),
Status(status::SystemStatus),
Upgrade(upgrade::SystemUpgrade),
Expand All @@ -39,6 +43,8 @@ enum Commands {
impl System {
pub async fn run(self) -> Result<()> {
match self.command {
#[cfg(unix)]
Commands::Brew(cmd) => cmd.run().await,
Commands::Install(cmd) => cmd.run().await,
Commands::Status(cmd) => cmd.run().await,
Commands::Upgrade(cmd) => cmd.run().await,
Expand Down
Loading
Loading