mise bootstrap linux:在 mise 中以声明式方式管理 Linux systemd 用户单元
2026/9/10 10:55:47 网站建设 项目流程

mise bootstrap linux:在 mise 中以声明式方式管理 Linux systemd 用户单元

【免费下载链接】misedev tools, env vars, task runner项目地址: https://gitcode.com/GitHub_Trending/mi/mise

本文围绕 mise 的mise bootstrap linux命令组展开,讲解它如何把[bootstrap.linux]配置段(核心为[bootstrap.linux.systemd.units])声明式地落盘为 systemd 用户服务/定时器。读完本文,你将能够:编写并验证 Linux bootstrap 配置、使用systemd-units status/apply检查与部署用户单元、并理解单元文件渲染路径、状态机语义以及命令背后的实现细节。

命令概览:mise bootstrap linux

mise bootstrap linux是 mise 引导(bootstrap)工作流中专门处理Linux 平台配置的命令组,其作用是将 mise.toml 中的[bootstrap.linux]配置段应用到当前 Linux 主机。命令参考页(docs/cli/bootstrap/linux.md)标注:

  • Usage:mise bootstrap linux <SUBCOMMAND>
  • Effect:read-only —— 命令组本身不修改状态,所有变更都发生在其子命令中
  • Flags:-h --help
  • 源码位置:src/cli/bootstrap.rs

从源码结构看,[bootstrap.linux]目前只有一个功能子命令入口,即systemd-units(别名systemd),见 src/cli/bootstrap.rs:

#[derive(Debug, usage_rs::Subcommands)] enum BootstrapLinuxCommands { #[usage(name = "systemd-units", alias = "systemd")] SystemdUnits(BootstrapSystemd), }

也就是说,mise bootstrap linuxmise bootstrap linux systemd指向同一个子命令组。该命令组对应mise bootstrap整体流程中的第 5 阶段(shell 激活、macOS 配置、Linux user units、用户设置),是“把机器配置成配置文件中声明的样子”这一目标在 Linux 平台上的落点。

此外,从 src/cli/bootstrap.rs 的Commands枚举结构看,mise bootstrap顶层还注册了一个名为linux-systemd-units(显示别名systemd)的快捷入口,可以直接跳过linux这一层;而--only/--skip过滤 bootstrap 阶段时使用的部分名正是linux-systemd-units(见 src/cli/bootstrap.rs 中bootstrap_part_name的映射BootstrapPart::Systemd => "linux-systemd-units")。因此下面的等价用法都成立:

mise bootstrap --only linux-systemd-units # 只运行 Linux systemd units 阶段 mise bootstrap --skip linux-systemd-units # 跳过该阶段

systemd-units子命令:status 与 apply

mise bootstrap linux systemd-units负责管理来自[bootstrap.linux.systemd.units]的 systemd 用户服务:安装单元文件并在当前用户管理器(user manager)中调和(reconcile)服务状态。注意它与[bootstrap.services]声明的系统级服务是两套独立机制(参考 docs/cli/bootstrap/linux/systemd-units.md)。

mise bootstrap linux systemd-units status

展示已配置的 systemd 用户服务当前状态,效果为 read-only。标志(见 docs/cli/bootstrap/linux/systemd-units/status.md):

标志说明
-J --json以 JSON 格式输出
--missing若任何已配置的用户服务未处于期望状态,则以退出码 1 结束
-h --help打印帮助

状态判定在 src/system/systemd.rs 中定义为一个四值状态机:

pub(crate) enum SystemdState { Active, // 单元正在运行 Inactive, // 单元已停止 Differs, // 已落盘单元内容与配置声明不一致 Missing, // 用户单元目录下没有对应的单元文件 }

其中“是否处于期望状态”由is_desired()依据声明中的start键判定:Activestart = trueInactivestart = false均视为期望态。status --missing只要发现任意单元偏离期望态即返回非零退出码,适合放进 CI 或巡检脚本做断言。

mise bootstrap linux systemd-units apply

安装并(按声明)启动 systemd 用户服务,效果为 modifies state。标志(见 docs/cli/bootstrap/linux/systemd-units/apply.md):

标志说明
-n --dry-run只打印将要执行的命令,不实际执行
-y --yes跳过确认提示
-h --help打印帮助

从 src/system/systemd.rs 的实现看,apply的实际动作序列是:

