Made by Nathan

programming and projects

Auto-reloading Your .bashrc (or .zshrc)

| Comments

If you often make changes to your ~/.bashrc, you might be sick of typing source ~/.bashrc after every change. It can also be annoying when you switch to a different tab in your terminal, but your new aliases or functions aren’t available until you type source ~/.bashrc.

While I was working on scm_breeze and my dotfiles repo, I grew tired of having to type this command, so I aliased it to sbrc. But I knew I could do better, so I created an auto-reload script that reloads my ~/.bashrc if there are any changes to itself, or any of the files that it loads.

When you run it at the beginning of your .bashrc, it wraps the source and . commands with a function that builds an index of all the sourced files. At the end of your .bashrc, you need to call the finalize_auto_reload function, which:

  • Removes the source and . overrides
  • Sorts the sourced file index and removes duplicates
  • Stores the mtime of the most recently modified source file in a variable
  • Adds the auto_reload_bashrc function to your PROMPT_COMMAND.

Whenever you start a new line in your terminal, the auto_reload_bashrc function reloads your .bashrc if any of the sourced files have changed. Changes are detected by looking up the most recent modification time from all of the sourced files, and comparing that time with the previous value.

My .bashrc sources 28 files from my dotfiles, scm_breeze, and RVM. Running the auto_reload_bashrc function to check for changes only takes 11 ms.

If you make a lot of changes to your .bashrc or .zsh, you can check out my auto-reloading script here: https://github.com/ndbroadbent/dotfiles/blob/master/bashrc/auto_reload.sh

Saving Space in the Terminal With Symbols

| Comments

I’m saving a little space in my terminal by replacing my username and group (ndbroadbent) with a single symbol. I’m doing this in my prompt, as well as in the output of ls commands:

ls and prompt with symbols

(My laptop’s hostname is also represented by a symbol.)

For the ls output, it was a bit tricky to re-justify the username and group columns after substituting my username. I decided to do it in ruby, and then played some ruby golf:

o=STDIN.read;re=/^(([^ ]* +){2})(([^ ]* +){3})/;u,g,s=o.lines.map{|l|l[re,3]}.compact.map(&:split).transpose.map{|a|a.map(&:size).max+1};puts o.lines.map{|l|l.sub(re){|m|"%s%-#{u}s %-#{g}s%#{s}s "%[$1,*$3.split]}}

This little script parses the modified output of ls -lhv, calculates the max length of the user/group/size columns, and then pads those columns with the correct number of spaces.

My final ls command looks like this:

ls -lhv --group-directories-first | sed \"s/$USER/\$(/bin/cat $HOME/.user_sym)/g\" | rejustify_ls_columns

(where rejustify_ls_columns is a function wrapping the ruby script above.)

Automate Your Rails Development With Cron: Keep Projects Updated, Install Gems, and Maintain a Repo Index

| Comments

A day in the life of a Rails developer will usually involve a few git pulls, bundle installs, and switching between different projects. I thought it would be great if my projects could be automatically kept up-to-date, so that I don’t have to spend too much time updating code or installing new gems.

I’m using the whenever gem to create cron tasks that:

  • Update all my git repos from their remotes
  • Satisfy all of my Gemfile’s dependencies
  • Cache rake and capistrano commands for tab completion
  • Fetch Travis CI build statuses to show in my terminal
  • Maintain an index of my git repos so I can quickly switch between projects, while keeping them organized

For all of these tasks, I’m using the git_index function provided by my SCM Breeze project. It creates an index of all your git projects by recursively scanning your code directory, and then lets you quickly jump to projects, or run batch commands for each of your repos. See my SCM Breeze blog post for more info about the repository index.

Updating Git Repos

Every 30 minutes I run a task that updates all of the local branches on all of my git repos. It does this as safely as possible:

  • Doesn’t do anything if there are any changed files in the working directory
  • Doesn’t do anything unless remote and merge branches are explicitly configured for that branch
  • Doesn’t do anything if it cannot ‘fast-forward’ merge a branch (i.e. the current commit is not a parent of the latest commit in the remote repo)

Basically, this means that it will leave you alone if you are working on a feature, or if you’ve committed something that you haven’t pushed yet.

