Keep Knowledge in Plain Text

The Pragmatic Programmer 一书发行于 1999 年,但其间的大多数观念放在今日来看也仍不过时。作者在「纯文本的威力」一节从什么是纯文本开端聊起,引出了将知识作为纯文本保存的观念。接着又从「防备过时」、「运用杠杆效应让已有东西发挥最大优势」和「易于测验」三个角度论述了纯文本的优势,提出不论互联网怎么改变,纯文本一定是一切信息载体的最小公分母,能够成为相互交流的标准。

What is dotfiles

UN*X 体系大多数东西都是以纯文本的形式装备,文件名以 . 最初,存储在用户的 home 目录中(为了坚持 Home 目录的整齐,现在越来越多的程序都开端运用 XDG 标准指定的路径替代 home 目录,默认为 ~/.config),所以这些文件也被统称为 dotfiles。

Why you should build your own dotfiles

优秀的工匠一般有自己专属的东西箱,集合了自己打磨多年,用起来最为趁手的各类东西。dotfiles 就开发者的东西箱,里边有最符合自己品尝和习气的东西装备。关于常常需求在终端上花费许多时刻的开发人员来说,办理和优化东西的装备是一项非常值得的出资,有时一些很简略的装备优化也能大幅提高你的作业效率。

Intro to Dotfiles

在 Github 上以 dotfiles 为关键词查找得到的结果

那么在 Github 上共享自己 dotfiles 的含义是什么呢?我觉得最重要的一点是你能够从其他人优秀的装备中取得灵感,然后继续地迭代和改进自己的「东西箱」,同时把自己的装备分享出来,供其他人参阅,有时或许还会收到一些改进意见。对我来说,dotfiles 是自己第一个真正含义上的开源项目。以此为起点,在继续的迭代和打磨中,我对 Shell 脚本,Linux 体系和 CLI 东西有了更深的了解,从 dotfiles 中孵化的几个东西也收成了一些 star。假如你想过更多地参加开源社区,但还没有找到一个好的切入点,不妨也从建立自己的 dotfiles 库房开端,这会是你学习新东西,提升脚本水平,了解 CI / CD ,实践自动化的绝佳场所。

Always consider version control

dotfiles 在开发环境中的方位其实和注册中心在微服务架构中的方位是相似的,开发所需的每一个东西对应一个「微服务」。注册中心能够便利咱们对微服务的装备进行集中办理和分发,dotfiles 也是一样。建立 dotfiles 库房今后,你的装备升级和改动都应该经过 dotfiles 完结,然后就能够很容易地同步到其他开发环境,因此 dotfiles 很或许会成为你整个职业生涯从事时刻最长的项目。出于这个原因,可维护性和可扩展性对 dotfiles 库房来说也很重要,咱们应该把它归入版别操控办理之中。

将 dotfiles 归入版别操控有许多优点,比方你能够放心肠尝试新装备而不必忧虑出问题的时分无法回滚,也能让代码和装备坚持干净整齐,你能够随时经过 VCS 检查很久以前的文件内容,而不必常常保存大段大段的老旧代码不舍得删除。

具体的挑选上,能够运用一些专门为办理 dotfiles 而规划的东西,比方 rcm 和 yadm 等,这些东西需求额定装置,除了 git 本身的才能以外,所做的作业主要便是复制或许链接装备文件到指定方位。我个人更喜爱运用 git 合作一些简略的 setup 脚原本替代这些东西,优点是防止了额定的依赖,当你装备一个新体系的时分需求做的作业更少;另一个优点是,理论上你能够在 setup 脚本里边完结任何作业,这使得咱们用非常灵活的方法装备每一个东西。

运用 git 追寻装备文件也有两种挑选,你能够将库房建立在整个 home 目录(或 ~/.config)之上,并经过 gitignore 来忽略非装备文件;也能够将需求追寻的装备文件保存到一个独立的目录中,并经过复制或许软链接的方法将其部署到各自的方位。两种计划都有不少支撑者,但我觉得后一种方法要好许多,主要有这么几个原因:

  • 防止意外删除文件的危险。假如将整个 home 目录或 ~/.config 目录归入 git 的版别操控,运转 git clean 等操作的时分,很或许会不小心删除掉一切未被追寻的文件。

  • 能够追寻不在 home 或许 ~/.config 中的装备文件。

  • 新机器上的装置和装备更简略。

Repository layout

