brew
Homebrew 配方和 cask —— 无需安装 Homebrew。
[bootstrap.packages]
"brew:postgresql@17" = "latest"
"brew:ffmpeg" = "latest"
"brew:imagemagick" = "latest"
"brew-cask:firefox" = "latest"mise 直接将 homebrew/core 的配方安装到 标准的 Homebrew 前缀中——arm64 macOS 上是 /opt/homebrew, Linux 上是 /home/linuxbrew/.linuxbrew。它会从 formulae.brew.sh API 获取元数据,解析运行时依赖闭包,从 ghcr.io 下载 预编译的 bottles(会验证 sha256 校验和),并执行与 brew 倒入 bottle 时相同的重定位、代码签名和链接工作。没有可用 bottle 的配方也会从源代码构建, 同样不需要 Homebrew(见 源代码配方)。mise 对 homebrew/core 配方从不调用 brew。
当第三方 tap 发布了 Homebrew API 元数据(api/formula/<name>.json 或 api/cask/<token>.json)时,也可以直接支持。请使用与传给 Homebrew 相同的完整限定名称:
[bootstrap.packages]
"brew:railwaycat/emacsmacport/emacs-mac" = "latest"
"brew-cask:owner/tap/app" = "latest"对于无法从 GitHub URL 推断的 tap,请添加一个 tap 源。这与 [plugins] 类似:键是 tap 名称,值是 GitHub git URL。
[bootstrap.brew.taps]
"acme/tools" = "https://github.com/acme/homebrew-tools.git"
[bootstrap.packages]
"brew:acme/tools/widget" = "latest"
"brew-cask:acme/tools/widget-app" = "latest"mise bootstrap packages brew tap 和 mise bootstrap packages brew untap 会管理 mise.toml 中的 [bootstrap.brew.taps];它们不会修改 Homebrew 安装。由于 mise 需要直接访问生成的 API 元数据的原始内容,目前不支持 非 GitHub 的 tap。
mise bootstrap packages brew tap railwaycat/emacsmacport
mise bootstrap packages brew tap acme/tools https://github.com/acme/homebrew-tools.git
mise bootstrap packages brew untap acme/toolsCask
Cask 使用 brew-cask: 管理器。mise 直接从 Homebrew cask API(或 tap API 元数据)获取 cask 元数据,下载制品,在 cask 提供 sha256 时验证其 sha256,提取归档,并将应用程序包安装到 /Applications,同时将版本记录在 <prefix>/Caskroom 下。与 Homebrew 一样,mise 会将每个应用程序包移动到 /Applications,并在其版本化的 Caskroom 路径留下一个符号链接,而不是在该处保留应用程序的第二份副本。
[bootstrap.packages]
"brew-cask:firefox" = "latest"
"brew-cask:homebrew/cask/visual-studio-code" = "latest"覆盖应用程序目录
默认情况下,app 制品会安装到 /Applications,与 Homebrew 的行为一致。设置 MISE_BREW_CASK_OPT_APPDIR 环境变量可将其安装到其他位置——例如无需提升权限、用户可写的 ~/Applications:
MISE_BREW_CASK_OPT_APPDIR="$HOME/Applications" mise bootstrap packages apply brew-cask:firefox该值必须是绝对路径,不能包含 ..,并且不能解析为文件系统根目录。在使用前,它会被解析为真实路径(会跟随符号链接),因此会作为 mise 创建的应用程序链接的固定包含边界。空值会被忽略,并回退到 /Applications。设置覆盖项后,目标为默认 /Applications 的 cask(大多数 cask 都是如此)会被重新定位到覆盖目录,同时保留 cask 请求的任何子目录;cask 固定在 $HOMEBREW_PREFIX/Applications 下的目标会留在 Homebrew 前缀中,绝不会被重新定位。这与 Homebrew 自身的 --appdir 安装选项一致。 要接管已安装在 cask 目标位置的应用程序,请使用带有 adopt = true 的表格形式:
[bootstrap.packages]
"brew-cask:textmate" = { version = "latest", adopt = true }要为所有已配置的 cask 启用接管,请设置 Homebrew bootstrap 默认值。单个 cask 可以使用 adopt = false 选择退出:
[bootstrap.brew]
adopt = true
[bootstrap.packages]
"brew-cask:textmate" = "latest"
"brew-cask:replace-me" = { adopt = false }与 Homebrew 的 brew install --cask --adopt 一样,mise 会下载并验证当前 cask 制品,只有在其内容完全一致时才会接管现有应用程序。已有的不同应用程序会保持不变,并且安装会失败,但声明了 auto_updates: true 的 cask 除外:与 Homebrew 一致,这些 cask 会直接接管已有应用程序,因为它可能已经自行更新。被接管的应用程序会由 mise 收据进行跟踪,但不会在 Caskroom 中保留重复的应用程序包。
在 Homebrew 元数据中声明 auto_updates: true 的 cask 会以当前版本安装,之后交由其自行更新。mise 不提供 auto_updates 覆盖项:cask 定义仍然具有决定权。这些自行更新的应用程序也会由收据跟踪,但不会保留重复的 Caskroom 应用程序包,普通的 mise 升级会跳过它们。
mise bootstrap status 会将这些条目标记为已安装(自动更新)。 对于由 mise 管理的 cask,Current 列是 mise 收据中记录的版本;实时应用程序可能已经自行更新到不同版本。JSON 状态会保留稳定的 "state": "installed" 值,并添加 "auto_updates": true。
macOS 隐私与安全(TCC)
替换 /Applications(或你配置的应用程序目录)下的应用程序包,与 brew reinstall --cask 属于同一类操作:macOS 可能会撤销该应用程序的隐私与安全授权(辅助功能、屏幕录制、完全磁盘访问、自动化以及类似权限)。mise 不管理 TCC;替换后,你可能需要在系统设置中重新授予权限。
迁移没有 Homebrew .metadata 的非托管应用程序包时,优先使用接管,这样 mise 可以记录所有权,而无需替换正在运行的应用程序包:
[bootstrap.brew]
adopt = true或者选择性接管:
[bootstrap.packages]
"brew-cask:firefox" = { version = "latest", adopt = true }每当替换现有 .app 时,mise 都会打印警告。当上游发布新的 cask 版本时,版本升级仍会替换应用程序包——请预期需要在这些升级后重新确认 TCC 提示,这与 Homebrew 的行为相同。
在 Linux 上,初始的 cask 支持仅限于不带生命周期钩子、不带结构化 preflight_steps 或 postflight_steps 的纯字体 cask——这些概念来自 Homebrew 的 cask DSL,详见 Homebrew Cask Cookbook。字体会安装到 $XDG_DATA_HOME/fonts,默认值为 ~/.local/share/fonts:
[bootstrap.packages]
"brew-cask:font-heavy-data-nerd-font" = "latest"其他 Linux cask 会被报告为不可用,并在其来自 [bootstrap.packages] 时跳过,从而允许 macOS 和 Linux 共享软件包列表。像 mise bootstrap packages apply brew-cask:firefox 这样的显式请求仍会失败,并显示明确的平台不支持错误。你也可以使用 { os = "macos" } 显式标记 macOS cask。随着 mise 为更多 cask 制品类型获得可移植实现,这一边界将逐步扩展。
brew-cask 目前支持应用程序包 cask(app 制品)、二进制和生成的命令包装器 cask(binary 和 command_wrapper 制品)、通用前缀制品(artifact)、简单的 macOS 安装程序包(pkg 制品)、基于脚本的 cask 安装程序,以及来自 dmg 和常见归档格式的 shell 补全(bash_completion、fish_completion、zsh_completion 和 generate_completions_from_executable)。二进制制品和生成的包装器会暂存到 Caskroom 中,并链接到 Homebrew 前缀,通常位于 <prefix>/bin 下。安装程序会通过 mise 的常规系统软件包 sudo 路径运行,因此非交互式运行不会因等待密码而挂起。Pkg cask 必须在其 uninstall 元数据中包含 pkgutil 收据 ID,这样 mise 才能在安装程序将文件写入 Caskroom 之外后验证安装状态。zap 的 pkgutil ID 会被视为清理元数据,而不是安装收据。对于带有生命周期钩子的 cask,mise 会获取由 API 元数据固定且经过 sha256 验证的 cask Ruby 源代码,并通过自有的 Cask DSL shim 运行受支持的 preflight/postflight 钩子,而不会委托给 Homebrew。mise 还支持针对 staged_path 执行 move/remove 操作的结构化 preflight_steps 和 postflight_steps,支持使用 Homebrew 序列化命令基础、参数、环境、守卫和 sudo 设置的 run 操作,以及具有 Homebrew 兼容的名称/完整匹配、重试、通知和失败策略的 terminate_process 操作。结构化的 copy 和 symlink 步骤支持 Homebrew 路径基础、模板、守卫、源 glob、替换和 sudo 行为。生命周期步骤创建的外部路径会记录在 mise 收据中,如果安装事务失败,会恢复这些路径。Cask formula 和 cask 依赖会优先安装,声明的 cask 冲突会在修改前导致失败。 需要自定义安装程序选项、服务、不受支持的钩子 DSL、不受支持的结构化生命周期步骤或其他 cask 制品类型的 cask,会显示明确的不支持制品错误并失败,而不是委托给 Homebrew。
直接 cask 倒入仍归 mise 所有。其完成状态会记录在 .mise-cask.toml 中;mise 不会生成 Homebrew 的私有 .metadata 收据。带有 .metadata 且恰好有一个 Caskroom 版本的 Homebrew 所有 cask,可以满足匹配的 brew-cask: 条目,而无需转移所有权。状态会将其报告为已安装,并使用该 Caskroom 目录名称作为 Current 版本;apply 会保持其不变,upgrade 会跳过其生命周期。mise 不会创建 .mise-cask.toml、接管 cask 或更改其元数据、应用程序目标、前缀二进制文件或补全链接;请使用 Homebrew 进行升级、重新安装或移除。 没有版本或包含多个版本的 Homebrew 元数据会失败,并显示 Homebrew 修复指导,而不是猜测哪个安装有效。
对于由 mise 管理的 cask,只要其收据和记录的目标仍然存在,状态就会将 cask 视为已安装。应用程序和字体内容指纹会保留用于 prune 和接管安全检查,但现有应用程序或字体内部的内容漂移不会将 cask 标记为缺失,也不会在 apply 时触发重新安装——替换 /Applications/*.app 会重置 macOS 隐私与安全(TCC)授权。二进制文件和补全符号链接仍要求记录的链接目标存在(通过廉价的 readlink 检查),并且目标可解析,因此悬空或被重新指向的链接仍可修复。缺失或未知的收据以及待处理事务仍会被报告为不健康,以便下一次 apply 进行协调。版本升级以及显式 remove + apply 仍会在你希望进行全新倒入时替换应用程序。
之所以存在这一点,是因为共享库包——postgres、ffmpeg、imagemagick、php——从根本上说无法由 mise 的按项目后端(如 aqua: 或 github:)提供:它们的瓶装包是针对固定安装路径和共享依赖树构建的。将它们安装到 Homebrew 的标准前缀,才是让它们正常工作的关键。
支持的平台
| 平台 | 前缀 |
|---|---|
| macOS arm64(Apple Silicon) | /opt/homebrew |
| Linux x86_64 | /home/linuxbrew/.linuxbrew |
| Linux arm64 | /home/linuxbrew/.linuxbrew |
不支持 Intel Mac——brew 管理器会报告在该平台不可用。在 Linux 上,如果某个 formula 没有适用于你的架构的 bottle(大多数 homebrew/core 都有 arm64 Linux bottle,但并非全部),则会改为从源代码构建。
前缀
如果前缀不存在,mise 会使用标准布局创建它——这是 brew 管理器唯一使用 sudo 的时候,模仿 Homebrew 自己的安装程序所做的事情(mkdir + chown 到你的用户)。之后,安装都只是以你的用户身份进行普通文件操作;不会有任何操作以 root 身份运行。
与真实 Homebrew 共存
mise 会像 brew 一样将瓶装包倒入 Cellar,并在每个 keg 中写入与 brew 兼容的 INSTALL_RECEIPT.json 文件。对于真正的 Homebrew 安装来说,mise 倒入的 keg 看起来就像它自己安装的一样:brew list、brew upgrade 和 brew uninstall 都可以对它们正常工作。反过来,mise 的状态检查会直接读取 Cellar,因此由 brew 安装的 formulae 也会被视为已安装。
对于非 keg-only formula,mise 会在 opt 记录旁维护 Homebrew 的 <prefix>/var/homebrew/linked/<name> 记录。对于已配置的 formula,如果任一记录缺失,mise bootstrap packages apply 会在不重新倒入 keg 或替换其公共链接的情况下恢复该记录。只有当旧版 mise 安装现有的公共链接与 keg 的布局匹配时,才会将其识别为已链接。不会执行依赖闭包迁移。
无论 formula 是由 mise 还是由真正的 Homebrew 倒入的,mise 都会直接读取 Homebrew 前缀。它绝不会覆盖前缀中并非由它创建的文件——链接冲突会列出冲突文件并失败,而不会强行覆盖它们。
导入和清理
mise bootstrap packages import --manager brew 会将已安装的 Homebrew formulae 快照到 [bootstrap.packages] 中,思路类似于 brew bundle dump。它会读取 Homebrew 前缀中的活动 opt 链接,并写入如下条目:
[bootstrap.packages]
"brew:ffmpeg" = "latest"
"brew:postgresql@17" = "latest"默认情况下,导入只记录那些其活动 keg 收据表明是按请求安装的 formulae。传入 --all 也会包含依赖 formulae。 带有 tap 的 formulae 会使用完整限定名写入,并且当 mise 能推导出常规的 GitHub tap URL 时,会自动添加推断出的 [bootstrap.brew.taps] 条目:
[bootstrap.brew.taps]
"acme/tools" = "https://github.com/acme/homebrew-tools.git"
[bootstrap.packages]
"brew:acme/tools/widget" = "latest"mise bootstrap packages prune --manager brew 会将当前配置以及可信、可加载、已跟踪的配置作为事实来源。它会移除那些不在已解析依赖闭包中的已链接 Homebrew formulae,这些闭包对应于已配置的 brew: 条目,包括由真实 Homebrew 安装的 formulae。
Prune 会移除活动 keg、其 opt 和已链接 keg 记录,以及指向该 keg 的前缀符号链接。使用 --dry-run 可预览操作,使用 --yes 可跳过确认提示。
这个命令是 mise 针对 bootstrap packages 的声明式清理,类似于 brew bundle cleanup。它不是上游的 brew prune,后者已被 Homebrew 移除,转而采用 cleanup 命令。
mise bootstrap packages prune --manager brew-cask 会将相同的合并配置模型应用于直接 cask 制品,但其所有权边界有意更加狭窄。只有当 cask 的安装时 .mise-cask.toml 收据明确标记其可安全清理,并且每个记录的目标仍具有 mise 在安装后记录的完全一致内容指纹时,才会移除该 cask。该命令会移除这些目标及 cask 的 Caskroom 条目;--dry-run 可预览计划,--yes 可跳过确认。被接管的应用程序和自行更新的应用程序会在没有重复 Caskroom 包的情况下进行跟踪,因此 mise 无法证明目标位置后来存在的应用程序包仍然是其所拥有的那个。这些仅存在元数据的应用程序因此永远不会被 prune 移除。
在其收据包含清理元数据之前安装的 cask 会被跳过,直到后续升级或重新安装刷新该收据。带有 pkg 或命令包装器构件、安装或卸载生命周期操作、待处理事务、Homebrew .metadata、已更改目标,或与其他 mise cask 共享目标的 cask,也会被跳过并说明原因。Prune 从不运行 zap 元数据,也不会根据当前的 Homebrew API 重建历史卸载行为。
倒酒的工作原理
对于依赖闭包中的每个公式(先处理依赖项):
- 获取适用于你平台的瓶子(来自 ghcr.io),并根据 API 元数据验证其 sha256 值。
- 提取到 Cellar 内的临时目录中(未完成的倒酒过程永远不会显示为已安装的软件包)。
- 重定位:瓶子中嵌入了类似
@@HOMEBREW_PREFIX@@的占位路径。mise 会将其重写为实际路径——在文本文件和二进制文件支持的可执行文件(例如 zipapp)的 shebang 前导部分中进行纯文本替换,同时保持其负载内容不变;并在 Mach-O 二进制文件中原地重写和重写加载命令(必要时将加载命令扩展到头部填充区域中),其行为与 brew 的 ruby-macho 完全一致。在 Linux 上,ELF 解释器和 rpath 会按照 brew 的 PatchELF gem 的方式进行修补:如果字符串不再适合原位置,就会将其移动到附加在二进制文件末尾的新段中,并将解释器指向<prefix>/lib/ld.so(mise 会维护一个符号链接,将其指向系统的动态加载器;如果安装了通过 brew 构建的 glibc,则指向该 glibc)。 - 重新签名(macOS):任何被修改的二进制文件都会使用
codesign进行临时签名——在 arm64 上这是必需的,因为内核会终止签名不匹配的二进制文件。 - 写入收据:写入兼容 brew 的
INSTALL_RECEIPT.json。 - 链接:创建
<prefix>/opt/<name>,并将 keg 的bin、lib、include、share等目录符号链接到 prefix 中。对于非 keg-only 公式,还会创建 Homebrew 的 linked-keg 记录。仅 keg 公式 会获得opt链接,但不会链接到 prefix 中,与 brew 的行为相同。
源码公式
有些公式根本没有 bottle(仅源码公式),还有一些虽然在其他平台有 bottle,但在你的平台上没有。mise 会直接从源码构建这些公式——仍然不依赖 Homebrew:
- Ruby — 由于公式本身就是 Ruby 代码,mise 会通过其常规工具机制提供一个由 mise 管理的 ruby(预编译、速度快;如果你已配置了 ruby,则会遵循你的配置)。
- Formula — 该公式的
.rb会从 homebrew/core 下载,并固定到生成 API 元数据时对应的精确 commit,同时使用 API 提供的 sha256 进行校验。 - Source — 会下载稳定版源码归档,并使用 API 提供的 sha256 进行校验。
- Build deps — 该公式的构建依赖(cmake、pkgconf、……)会被加入安装闭包,并优先作为常规 bottle 安装。
- Build — mise 使用自己的 Formula-DSL shim 对公式进行求值,并在规范前缀下运行
def install,同时将PATH、PKG_CONFIG_PATH和编译器标志指向依赖的 keg。该 keg 会获得与已倒入 bottle 相同、兼容 brew 的收据,并带有poured_from_bottle: false——这与 brew 标记其自身源码构建的方式完全一致。
该 shim 实现了 Formula DSL 中常用的子集 (configure/cmake/meson 风格构建、resources、patches、标准路径和环境辅助函数)。对于使用了 shim 未覆盖的 DSL 部分的公式——例如语言特定的辅助函数如 virtualenv_install_with_resources、VCS 下载,以及类似功能——会明确报出 formula uses ... 错误,而不是悄悄编译错误。
源码构建需要可用的工具链(macOS 上需要 Xcode Command Line Tools,Linux 上需要 gcc/make),这与在纯 Homebrew 下的要求完全一致。
升级
mise bootstrap packages upgrade 会重新根据 formulae.brew.sh API 解析已配置的配方,并倾倒任何当前版本与已链接 keg 不同的配方——新的 keg 会替换旧的,链接也会重新指向,就像 brew upgrade 所做的那样。由于瓶装包只存在于配方的当前版本中,因此“升级”和“安装当前瓶装包”是同一个操作。
限制
- Cask 工件覆盖范围有意保持狭窄。 在 macOS 上,
brew-cask支持应用程序包、二进制工件、字体工件,以及来自 dmg 和常见归档格式的简单 pkg 安装程序。在 Linux 上,它支持不带生命周期钩子的纯字体 cask,也不支持结构化的preflight_steps或postflight_steps。其他工件类型、不带pkgutilID 的 pkg 安装程序,以及带有自定义选项的 pkg 安装程序都会明确失败。 - 尚未实现
brew services。 - 尚未实现 Cask 导入。 Cask prune 仅限于由 mise 所有、且其安装时收据证明可以安全移除的直接 工件。在支持这些工件的卸载语义之前,pkg 工件和包含生命周期操作的 cask 会被跳过。
- 源代码构建涵盖常见的 formula 形态。 mise 的 formula shim 实现了 DSL 中广泛使用的子集(请参阅 源代码 formula);超出该范围的 formula 会失败,并清晰指出不受支持的功能。
- 使用规范的 formula 名称。
postgresql@17是 formula 名称,而不是 mise 的版本固定值——API 的当前稳定版本决定实际安装的版本。别名(postgres)可以正确安装,但mise bootstrap packages status无法跟踪它们;mise 会发出警告并告知你规范名称。 PATH由你自行设置:必须将<prefix>/bin添加到PATH,才能使用链接的 二进制文件,这与 Homebrew 本身的使用方式相同。