Provisioning your computer with one command: awesome

营销策划 2016-04-21

Seriously do this right now, it's amazing.

This post is about how I wrote a fantastically useful script. If you have multiple computers, you will be thanking me by the end of it. Bold words
, but what I’m going to describe here is that

The problem

The problem is that, like many of you, I have multiple computers. I have two desktops and two laptops (I just like keeping my old computers, they work), and installing a program on one was always a hassle, because I’d then have to remember to install it on the others and configure it the same way. Not only that, but, when I sometimes had to reformat (for performance, or to solve a problem, or whatever), I had to spend ages getting all the programs and their preferences working just the way I wanted them again.

I needed a better way to do this, and this is the post where I describe that way and how you can do it too. Here it is:

The solution

The solution actually consists of two steps. First, I need to create a git repository to hold the various preferences and assorted dotfiles
in my home directory. The term “dotfiles” refers to the hidden files in each user’s home directory in UNIX, where programs store their preferences, settings, and other user-specific information. They are called that because they are hidden files, and hidden files in UNIX start with a dot.
Storing them in a repository will allow me to save a history of all my application settings and sync them between computers by just pulling and merging. Programmers usually think git is the answer to everything, so this is very stereotypical of me.

The second step is to create a script to install programs and perform system-level tasks. This script will be stored in the repository above, for ease of use.

Of course, this entire solution kind of assumes you aren’t using Windows. If you are, I’m sorry, but at least you have AAA games available, so it’s not all bad.

A repository for dotfiles

Adding our dotfiles to a git repository is pretty simple, although there are helpers for you to get as fancy as you like
. I’ve opted to just create a git repository in my home directory, which can sometimes cumbersome, so I would recommend that you pick a utility that suits your needs from the page above and go with it.

You should keep a few things in mind:

  • Never run git clean
    on your home dir, as it will delete everything (your home is now a git repository, after all). Using one of the utilities above will help with this, since those don’t just turn everything into one huge repository.
  • Be judicious in choosing which files you add to the repository. Don’t just add everything that’s in your home directory, or even most of it, because that will just require unnecessary commits and make your repository larger. It does take a bit more time to figure out where programs store their preferences, but it’s nothing you can’t find out in two minutes, and it will ensure you only sync the things you really need to.
  • If some files contain sensitive information, you can use the excellent git-crypt
    to encrypt them with your GPG key
    If you’re unfamiliar with GPG, here’s a good introduction
    , including details on how you can generate your key.

    This will ensure that nobody but you will be able to read the sensitive information (not even your git host), but be careful not to commit decrypted files, which can sometimes inadvertently happen.

After (some of) your dotfiles are sitting safely in a repository, all you need to do is remember to commit and push the changes every so often. When you switch computers, just git pull, and all your updated preferences will be available there as well!

We are now ready to proceed to phase “awesome”.

Writing a provisioning script

The role of the provisioning script is to bring the system into a specified state, from any starting state. This usually means using the system’s package manager to install packages, as well as checking if things are missing and only then installing them. The way I achieved this is with a series of small scripts, but you should feel free to use whatever tool you feel comfortable with. In my case, I used Ansible
just because it’s very easy to write such a simple script, but anything else (even a simple and portable bash script) would work just as well.

The main script

First, let’s start with the main Ansible script:

# This playbook is meant to provision a new computer.
# Run with: ansible-playbook -i , provision.yml

- name: Provision a machine.
  connection: local

  - name: Add Virtualbox server.
    apt_repository: repo='deb {{ hostvars[inventory_hostname]["ansible_distribution_release"] }} contrib' state=present
    register: ppa
    sudo: yes

  - name: Add repository keys.
    apt_key: id={{ item }}
    register: ppa
    sudo: yes
      - 54422A4B98AB5139  # Virtualbox

  - name: Add PPAs.
    apt_repository: repo='{{ item }}'
    sudo: yes
    register: ppa
      - ppa:webupd8team/java
      - ppa:fish-shell/release-2

  - name: Update the apt cache.
    apt: update_cache=yes
    sudo: yes
    when: ppa.changed

  - name: Install Mac packages.
    apt: name={{ item }}
    sudo: yes
     - macfanctld
    when: '"Apple" in hostvars[inventory_hostname]["ansible_system_vendor"]'

  - name: Install packages.
    apt: name={{ item }}
    sudo: yes
     - adobe-flashplugin
     - aptitude
     - aria2
     - build-essential

  - name: Run bootstrap script.
    shell: ~/bin/provisioning/scripts/bootstrap

  - name: Run settings script.
    shell: ~/bin/provisioning/scripts/settings

  - name: Change shell to fish.
    sudo: yes
    user: name={{ hostvars[inventory_hostname]["ansible_user_id"] }} shell=/usr/bin/fish groups=dialout,disk append=yes

That’s most of the actual file I use. I’ve omitted some packages for brevity, but you can see that what I’m doing is adding some non-PPA apt repositories, adding their keys, then adding the PPAs, updating the cache to refresh everything above, installing Mac-specific packages (one of my computers is a Macbook Air), installing all the packages I use, running two scripts that I’ll get to shortly, and changing my shell to the best shell in existence, fish

Just copy and paste that thing right into a file. I have a ~/bin/
directory I put executable scripts in (and add some of them to my dotfiles repo above), but you can also customize this. I put the script above in ~/bin/provisioning/provision.yml
and use the following little bash script ( ~/bin/provision
) to run it:


/usr/local/bin/ansible-playbook -K -i , ~/bin/provisioning/provision.yml

This lets me just type provision
into a terminal, and my computer is soon synchronized to the latest state.

The bootstrap script