信任咱们必定已经有一些装备文件了,比方 .zshrc ,.vimrc ,.gitconfig等。初始化一个 dotfiles 库房很简略,新建一个文件夹(主张在 home 目录),然后把一切这些装备文件移动到 dotfiles 里边,然后用 git 提交,完结。

装置一般便是把 dotfiles 中的文件软链接到实际方位,比方:

ln -sf ~/dotfiles/vimrc ~/.vimrc

你能够在每次需求的时分运转这些指令来装置,但我主张你把这些指令写到一个 install 脚本中,防止每次装置的时分回想或许查找每个装备文件应该链接到哪个方位。

下面是一个 dotfiles 库房的布局:

❯ tree dotfiles
dotfiles
├── archlinux
│  ├── mirrorlist
│  ├── pacman.conf
│  ├── paru.conf
│  ├── sddm.conf
│  └── setup.sh
├── osx
│  └── setup.sh
├── brew
│  ├── Brewfile.darwin
│  ├── Brewfile.linux
│  └── setup.sh
├── git
│  └── setup.sh
├── tmux
│  ├── setup.sh
│  ├── tmux.conf
│  └── tmux_osx.conf
├── vim
│  ├── nvim
│  ├── snippets
│  ├── spell
│  ├── ideavimrc
│  ├── setup.sh
│  └── vimrc
├── zsh
│  ├── sheldon
│  ├── setup.sh
│  ├── starship.toml
│  ├── zshenv
│  └── zshrc
└── install

装置的时分,只需求在根目录下履行 ./install <module>,install脚本会进入 module 对应的文件夹中运转setup.sh。每逢你装备好了一个新东西,就能够在 dotfiles 中建立一个子目录,把装备文件移动到里边,然后把软链接指令记载在 setup.sh中再运转一下就能够了。

下面是 install 脚本的一个样例:

#!/usr/bin/env bash
# get dir of the current script and cd in it, so you can run the script properly wihtout enter into the root dir
SDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) && cd "$SDIR" || exit 1
# show usage
usage() { echo "Usage: $(basename "$0") <module> [module]..." >&2; }
# simple log printer
loginfo() { printf "%b[info]%b %s\n" '\e[0;32m\033[1m' '\e[0m' "$@" >&2; }
logerro() { printf "%b[erro]%b %s\n" '\e[0;31m\033[1m' '\e[0m' "$@" >&2; }
[[ "$#" -lt 1 ]] && usage && exit 1
# install a module
install_module() {
    local module="$1"
    loginfo "install $module config ..."
    [[ ! -f "$module/setup.sh" ]] && logerro "$module config not found!" && return 2
    "$module/setup.sh" && loginfo "$module config installed successfully!"
}
# install every specified modules
for module in "$@"; do
    install_module "${module%/}"
done

那么每个 setup.sh是什么样子呢,这个其实依据每个要装备的目标确定。比方关于 zsh,或许便是几行简略的软链接指令:

#!/usr/bin/env bash
SDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
ln -sf "$SDIR/zshrc"  ~/.zshrc
ln -sf "$SDIR/zshenv" ~/.zshenv
# change the default shell to zsh (if necessary)
[[ "$SHELL" =~ "zsh" ]] || chsh -s "$(command -v zsh)"

Not only dotfiles

Dotfiles 库房能够办理的内容其实并不局限于相似zshrc,vimrc这样的装备文件,你能够在setup.sh中记载任何能够经过指令完结的操作。举个比方,咱们必定都在用 homebrew 包办理器。当咱们拿到一个新 Mac 的时分,需求先去官网检查装置指令,然后一边回想一边手动装置每一个需求用到的东西,有些记不清名字或许还要 google 一番。但 brew 其实有个指令brew bundle dump能够生成一个 Brewfile 文件,这个文件记载了体系一切已装置的包。你要做的便是把这个文件放到到你的 dotfiles 傍边,并配上一个setup.sh脚本:

#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
SDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) && cd "$SDIR" || exit 1
# install homebrew if not installed
if ! hash brew &>/dev/null; then
    bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
fi
# install all the packages in Brewfile
if [[ $OSTYPE =~ darwin ]]; then
    brew bundle install --file Brewfile.darwin
elif [[ -x /home/linuxbrew/.linuxbrew/bin/brew ]]; then
    eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
    brew bundle install --file Brewfile.linux
fi

Homebrew 很早就支撑 Linux 了,开发机上 Debian 包办理器软件源太老,比较新的东西根本都没有,主张运用 brew 来取得和 mac 一致的体会。

