Skip to content

yadm

yadm用于在类 Unix 系统上管理你的 dotfiles 文件。所谓的 dotfiles 即以点开头的文件,他在 Linux 中会被隐藏因此常用来作为配置文件。所以 yadm 说白了就是管理个人配置文件的。

安装

各大发行版都提供了 yadm 包,直接使用包管理工具安装即可。实际上 yadm 就是一个shell script通用性非常的好(兼容 bash 即可),可以直接使用下面的命令安装:

Bash
sudo curl -fLo /usr/local/bin/yadm https://github.com/TheLocehiliosan/yadm/raw/master/yadm && sudo chmod a+x /usr/local/bin/yadm

基本使用

yadm 是构建在 git 之上的,git 要求整个仓库都在同一个文件夹下面,文件夹下面所有的文件都是潜在跟踪目标。yadm 改进了这一点,它会创建一个干净的 repository,只追踪用户主动添加的文件。其他 yadm 的使用流程基本上和 git 是一样的:

Bash
# 初始化一个仓库,yadm 默认的 worktree 是 $HOME
yadm init
Initialized empty shared Git repository in $HOME/.local/share/yadm/repo.git/
# 添加文件到暂存区
yadm add <file-path>

# 提交
yadm commit -m <mssage>

# 绑定远程仓库并推送
yadm remote add origin <url>
yadm push -u origin <local branch>:<remote branch>

之后我们要在一个新的机器上部署只需要执行:

Bash
yadm clone <url>
# 如果存在子模块还需要执行
git submodule update --init --recursive

# 或者直接执行
yadm clone --recurse-submodules <url>

如果本地存在与版本库不同的内容的同路径文件,并不会覆盖本地,你需要自己来处理差异或者直接覆盖掉。

高级功能

子模块(submodule)

yadm 以$HOME目录作为仓库的 git。同样他是支持添加子模块的:

Bash
git submodule add <url>

完全可以像正常的管理 git 子模块那样管理即可。注意一定不要跟绝对路径:

Bash
# 使用 ~/.config/dir  这样会是一个绝对路径,在 MacOS 和 Linux 以及不同的用户下切换会出现问题
[submodule "/Users/yangguodong/.config/python_cookbook"]
        path = /Users/yangguodong/.config/python_cookbook
        url = git@gitee.com:hncjygd/python_cookbook.git
# 直接切换到 .config 然后 submodule add ,给出的是相对路径,yadm 的管理的仓库根为 $HOME 这样就没有问题
[submodule ".config/python_cookbook"]
        path = .config/python_cookbook
        url = git@gitee.com:hncjygd/python_cookbook.git

引导(Bootstrap)

所谓的 Bootstrap 即引导,他通常用于初始化操作,最常见的就是安装依赖。yadm 对 Bootstrap 的支持非常简单,他在 clone 后会自动运行$HOME/.config/yadm/bootstrap脚本。

这个 bootstrap 可以是任何可执行文件,不过通常就是一个 shell 脚本。如果该脚本存在 在 clone 会提示你:

Bash
Found .config/yadm/bootstrap
It appears that a bootstrap program exists.
Would you like to execute it now? (y/n)

即使选着了 n 也不需要着急,直接运行 yadm bootstrap 同样可以安装。

一个简单的示例,在 macOS 上安装 homebrew:

Bash
#!/bin/sh

system_type=$(uname -s)

if [ "$system_type" = "Darwin" ]; then

  # install homebrew if it's missing
  if ! command -v brew >/dev/null 2>&1; then
    echo "Installing homebrew"
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  fi

  if [ -f "$HOME/.Brewfile" ]; then
    echo "Updating homebrew bundle"
    brew bundle --global
  fi

fi

加密(Encryption)

yadm 管理的文件通常需要公布到远程仓库中,他们应当是脱敏的,但是像管理 ssh 密钥的需求是存在的。yadm 同样提供了一种机制来为我们加密这些文件。使用加密需要以下步骤:

  1. $HOME/.config/yadm/encrypt文件中添加要加密的文件(支持通配符)
  2. 执行yadm encrypt命令,他会读取上面的配置文件然后询问密码,之后会将匹配到的文件加密保存到$HOME/.local/share/yadm/archive文件中
  3. 之后我们需要将上述的两个文件提交到 yadm 管理yadm add .config/yadm/encrypt & yadm add .local/share/yadm/archive
