教你用Org-mode管理dotfiles
Table of Contents
某一天,'polm23'在Hack News上提了个问题, 你们都是怎么管理dotfiles的. 我分享了一下我的方法并贴上了两个例子. Turns out, rare for me, I stumbled upon an original way to edit, document and deploy dotfiles. 虽然我们Emacs用户早已开始用Org-Mode来管理.emacs文件了, 但还是只有极少数的人用它来管理其他的dotfile.
用Org-Mode来管理dotfile,我至少能想到三个优势.
- 你可以对每一行的配置都加上文档说明. 你可以记录下你是什么时候写下这行配置的,为什么要写下这行配置. 你也可以在这段代码块附件记下其他可能的配置,这样你就无需老是去查看man或其他文档信息了.
- 你不再需要创建那些容易出错的符号链接了. 我们经常会写出像
ln -s dotfiles/vimrc ~/.vimrc
这样的创建符号链接对吧? 即使我们记得总是在创建符号链接时使用绝对路径,那当你想把dotfile从BSD迁移到Linux时怎么办呢?~
在两个系统可是表示不同路径的呀! 当然,你可以在发布脚本中为不同操作系统创建不同的分支,但是借助Org-Mode,你根本无需这么麻烦. - 你甚至可以管理远程服务器上的dotfile. Org-Mode可以将代码块'tangle'到远程主机上去, 所以你可以象管理本机上的
.bashrc
一样管理服务器上的nginx.conf
文件.
1 1 Here's how
1.1 1.1 Requirements
24.x及以上版本的Emacs, 8.x或更新的Org-Mode, 还有你最喜爱的键盘.
然后在Emacs中创建一个新文件,你可以把它叫做 dotfiles.org
或其他什么名字.
1.2 1.2 "Normal" dotfiles
这些文件直接就存放在你的home目录下. 因此可以很直接的在Emacs中管理这些文件. 为了便于组织,你可以将同类的文件放在同一个标题下. 比如,对于那些与email相关的文件,你可以创建这样一些标题:
* Email ** Muttrc ** Aliases ** goobookrc
然后将你文件的内容放在每个子标题下的代码块中,像这样:
* Email ** Muttrc #+BEGIN_SRC conf :tangle ~/.muttrc #source "/etc/Muttrc" # Not available on OS X source "gpg --batch --passphrase-file ~/.sec/.passphrase --textmode -d ~/.sec/mutt.gpg |" set realname="Haoyang Xu" set sig_dashes ... #+END_SRC
这样一来,当你运行 M-x org-babel-tangle
或按下 C-c C-v t
时,上面代码块的内容会写入到 $HOME/.muttrc
文件中, 并覆盖原内容.
其他的子标题也是一样的处理方式:
* Email ** Muttrc ... ** Aliases #+BEGIN_SRC conf :tangle ~/.aliases alias mumon foobar@example.com #+END_SRC ** goobookrc #+BEGIN_SRC conf :tangle ~/.goobookrc [DEFAULT] # The following are optional, defaults are shown # This file is written by the oauth library, and should be kept secure, # it's like a password to your google contacts. ;oauth_db_filename: ~/.goobook_auth.json # The client secret file is not really secret. ;client_secret_filename: ~/.goobook_client_secret.json ;cache_filename: ~/.goobook_cache ;cache_expiry_hours: 24 ;filter_groupless_contacts: yes #+END_SRC
现在当你执行 org-babel-tangle
后,会产生3个dotfile. 你可以将这个org文件纳入到版本控制系统中,在Emacs中编辑dotfile然后一键发布它们.
deploy them with one command.
1.3 1.3 Dotfiles in a dotdir
假设,你想管理你的存放在 .ssh
目录下的SSH配置文件. 它的管理方式大致上与上面的没什么两样.
不过如果你是在一台新机器上,尚没有 .ssh
目录,那Org-mode在tangle时会报错.
这种情况下,你需要在文件的开头写上一行:
# -*- eval: (make-directory ".ssh" "~") -*-
你也可以在文件开始前创建一个代码块来帮你运行创建目录的命令.
#+BEGIN_SRC sh mkdir ~/.ssh #+END_SRC
不过,最好的方法还是设置header argument mkdirpl
为 yes
. 像这样:
#+BEGIN_SRC sh :mkdirp yes :tangle ~/.ssh/config (your .ssh/config contents) #+END_SRC
这样一来,若没有 .ssh
目录的话,Org-Mode会自动帮你创建.
1.4 1.4 Emacs dotfiles
如果你是采用的老旧的 .emacs
或 init.el
来存放你的Emacs配置,那么管理的方法跟上面说的一样.
然而,若你的配置本来就是嵌入到其他.org文件中的,我想你应该不会喜欢把这些org文件的内容再放到 dotfile.org
中的org代码块中.
我的解决方案是在 home.org(我的dotfile)
所在目录下创建一个目录来存放这些Emacs dotfile.
这种方法不太完美,不过我目前也没有其他什么好的方法了. 这还真是讽刺啊.
1.5 1.5 Credentials and secrets
若你把你的dotfile放到网上去,那么你需要给dotfile加密. 好在,Emacs对加解密文件支持得很好.
你把那些不想公开的东西写入一个特定的org问你就暗中,然后用 epa-encrypt-file
来将该文件进行加密,加密后的文件以 .gpg
为后缀. 然后你就可以删掉明文的org文件了.
下一次你在编辑加密过的 .org.gpg
文件时, Emacs会通过 gpg-agent
提示你输入密码,然后自动为你解密.
2 2 Documenting changes
上面的方法能够让你简单而快速的把所有的dotfile都放到少量的org文件中去. 但是仅仅这样还不够. 用Org-mode来管理dotfile的好处更在于能够无缝的整合文档说明,并且能够快速的进行部署.
你可以把一段非常长的配置内容细分到多个代码块中去,然后在部署时将这些代码块中的内容整合到一个文件中去. 你甚至可以把这些代码块放置在不同的子标题下.
这种情况下, 你无需为每个代码块的属性(官方文档称之为head arguments)都设置以此要写到哪个文件中, 你可以直接在标题的属性中(为该标题下的所有代码块)指定该文件的路径. 下面举个例子:
* Git :PROPERTIES: :tangle: ~/.gitconfig # <- all src blocks under this 'Git' subtree will be written to ~/.gitconfig :END: ** personal information #+BEGIN_SRC conf [user] name = John Doe email = john.doe@example.net #+END_SRC ** push settings #+BEGIN_SRC conf [push] default = upstream #+END_SRC ...
当你tangle该文件时,所有 * Git
子树下的代码块都会被写入到 $HOME/.gitconfig
中.
(译者注:新的org-mode下需要把 :tangle: ~/.gitconfig
改成 :header-args: :tangle ~/.gitconfig
)
现在假设我们要修改一下Git的推送配置,我们可以很方便的定位到相关位置然后做出一些修改:
** push settings #+BEGIN_SRC conf [push] default = simple #+END_SRC
过了一段时候后,你可能就会忘记改了哪部分内容了. 那么为什么为这次的修改留下一些笔记呢?
** push settings #+BEGIN_SRC conf [push] default = simple #+END_SRC [2016-03-19 Sat 22:31] change push default from 'upstream' to 'simple'.
你可以在几乎任何位置通过按下 C-u C-c !
来插入一段时间戳. 如果你只需要插入日期而不关心具体的时间,那么你可以按下 C=c !
来插入.
如果你经常需要修改某项配置,可以考虑为所有可能的选项加上说明:
** push settings With ~push.default~ set to ~simple~, ~git push~ will fail if the current local branch is not tracking a remote branch, even if remote has a branch with the same name. This seems to be the safest option. Other possible values are: - ~upstream~: push the local branch to its upstream branch. - ~current~: push the local branch to a branch of the same name. #+BEGIN_SRC conf [push] default = simple #+END_SRC [2016-03-19 Sat 22:31] change push default from 'upstream' to 'simple'.
有时,你可能想体验一下各类选项的组合效果,你可以将这些组合都写下来,然后告诉Org-mode不要tangle其中的某些代码块:
safe_threshold=1 encryption_mechanism=ECDHE_RSA
safe_threshold=0 encryption_mechanism=HMAC-SHA1
上面例子中,只有后面的代码块会被写入配置文件中.
3 3 Managing remote dotfiles and configs
你可以为 :tangle
这个head argument或者其对应的子树属性赋一个远程主机上的位置,一般来说这个主机你能够用SSH访问.
假设你在管理一台web服务器,你可以通过Org-mode来管理它的相关配置而无需远程登录到web服务器上编辑文件:
* Nginx :PROPERTIES: :tangle: /webadmin@ssh.example.org:configs/nginx.conf #+BEGIN_SRC conf worker_processes 4; events { worker_connections 1024; } ... #+END_SRC #+BEGIN_SRC sh :dir /ssh:webadmin@ssh.example.org|sudo:ssh.example.org :tangle no cp /home/webadmin/configs/nginx.conf /etc/nginx/ chown nginx:nginx /etc/nginx/nginx.conf #+END_SRC
第一个代码块的内容会写入到远程文件 /home/webadmin/nginx.conf
中, 二第二个代码块由于参数 :tangle
为 no
因此不会tangle到任何文件中去.
不过你可以在本地的Emacs中运行这段代码,它会提示你输入sudo的密码,然后拷贝配置文件到指定的地方,并设置好配置文件的宿主.
4 4 Caveats
- Emacs是单线程的. 如果你是通过一个非常缓慢的链接来tangle远程文件或执行远程shell,可能会有明显的延迟.
你没有用到符号链接,自然也就享受不到符号链接带来的好处. 假设你通过命令
git config --global ....
修改了Git的配置,那么这个修改并不能自动同步到你的Org文件中去. 你需要手工更新你的dotfile.org
.UPDATE: Ken Mankoff告诉了我一个技巧可以部分的解决这个问题. 你可以在你的dotfile.org的顶端加上这么一行:
#+PROPERTY: header-args:conf :comments link :tangle-mode (identity #o444)
用Ken自己的话说就是: "这使得tangle出来的文件是只读的,这就防止我一不小心错改了这些文件. 我还在没份代码前创建了一份注释链接,这样我就可以直接从这份dotfile直接跳转到相对应的原Org文件中去"
- 你的
dotfile.org
可能会变得很庞杂. 不过这对大多数人来说并不是什么大不了的问题. 在我的 late-2012 MacBook Pro上打开一个几百K的Org文件,跟打开一个新文件没啥区别. 不过随着时间的推移,文件中可能会被log和文档说明所充斥. 这是你可能需要按标题将原文件划分成多个org文件. 不过在Org-Mode中创建不同文件之间的链接很容易,你也可以很方便的在这些文件中之间跳转.
5 5 Acknowledgements
这套系统是受到了 Sacha Chua 的Emacs配置 所启发. 我也是在看到Howardism关于 literate devops 的演讲之后才意识到原来Org-Mode可以作为这么强大的系统管理工具来用的. 最后,十分感谢那些创建和维护 Org-Mode, Tramp 以及 GNU Emacs的人.