今后不论你是在 Linux 开发机上,仍是在全新的 Mac 环境上,都能够经过运转./install brew把一切的东西给装置好。以 go sdk 为例,假如你想装置最新的版别,只需求一行指令 brew install go,假如想切换到一个老版别,也只需求 3 行指令:

brew install go@1.16 # install go 1.16
brew unlink go       # unlink current go
brew link go@1.16    # link go@1.16 as default go

即便是 GUI 程序,你也应该优先考虑运用 brew 来装置,而不是去 AppStore 查找,或许去官网手动下载。由于运用brew能很便利地进行更新和卸载,还能经过Brewfile快速康复装置。Homebrew 的 Cask 库房收录了许多 GUI 软件,咱们常用的像 chrome, jetbrains-toolbox,微信,网易云音乐等库房都有收录,装置时只需求加上一个–cask选项即可(相同能够被brew bundle dump记载下来):

brew install --cask wechat # 装置桌面微信

Dockerize

有些时分咱们暂时需求在某些机器上作业,假如想运用自己了解的作业环境,就得一遍又一遍地装置软件包,装备 dotfiles,作业结束的时分或许还得清理这些痕迹,比较麻烦。供给了 OS 级别文件阻隔才能的 Docker 能够帮助咱们处理这些问题,你能够将作业环境所需的包以及装备文件,连同操作体系一同打包成镜像,然后直接在其他机器上以容器的方法运转。「懒惰是程序员的美德」,打包和更新镜像的作业咱们能够交给 CI 来做,就像这样:

github.com/wfxr/dotfil…

有了自己的 dotfiles 镜像,即便在一穷二白的新机器上,咱们只需这样一条指令就能够得到自己了解的开发环境:

docker run -v <local-dir>:/root/work -it wfxr/dotfiles zsh

Best Practices

  • 挑选支撑纯文本装备的软件和东西。

寻求纯文本装备不是为了看起来极客,而是由于纯文本易于阅读和测验,和版别操控东西合作的更好,便利自动化,并且在 server 环境中也能够运用。在 Linux 中,假如你运用 AwesomeWM 或许 i3WM 这类支撑纯文本装备的桌面办理器,重装体系之后只需链接一下装备目录,体系桌面,启动项以及快捷键绑定等立马就能康复成你习气的状况。相反,假如是 Gnome 或许 KDE 这样重度依赖 GUI 来进行装备的桌面,就需求经过一番冗长的设置之后才能开端高效率地作业。

  • 运用插件办理器,而不是手动装置。

zsh,fish,vim,tmux等都有各自的插件办理器,用它们来装置各类插件比自己手写要简略的多,更新起来也很便利。

  • 让装置脚本具有幂等性。

幂等性能够帮咱们完成声明式的装备办理,你能够放心肠屡次履行而不必忧虑过错和冲突。举例来说,像mkdir foo 和 ln -s src dest这样的句子便是幂等的,重复履行会报错;用幂等的写法应该改成mkdir -p foo和ln -sf src dest。

  • 经过 .local 文件阻隔本地装备

有时某些装备只对特定的环境才有含义(比方开发机上常常用到的代理装备),不需求把这些信息放在 dotfiles 库房中同步到其他地方。咱们能够把它记载在一个独自的 .local文件中,然后用相似source或许include句子引进到主装备文件中,像下面这样:

# filename: ~/.zshrc
# ... other configs
# load ~/.zsh.local if exists
[ -f ~/.zsh.local ] && source ~/.zsh.local
# filename: ~/.zsh.local
# http proxy
# example: proxy git clone https://github.com...
proxy() {
    http_proxy=http://sys-proxy-rd-relay.byted.org:8118 \
        https_proxy=http://sys-proxy-rd-relay.byted.org:8118 \
        no_proxy=.byted.org \
        $*
}
# go proxy
go env -w GOPROXY="https://goproxy.byted.org|https://goproxy.cn|direct"
go env -w GOPRIVATE="*.byted.org,*.everphoto.cn,git.smartisan.com"
go env -w GOSUMDB="sum.golang.google.cn"
  • 运用 git-crypt 加密隐私装备

关于一些私密装备,假如有同步需求,但放在公开的 dotfiles 中又不安全,能够挑选用 git-crypt 对库房中指定的文件进行加密,然后能够安全地共享。git-crypt 在文件在提交的时分会自动加密,签出的时分会自动解密,整个过程是透明的,非常便捷。

Useful links

  • dotfiles.github.io/tutorials/
  • github.com/wfxr/dotfil…
  • github.com/ibraheemdev…