DEV Community

Mpho Mphego
Mpho Mphego

Posted on • Edited on

How I increased my productivity using dotfiles [updated]

Originally published at blog.mphomphego.co.za on February 28, 2019.


Updated: 17 March 2019
Added demonstrational video for my new-computer set-up script.
Click Gif: demo


How I increased my productivity using dotfiles

TLDR; You can set up a new system using dotfiles and an installation script in minutes. It’s not hard to create your own repository, and you’ll learn a ton along the road. This is truly more about the journey than the destination!

There is no frustrating feeling like the feeling of starting all over again especially when it comes to a fresh OS install or having your hard-drive crashing on you which then results in reinstalling your OS all over again!

And you think to yourself, OMG! my aliases, system settings and configuration files, every little helper file and script GONE, just like that!

That new fresh OS install on your new computer is a shell of its former self, everything fresh out of the box - How annoyed you will/would be.

Your heart warms as you think back to the comfort and productivity that came with your computer before. If only there was a way to take everything you know and love on the go and never to worry about the agony of reinstalling everything...

Thankfully, there is!


Overtime I got annoyed and frustrated to the point where I created my own linux setting up script that auto-installs everything I needed from packages, dotfiles, configuration files and settings.

If you would like to check it out, [new-computer] - should you have nice ideas to share or want to collaborate, feel free to send me a tweet @orifhampho or fork and send me a PR!

Automate Everything!

There are two parts to this:

  • The first is a repository/backup of my dotfiles & configs.

    This repository contains, and track changes for most of my important files in my /home partition, such as .bashrc, .bash_aliases, and other related files.

    Dotfiles are used to customize your system. The "dotfiles" name is derived from the configuration files in Unix-like systems that start with a dot (e.g. .bash_profile and .gitconfig). For normal users, this indicates these are not regular documents, and by default are hidden in directory listings. For power users, however, they are a core tool belt.
    Backing my dotfiles to GitHub keeps everything neatly tracked and version controlled.

  • The second repository packs a punch, it contains a script which when ran it will install every little package, configs, githooks and dotfiles, I need for my day-to-day productivity from academic, work-related to entertainment.
    installer.sh is what makes this all magically work.
    I will document it in detail on the next post.

My Dotfiles

An Example Dotfiles Repository

For this post, I’m just going to list a subset of my own dotfiles repo.

Current Structure

Below is the structure of my dotfiles repository. It’s also what we’ll use in our walk-through below.

.
├── .config
│   ├── Code
│   │   └── User
│   ├── gummi
│   │   ├── gummi.cfg
│   │   ├── snippets.cfg
│   │   └── welcome.tex
│   ├── Mendeley Ltd.
│   │   └── Mendeley Desktop.conf
│   ├── ranger
│   │   ├── bookmarks
│   │   ├── commands_full.py
│   │   ├── commands.py
│   │   ├── history
│   │   ├── rc.conf
│   │   ├── rifle.conf
│   │   ├── scope.sh
│   │   └── tagged
│   ├── redshift.conf
│   ├── Slack
│   │   └── local-settings.json
│   ├── sublime-text-3
│   │   ├── Packages
│   │   ├── TrailingSpaces
│   │   └── WordHighlight
│   ├── terminator
│   │   └── config
│   └── xfce4
│       ├── helpers.rc
│       ├── help.rc
│       ├── panel
│       ├── terminal
│       ├── xfce4-screenshooter
│       ├── xfce4-taskmanager.rc
│       └── xfconf
├── .dotfiles
│   ├── .bash_aliases
│   ├── .bash_functions
│   ├── .bashrc
│   ├── .docker_aliases
│   ├── .dotfiles_setup.sh
│   ├── .git-completion.bash
│   ├── .gitconfig
│   ├── .gitignore
│   ├── .nanorc
│   ├── .profile
│   └── .travis.conf
├── .ipython
│   └── profile_default
│       ├── db
│       ├── ipython_config.py
│       ├── ipython_kernel_config.py
│       ├── log
│       ├── pid
│       └── startup
├── LICENSE
├── My-Git-Repos.txt
├── Pictures
│   ├── glasses-and-computer-screen.jpg
│   ├── vertical_background.jpeg
│   └── wallpaper.jpg
├── README.md
└── .travis.yml
Enter fullscreen mode Exit fullscreen mode

Diving deep into the dotfiles

We will take a look at the following examples:

  • .dotfiles/.profile
  • .dotfiles/.bashrc
  • .dotfiles/.bash_aliases
  • .dotfiles/.bash_functions
  • .dotfiles/.docker_aliases

.profile

This file is located in your /home directory is loaded first upon login, it is either called .profile or .bash_profile.
What to put in the .profile is truly up-to the individual and it can be expanded significantly. I personally like to keep my .profile as small possible and only have things I need to be ran once.
For example, I define all my colours and colour functions in my .profile

case ${TERM} in
    '') ;;
  *)
    # Define a few Colours
    BLACK="$(tput -T xterm setaf 0)"
    BLACKBG="$(tput -T xterm setab 0)"
    # ....
esac

# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
    fi
fi