Some changes don’t fit (or aren’t practical to do) with the above Ansible script, so I wrote another short bash script to perform those. It mainly clones repositories and builds the software in them:


# Install git-crypt.
if [ ! -f ~/bin/git-crypt ]; then
    echo "Installing git-crypt..."

    cd /tmp/
    git clone
    cd /tmp/git-crypt/
    cp /tmp/git-crypt/git-crypt ~/bin/

    cd ~
    ~/bin/git-crypt unlock

# Install Vundle.
if [ ! -d ~/.vim/bundle/vundle ]; then
    echo "Installing Vundle..."
    git clone ~/.vim/bundle/vundle
vim +PluginInstall +qall

# Install Powerline fonts.
if [ ! -f ~/.fonts/DejaVu Sans Mono for Powerline.ttf ]; then
    echo "Installing Powerline fonts..."
    git clone /tmp/fonts/
    cd /tmp/fonts

# Install virtualfish.
if [ ! -d ~/.config/fish/virtualfish ]; then
    echo "Installing virtualfish..."
    git clone /tmp/virtualfish/
    mv /tmp/virtualfish/virtualfish/ ~/.config/fish/

You can just download and run the above script now, and it will set up various useful programs for you, including git-crypt

The configuration script

The final piece of the puzzle is the configuration script. This sets various options in gconf that can’t easily be set elsewhere.

To discover these settings, I used dconf to watch the root key (with dconf watch /
and toggle the option I wanted to store. dconf then printed the key and value, which I just stuck into this file.

The following script will also swap your Caps Lock and Ctrl keys, which I highly recommend, and which you will love, once you get past the first half day of discomfort.


# Set various system options.
gsettings set org.gnome.desktop.session idle-delay "uint32 300"
gsettings set org.gnome.settings-daemon.plugins.power idle-dim true
gsettings set org.gnome.desktop.screensaver lock-enabled true
gsettings set org.gnome.desktop.screensaver lock-delay "uint32 0"
gsettings set org.gnome.desktop.screensaver ubuntu-lock-on-suspend true
gsettings set org.gnome.desktop.input-sources xkb-options "['ctrl:swapcaps']"
gsettings set org.gnome.settings-daemon.plugins.xsettings overrides "{'Gtk/EnablePrimaryPaste': }"
gsettings set org.gnome.desktop.wm.preferences resize-with-right-button true
gsettings set terminal 't'
gsettings set home 'e'
gsettings set org.compiz.integrated show-hud "['']"
gsettings set com.canonical.indicator.sound visible true
dconf write /org/compiz/profiles/unity/plugins/unityshell/show-launcher "''"

# Change default file manager to Nemo.
xdg-mime default nemo.desktop inode/directory application/x-gnome-saved-search

# Gnome-Do
gconftool-2 --type bool --set /apps/gnome-do/preferences/Do/CorePreferences/QuietStart true
gconftool-2 --type string --set /apps/gnome-do/preferences/Do/Platform/Common/AbstractKeyBindingService/Summon_Do "space"

# Clock tray
dconf write /com/canonical/indicator/datetime/show-day true
dconf write /com/canonical/indicator/datetime/show-date true
dconf write /com/canonical/indicator/datetime/time-format "'24-hour'"
dconf write /com/canonical/indicator/datetime/show-seconds true
dconf write /com/canonical/indicator/datetime/show-auto-detected-location true

Setting up a new machine from scratch

Now that everything above is in place, a great benefit we reap immediately is that we can now set it up to our exact preferences with a single command. The one simple step that we need to take to perform this amazing, single-command feat is:

  1. Clone the dotfiles repository to the new home directory (or just download the zip file). This will probably require that we have copied our SSH keys to this machine in step 0.5.
  2. Decrypt the repository, if it’s encrypted with git-crypt. This will require that we copy our GPG keys over to the new machine in step 1.5.
  3. Run the provision
    command, which will download all the programs, whose settings have been put in place by step 1.
  4. That’s it! Everything is now working as if we’ve always used this computer, with one very easy step.
  5. We’ve always been at war with Eurasia.


I’ve been using this for around a year now, and I’m kicking myself for not having done it earlier. This setup makes it trivial to format any of my PCs and have it ready to go again within the hour. The biggest benefit, however, is that I don’t have to spend time remembering how I set up one or the other computer, or have slightly different preferences from one machine to the next. All my computers have the exact same configuration without me needing to perform any setup twice, and everything stays up to date as I make any change on any of them.

I highly recommend that you try it out, if only for a few days, as it’s pretty simple to get started. I’m sure you’ll love it enough that you’ll be wondering how you lived without it, like I did.

If you have any feedback, or if the way that I jump from past to present tense or first person singular to first person plural in my writing bothers you, please send me a tweet
or leave a comment below. I’ve always wondered how annoying and/or noticeable it is to readers.

责编内容by:Stavros' Stuff (源链)。感谢您的支持!


起底Git——开篇 最近整理了一份git的分享,从如下六个方面对git进行了讲解,我在内部试着讲了一下效果还不错, 感兴趣的可以约我进行团队培训(价格不菲),建议大家自学。 ...
Publishing npm Packages Using CircleCI 2.0 This is a guest post written by Armando Canals, co-founder at packagecloud . ...
Asciinema – Record Terminal Sessions And Share The... I already know about recording the Terminal activities using “script” command...
Like grep, but for binaries bingrep Greps through binaries from various OSs and architectures, and colo...
Linux命令行与脚本笔记:理解子shell 2017.6.26 ps -f 命令能够表现子shell的嵌套关系。 添加 ; 来分隔命令可以指定依次运行的一系列命令。 ...