  1. 将变更的单元文件重写到~/.config/systemd/user/
  2. 执行systemctl --user daemon-reload(所有 systemctl 调用都带 30 秒超时,见 src/system/systemd.rs 的SYSTEMCTL_TIMEOUT);
  3. 对声明了wanted_by的单元执行 enable,对wanted_by = []的单元执行 disable;
  4. start = true时 restart 单元,start = false时停止单元。

apply不会隐式触发——mise 绝不会在别的命令里偷偷写 systemd 单元,只有mise bootstrap linux systemd-units apply和完整mise bootstrap流程会这么做。因此推荐的工作流是先status预览、再apply --dry-run确认、最后apply --yes落盘:

mise bootstrap linux systemd-units status # 查看各单元状态 mise bootstrap linux systemd-units status --json # 机器可读 mise bootstrap linux systemd-units status --missing # 偏离期望态则退出码 1 mise bootstrap linux systemd-units apply # 写入并启动缺失/变更的单元 mise bootstrap linux systemd-units apply --dry-run # 只打印将执行的命令 mise bootstrap linux systemd-units apply --yes # 跳过确认提示

[bootstrap.linux]配置段:units 声明与渲染规则

配置声明在[bootstrap.linux.systemd.units.<name>]下。从 src/system/systemd.rs 的SystemdTomlConfig结构看,每个条目被反序列化为一个单元声明,键名即 systemd 指令的小写蛇形写法,核心字段包括:

  • 通用([Unit]):descriptionafterwantsrequires
  • 服务([Service]):exec_starttyperemain_after_exitexec_stoptimeout_start_sectimeout_stop_secno_new_privilegesprivate_tmpenvironment(表,渲染为Environment)、environment_fileniceumaskworking_directoryrestartrestart_secstandard_outputstandard_error
  • 定时器([Timer]):on_boot_secon_unit_active_secon_unit_inactive_secon_calendarrandomized_delay_secaccuracy_secpersistentunit
  • 行为控制:start(默认true)、wanted_by(service 默认["default.target"],timer 默认["timers.target"]

渲染规则:

  • 每个条目被写入~/.config/systemd/user/dev.mise.<name>.servicedev.mise.<name>.timer(含 timer 键的条目渲染为.timer),由systemctl --user管理;
  • 单元名允许字母、数字、._-@;mise 只负责自己用dev.mise.前缀创建的单元文件,不会动其他用户单元;
  • exec_startexec_stopworking_directory中裸写的~/~/会在写盘前展开为当前用户主目录;
  • 定时器条目的unit若不带类型后缀(如unit = "healthcheck"),会解析为 mise 拥有的dev.mise.<unit>.service;要指向外部单元需写全限定名(如unit = "nginx.service"),按原文写入;
  • 定时器必须至少设置on_boot_secon_unit_active_secon_unit_inactive_secon_calendar之一,且服务专用键(exec_startenvironmentrestart等)在 timer 条目上会被拒绝——被定时器触发的那个单元应写成独立的服务条目。

一个完整的真实示例可直接参考 src/config/config_file/mise_toml.rs 中test_bootstrap_linux_systemd_units测试用例使用的配置:

[bootstrap.linux.systemd.units.my-sync] description = "sync files" after = ["network-online.target"] wants = ["network-online.target"] requires = ["credentials.service"] exec_start = "~/.local/bin/my-sync --watch" type = "oneshot" remain_after_exit = true exec_stop = "~/.local/bin/my-sync --stop" timeout_start_sec = "120" timeout_stop_sec = "30" no_new_privileges = true private_tmp = true environment = { PATH = "/usr/bin:/bin" } environment_file = ["-%h/.config/my-sync.env"] nice = 10 umask = "0007" working_directory = "~" restart = "on-failure" restart_sec = "5s" standard_output = "append:%h/.local/state/my-sync.log" wanted_by = ["default.target"] [bootstrap.linux.systemd.units.my-sync-timer] on_boot_sec = "2min" on_unit_active_sec = "10min" on_unit_inactive_sec = "5min" on_calendar = "hourly" randomized_delay_sec = "30s" accuracy_sec = "1s" persistent = true unit = "dev.mise.my-sync.service"

该测试断言了每个字段被正确解析进配置对象(如environment.get("PATH") == "/usr/bin:/bin"wanted_by == ["default.target"]、timer 的unit == "dev.mise.my-sync.service"),可以作为字段取值方式的可靠依据。

声明式语义:合并、替换与清理

SystemdState的调和逻辑与配置解析(MiseToml::bootstrap_config(),见 src/config/config_file/mise_toml.rs)的结构看,[bootstrap.linux.systemd.units]遵循几条重要语义:

  • 声明式且可叠加:unit 名按配置层级(全局 → 项目)合并,同一单元名在更局部的配置中声明时,会整体替换先前的完整声明;
  • service/timer 切换自动清理:当一个条目的性质从 service 变为 timer(或反向)时,mise 会停止、禁用并删除过期的“兄弟”单元,避免残留;
  • 纯 Linux:在其他平台上该配置段不生效——status会把这些条目列为 skipped,apply直接忽略;
  • 仅用户单元、仅目标用户:mise 只写~/.config/systemd/user并用systemctl --user;要管理/etc/systemd/system下的系统服务需改用 bootstrap 的 managed files 与 services 机制。以sudo mise运行时该阶段会被跳过,因为systemctl --user会指向错误的用户管理器;
  • 单元环境隔离:单元命令不会继承你交互式 shell 里的 mise 激活,必须在ExecStart里显式写可执行路径,并通过environment/environment_file提供所需环境。ExecStart走 systemd 命令语法,需要 shell 运算符时要显式调用 shell 或包一层脚本。

状态机在源码中的验证

实现侧的关键证据集中在 src/system/systemd.rs:

  • 单元文件路径构造:user_units_dir().join(format!("dev.mise.{name}.service"))(约 src/system/systemd.rs);
  • timer 渲染示例(单测断言的期望输出):
[Unit] Requires=network-online.target [Timer] OnBootSec=2min OnUnitInactiveSec=5min RandomizedDelaySec=30s AccuracySec=1s Persistent=yes Unit=dev.mise.healthcheck.service [Install] WantedBy=timers.target
  • SystemdStatus结构同时记录request(期望声明)、path(单元文件路径)、active/enabled(manager 实时状态)与state(上述四值状态),status --json输出的就是这些字段的 JSON 化结果。

小结与延伸阅读

mise bootstrap linux本身是 read-only 的命令组骨架,真正干活的是systemd-units apply/status这对子命令:前者把[bootstrap.linux.systemd.units]的声明落盘为dev.mise.*用户单元并调和到期望状态,后者以active/inactive/differs/missing四态汇报偏离(可配合--missing做退出码断言)。它与 macOS 侧的mise bootstrap macos子命令在 bootstrap 流程中构成平台对称的设计。

延伸阅读:

  • systemd 用户单元专题文档:docs/bootstrap/systemd.md
  • mise bootstrap总览:docs/cli/bootstrap.md
  • 子命令参考:systemd-units、apply、status
  • 实现源码:src/cli/bootstrap.rs、src/system/systemd.rs、配置解析 src/config/config_file/mise_toml.rs

【免费下载链接】misedev tools, env vars, task runner项目地址: https://gitcode.com/GitHub_Trending/mi/mise

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询