# set PATH so it includes user's private bin directories
PATH="$HOME/.venv/bin:$HOME/bin:$HOME/.local/bin:$PATH"
export PATH="$HOME/.poetry/bin:$PATH"

recho() {
    echo "${RED}$1${NC}"
}

gecho() {
    echo "${GREEN}$1${NC}"
}

export -f recho
export -f gecho
Enter fullscreen mode Exit fullscreen mode

Full example: my .profile

If you want to dive into start-up scripts a bit more, Peter Ward explains about Shell startup scripts.

.bashrc

.bashrc is a shell script that Bash runs whenever it is started interactively i.e. when you open a new terminal. It initializes an interactive shell session.
For example, I define my PS1, shell options, key-bindings, aliases, functions and custom message.

home

__git_status_info() {
    STATUS=$(git status 2>/dev/null |
    awk '
    /^On branch / {printf($3)}
    /^Changes not staged / {printf("|?Changes unstaged!")}
    /^Changes to be committed/ {printf("|*Uncommitted changes!")}
    /^Your branch is ahead of/ {printf("|^Push changes!")}
    ')
    if [ -n "${STATUS}" ]; then
        echo -ne " (${STATUS}) [Last updated: $(git show -1 --stat | grep ^Date | cut -f4- -d' ')]"
    fi
}

__disk_space=$(/bin/df --output=pcent /home | tail -1)
_ip_add=$(ip addr | grep -w inet | gawk '{if (NR==2) {$0=$2; gsub(/\//," "); print $1;}}')
__ps1_startline="\[\033[0;32m\]\[\033[0m\033[0;38m\]\u\[\033[0;36m\]@\[\033[0;36m\]\h on ${_ip_add}:\w\[\033[0;32m\] \[\033[0;34m\] [disk:${__disk_space}] \[\033[0;32m\]"
__ps1_endline="\[\033[0;32m\]└─\[\033[0m\033[0;31m\] [\D{%F %T}] \$\[\033[0m\033[0;32m\] >>>\[\033[0m\] "
export PS1="${__ps1_startline}\$(__git_status_info)\n${__ps1_endline}"
# ------------
IP_ADD=`ip addr | grep -w inet | gawk '{if (NR==2) {$0=$2; gsub(/\//," "); print $1;}}'`
printf "${LIGHTGREEN}Hello, ${USER}@${IP_ADD}\n"
printf "Today is, $(date)\n";
printf "Sysinfo: $(uptime)\n"
printf "\n$(fortune | cowsay)${NC}\n"
Enter fullscreen mode Exit fullscreen mode

Full example: my .bashrc

.bash_aliases

A Bash alias is essentially nothing more than a keyboard shortcut, an abbreviation, a means of avoiding typing a long command sequence. Read more. Here are some examples:


alias update='sudo apt-get -y update'
alias upgrade='sudo apt-get -y --allow-unauthenticated upgrade && sudo apt-get autoclean && sudo apt-get autoremove'
alias hist='history --color=always'
alias hist-grep='history | grep --color=always'
alias youtube="$(command -v youtube-dl)"
alias youtube-mp3="$(command -v youtube-dl) -x --audio-format mp3"
alias rsync='rsync --progress'
alias less='less -N'
Enter fullscreen mode Exit fullscreen mode

Full example: my .bash_aliases

.bash_functions

Commands that are too complex for an alias (and perhaps too small for a stand-alone script) can be defined in a function. Functions can take arguments, making them more powerful.


cd() {
    # The 'builtin' keyword allows you to redefine a Bash builtin without
    # creating a recursion. Quoting the parameter makes it work in case there are spaces in
    # directory names.
    builtin cd "$@" && ls -thor;
}

compile() {
     if [ -f $1 ] ; then
         case $1 in
             *.tex)    latexmk -pdf $1               ;;
             *.c)      gcc -Wall "$1" -o "main" -lm  ;;
            # List should be expanded.
             *)        recho "'$1' cannot opened via ${FUNCNAME[0]}" ;;
         esac
     else
         recho "'$1' is not a valid file"
     fi
}

committer() {
    # Add file(s), commit and push
    FILE=$(git status | $(which grep) "modified:" | cut -f2 -d ":" | xargs)
    for file in $FILE; do git add -f "$file"; done
    if [ "$1" == "" ]; then
        # SignOff by username & email, SignOff with PGP and ignore hooks
        git commit -s -S -n -m"Updated $FILE";
    else
        git commit -s -S -n -m"$2";
    fi;
    read -t 5 -p "Hit ENTER if you want to push else wait 5 seconds"
    [ $? -eq 0 ] && bash -c "git push --no-verify -q &"
}

createpr() {
    # Push changes and create Pull Request on GitHub
    REMOTE="devel";
    if ! git show-ref --quiet refs/heads/devel; then REMOTE="master"; fi
    BRANCH="$(git rev-parse --abbrev-ref HEAD)"
    git push -u origin "${BRANCH}" || true;
    if [ -f /usr/local/bin/hub ]; then
        /usr/local/bin/hub pull-request -b "${REMOTE}" -h "${BRANCH}" --no-edit || true
    else
        recho "Failed to create PR, create it Manually"
        recho "If you would like to continue install hub: https://github.com/github/hub/"
    fi
}