Otherwise, it will bring your branches up-to-date and send a desktop notification:

Git update notification

Installing Gem dependencies

I have a task that runs every hour to ensure that all of my gem dependencies are installed. If someone adds a new gem dependency to your project, it’s great to have that automatically installed when the repo is automatically updated.

I’m using the git_index function to run a script called bundle_check_or_install for each of my git repos. It also sends desktop notifications on update or failure, and doesn’t do anything if the gems are already up-to-date.

If bundle install fails for any reason, it will touch a file at '.skip_bundle_auto_install~ (and exclude that file from git by adding it to .git/info/exclude). Any future attempts to auto-update your gems will abort with the notification that “Previous bundle install failed”. This notification can be disabled if .skip_bundle_auto_install~ contains the string “SILENT”. I do this when I stop caring about old projects, but don’t want to delete or archive them.

Caching Rake and Capistrano commands for tab completion

rake -T and cap -T can take a long time to run, so I run a task every hour to cache the available tasks for all of my projects, and I use these cached tasks to provide tab completion. The tasks in saved to files like .cached_rake_tasks~ and .cached_cap_tasks~. Here’s how I set up the Bash tab completion.

Fetching Travis CI Build Status

I’ve written about this before in my Travis CI Status in Shell Prompt post. Every 30 minutes I run a task to fetch build statuses for all the repos that contain a .travis.yml file. The status is saved in a hidden file called .travis_status~, and displayed in my prompt like this:

Travis CI status in prompt

Updating Git Repo Index

All of the previous tasks depend on the git repo index being up-to-date. It only takes a few seconds to scan through my code directories, so I run this task every minute.

This index lets me keep my code organized in different folders, while also letting me jump to different projects by name:

Git Status With Shortcuts


If you want to set this up for yourself, you’ll need to check out my SCM Breeze project. The schedule.rb that defines these tasks lives in my dotfiles repo, and you can find the scripts I use in the /bin directory.

Post a comment if you have any automation ideas to share!

How to Start a Google Group for a Git Project

| Comments

GitHub is a great way to manage code for an open source project, but it doesn’t provide any way to send a message to all of your watchers. This can be necessary whenever you make certain changes to your application. For example, I recently merged a pull request in Errbit that requires users to run a Rake task next time they update the code. I had no way of notifying our users, so I decided to create a Google Group mailing list for these kinds of notifications.

Creating a Google Group is easy (just go to https://groups.google.com and follow the prompts), but the slightly tricky part is sending invites to everyone who’s interested in your project. I decided to start by inviting all the contributors to the Errbit codebase (there’s 73 contributors at the time of writing this post.)

You can get a list of contributor emails by running git log --format='%ae' | sort -u from your git repo. However, Google Groups only lets you invite 10 emails at a time, so here’s a Bash/Zsh script that will print all of your contributor emails as CSV, in groups of 10:

emails=( $(git log --format='%ae' | sort -u) )
total_groups=$(( ${#emails[@]} / 10 ))

for ((i=0; i <= $total_groups; i++)); do
  echo "Group $((i + 1)):"
  echo "-------------------------------------------------------"
  grouped_emails=( ${emails[@]:$(( $i * 10 )):10} )
  printf "%s, " "${grouped_emails[@]}" | cut -d "," -f 1-${#grouped_emails[@]}
  echo
done

After pasting this script into your terminal, you can copy and paste each batch of emails into the Enter email addresses of people to invite textarea. But be careful to write and save your invite message in your text editor, because if you write it on the page you won’t be able to get it back after sending the first batch of invites.

It will still be quite a tedious process since you’ll need to enter a captcha for each batch, but hopefully this script will save you some time.

Skype Notifications Using Libnotify on Linux

| Comments

Skype on Linux comes with it’s own notification system. It’s not that bad, but it’s not consistent, either. If you want to configure Skype to use libnotify instead, run the following script in your terminal:

(NOTE: You must have installed Skype, and logged in at least once.)

bash < <(curl -s https://raw.github.com/gist/2181122/skype-libnotify.sh)

This downloads and executes the script from this gist: https://gist.github.com/2181122

It fetches the skype configuration from here, which is part of my dotfiles repo.