Bash
# 注意不要写成 $HOME/.ssh/*.key
.ssh/*.key

当我们需要在一个新的环境中 clone 配置后,这些加密的文件是不会自动解密的,如果想要解密还需要执行:

Bash
yadm decrypt

加密默认是基于 gpg 的,因此需要可以运行 gpg 命令。如果在终端中执行加密和解密有时候会提示gpg: signing failed: Inappropriate ioctl for device错误,这是因为 gpg 无法在当前终端弹出密码输入窗口导致的,此时可以定义export GPG_TTY=$(tty)即在终端显示密码输入窗口。

更改为 openssl 加密

gpg 加密问题比较多,可以修改为 openssl 加密:

Bash
yadm config yadm.cipher openssl

之后需要将.config/yadm/config添加到仓库中。

多平台部署

所谓的Alternate Files即提供了一种机制来作为后备文件,这些后备文件具有特定的文件名,他们都能够在不同的环境中被部署。

后备文件使用##<condition>[,<condition>,…]作为后缀,其中condition就是条件,除了default条件外每一个条件都是以key.value形式的键值对,可用的条件:

含义 典型值
default 默认 default
arch,a 架构(uname -m) a.x86_64,a.arm64
distro,d 发行版(/etc/os-release id) d.debian,d.centos
hostname,h 主机名(uname -n) h.hostname
os,o 操作系统名(uname -s) o.Darwin,o.Linux,o.WSL
user,u 用户名(id -u -n)

可以使用任意数量的条件,yadm 会从最佳的匹配来进行选着,例如一个文件:

Bash
$HOME/path/example.txt##default
$HOME/path/example.txt##os.Darwin
$HOME/path/example.txt##os.Darwin,hostname.host1
$HOME/path/example.txt##os.Darwin,hostname.host2
$HOME/path/example.txt##os.Linux

如果在 Linux 上直接匹配到##os.Linux,如果是在 MacOS 上有几种情况:

  1. 如果hostname = host1那么就匹配##os.Darwin,hostname.host1
  2. 如果hostname = host2那么就匹配##os.Darwin,hostname.host2
  3. 如果是其他hostname的用户那么就匹配到##os.Darwin
  4. 再有其他情况,例如在 WSL 中就使用##default

对于加密保存的文件,即列入$HOME/.config/yadm/encrypt中的文件, 还有 bootstrap 都同样适用这种命名方式。 通常不推荐使用这么复杂的管理方式。如果可能的话还是尽量提供一套统一的配置文件,可以在文件中检测变量来提供不同的分支。

yadm alt

Alternate Files 的实现方式是在 clone 项目的时候匹配最佳的文件,然后使用符号连接来引用这个文件,还是上面的例子,如果我们在 WSL 中使用实际上是创建了一个:

Bash
ln -s $HOME/path/example.txt##default $HOME/path/example.txt

他最大的问题在于,所有 Alternate Files 文件还是位于$HOME/path/目录下,这就导致了这个目录异常混乱。

yadm 提供了一种机制,我们可以将这些文件以相对路径的形式放置到$HOME/.config/yadm/alt/目录下,例如上面的可以放置到 $HOME/.config/yadm/alt/path/example.txt##default,之后的交由 yadm 来管理即可。

还有 yadm 会自动创建这些符号链接,如果存在冲突可以解决后执行:

Bash
yadm alt

XDG Base Directory specifications

配置文件通常都是家目录下的一系列文本文件,他们通过以 . 开头来隐藏。这就导致了一个问题就是家目录下的文件异常混乱。开源社区发布了一套规范即XDG Base Directory spec来约束家目录下文件的存放:

环境变量 默认值 含义
$XDG_CONFIG_HOME $HOME/.config/ 软件配置存放路径
$XDG_DATA_HOME $HOME/.local/share/ 软件依赖的数据存放目录(静态的)
$XDG_STATE_HOME $HOME/.local/state/ 软件依赖的状态数据存放目录(动态的,不可移植的。例如日志)
$XDG_CACHE_HOME $HOME/.cache/ 软件缓存存放路径(非必要文件)

还推荐用户的可执行文件位于$HOME/.local/bin目录下,需要将他们放置到$PATH环境变量中

目前很多程序都已经遵循了这个规范,但是也有例外。如果想要不支持该配置的程序也支持通常有两种方式,这主要取决于应用:

Bash
# 有些程序会使用特定的环境变量来引入配置
export NPM_CONFIG_USERCONFIG="$XDG_CONFIG_HOME/npm/npmrc"

# 有些程序可能使用特定的参数来运行,这样我们可以使用 alias
# 最新的 tmux 已经支持了 XDG 目录规范
alias tmux="tmux -f ~/.config/tmux/tmux.conf"