Enter fullscreen mode Exit fullscreen mode

Full example: my .bash_functions

.docker_aliases

Similar to .bash_aliases, in this file I defined all my docker run shortcut

# bat supports syntax highlighting for a large number of programming and markup languages
# see: https://github.com/sharkdp/bat
alias bat='docker run -it --rm -e BAT_THEME -e BAT_STYLE -e BAT_TABS -v "$(pwd):/myapp" danlynn/bat'
# A collection of simplified and community-driven man pages.
# see: https://github.com/sharkdp/tldr
alias tldr='docker run --rm -it -v ~/.tldr/:/root/.tldr/ nutellinoit/tldr'
# Simplified docker based markdown linter
# see: https://github.com/mmphego/my-dockerfiles/markdownlint
alias markdownlint='docker run --rm -it -v "$(pwd):/app" mmphego/markdownlint'
# Simplified docker based latexmk
# see: https://github.com/mmphego/my-dockerfiles/latex-full
alias mklatex='docker run --rm -i -v "$PWD":/data --user="$(id -u):$(id -g)" mmphego/latex:ubuntu'
Enter fullscreen mode Exit fullscreen mode

Full example: my .docker_aliases

Other dotfiles

I only listed a subset of dotfiles above, many packages also store their settings in a dotfile, for example

  • .gitconfig for Git
  • .nanorc for nano

Installing the Dotfiles

NOTE: Be careful you should have some dotfiles defined in your /home directory. Before you continue you need to ensure that they are backed up somewhere.
Remember with great power comes great responsibility.

I wrote a dotfiles installation script to automate symlinking the dotfiles in the repo to your /home directory. See this .dotfiles_setup.sh for an example.
Note that the script uses 'ln' instead of GNU stow for symlinking - I will update the script soon.

To install the dotfiles on a new system, we can do so easily by cloning the repo:

cd $HOME
git clone --depth 1 https://github.com/mmphego/dot-files
rsync -uar --delete-after dot-files/{.,}* "${HOME}"
bash .dotfiles/.dotfiles_setup.sh install
# To delete
# bash .dotfiles/.dotfiles_setup.sh delete
Enter fullscreen mode Exit fullscreen mode

This script will backup existing dotfiles and then do the symlinking.

When done, you should just run the test to ensure that the installation was successful.

bash .dotfiles/.dotfiles_setup.sh test
Enter fullscreen mode Exit fullscreen mode

Conclusion

That's it for this blog post, I hope you picked up a thing or 2 from this post.
dotfiles are a very personal thing, Look around at other repositories and start building your dotfiles the way you like. I have always had a knack for searching for various repositories containing dotfiles on GitHub and other blogs, and I have often found something useful. It is always tricky to get everything to work according to your specs but once you have everything under-control you are good to go.

Go build set up your own dotfiles and the next time when your computer crashes/reinstall OS, it won't be that bad!

Feedback

If you have nice ideas to share or want to collaborate, feel free to send me a tweet @orifhampho or fork and send me a PR!

Reference

Top comments (9)

Collapse
 
waynee95 profile image
waynee95

Nice article. It definitely helps so much when you can git clone your configs onto a new machine and you are ready to go. Also helps with having the same setup on each of your machines.

Personally I use GNU Stow to manage my dotfiles. So I don't need to write my own script for that. GNU Stow is really convenient.

Collapse
 
mmphego profile image
Mpho Mphego

A colleague of mine told me about GNU Stow, I think it's about time I look into it, instead of symlinking my files with ln.
Thanks

Collapse
 
chrisrhymes profile image
C.S. Rhymes

Really good idea!

I used to use something similar where I had a git repo of my symlinked dot files. I had a work branch that had all the work proxy settings and a home branch for when I was working from home direct to the WiFi without a proxy server.

Collapse
 
mmphego profile image
Mpho Mphego

I sync all my dotfiles across my work and home machine, it's like using the same machine!!! flawless

Collapse
 
jorinvo profile image
jorin

Nice post :) Thanks for sharing!

I shared an article a while ago explaining how I evolved my install script into an update script to never run into the issue again of having a stale script that doesn't work on a new machine:
dev.to/jorinvo/automate-your-mac-s...

Also, my personal preference is to keep, both, my setup script and my bash config so simple that I'm not afraid of breaking it. I put all bash configs in a single .bashrc file to make it straight forward to find anything and change anything when I feel like.
No matter how we organize the files, I think the point I'm trying to make is, that dotfiles are a living thing which we adjust constantly and we should minimize the friction to change as much as we can :)

Collapse
 
hinchk profile image
Kasey

Thank you very much for this article, well written and a great approach!

Collapse
 
mmphego profile image
Mpho Mphego

You are welcome.

Collapse
 
sobolevn profile image
Nikita Sobolev • Edited

I love dotfiles!

There is something so personal in sharing your configuration with others.
Maybe you will like my setup as well: github.com/sobolevn/dotfiles

Collapse
 
mmphego profile image
Mpho Mphego

Your dotfiles, are life.
I found some gems in there.

Thank you for sharing.