giant robots smashing into other giant robots

We are thoughtbot. We make web & mobile apps.

Tagged:

Comments (View)

I learned to alias shell commands with Hooked on Phonics

Do your co-workers snicker at your sentence-length shell aliases? I’m here to tell you it’s okay because my aliases border on the Dostoyevskian, too.

Some commands you do a thousand times a day. They deserve super-short aliases:

alias be="bundle exec"
alias s="bundle exec rspec"
alias cuc="bundle exec cucumber"

Like h() or t() in Rails, the more often you invoke a command, the more acceptable it becomes to have a short, cryptic alias.

However, there’s a class of commands I find myself running about 1-20 times a day: interacting with our staging and production environments on Heroku:

# Heroku staging
alias staging='heroku run console --remote staging'
alias staging-process='watch heroku ps --remote staging'
alias staging-releases='heroku releases --remote staging'
alias staging-tail='heroku logs --tail --remote staging'

# Heroku production
alias production='heroku run console --remote production'
alias production-process='watch bundle exec heroku ps --remote production'
alias production-releases='heroku releases --remote production'
alias production-tail='heroku logs --tail --remote production'

# Heroku databases
alias db-pull-staging='heroku db:pull --remote staging --confirm `basename $PWD`-staging'
alias db-pull-production='heroku db:pull --remote production --confirm `basename $PWD`-production'
alias db-copy-production-to-staging='heroku pgbackups:restore DATABASE `heroku pgbackups:url --remote production` --remote staging  --confirm `basename $PWD`-staging'
alias db-backup-production='heroku pgbackups:capture --remote production'
alias db-backups='heroku pgbackups --remote production'

Here’s where Neckbeard next to you silently pities your Fisher-Price programming style. What self-respecting programmer would type all that?!

Well, through the magic of autocompletion, you never type more than a few characters for each command. They might look goofy, but they’re memorable.

If you’d like these aliases and other goodies, they’re packaged up in our dotfiles.

Then, the next time you feel your pair programming partner smirking over your shoulder, just tell ‘em: “I learned to alias shell commands with Hooked on Phonics!”

Tagged:

Comments (View)

capybara-webkit doesn’t like #fork

Recently, when helping to convert a feature suite to use capybara-webkit, we ran into a problem where, about halfway through, the tests would start erroring out with Errno::EPIPE.

Errno::EPIPE is a “Broken Pipe” error. A pipe in Unix is a way for data to flow from one process to another, and a broken pipe means that one end of the pipe isn’t connected anymore. In this case, the pipe was broken because the webkit-server process that capybara-webkit uses went away. And since well-behaved processes don’t normally go away unless you ask them to, we assumed it was crashing.

With a little of digging, we found the feature that was causing the problem. Annoyingly, the error never popped up when the feature was run alone, meaning it was the interaction of multiple features or steps. After whittling away the options, we found the right situation to force the error: a feature using capybara-webkit followed by one specific scenario in a different feature (which, oddly, didn’t use any Javascript at all). Being able to reliably reproduce the error is a big step towards finding a solution.

Knowing what scenario we were dealing with, we could find the specific step that was the source of the problem. The step makes a call to fork. The way that capybara-webkit closes the browser when the tests are over is by using an at_exit hook, which is called when Ruby is just about to close. Forking a process clones everything about it, including, in the case of Ruby, its at_exit hooks. So, what happened was that the fork executed what it needed to execute and then exited like normal, but it meant that it took the webkit-server process with it because of the at_exit hooks it inherited, leaving no browser to run the javascript-enabled features.

This was good news, though. The webkit-server process wasn’t crashing! It was behaving exactly like it was expected to, but just a little premature. To get around the at_exit hooks, we call exit! in the forked process, which specifically bypasses at_exit hooks. This lets our fork get on with its work, and it keeps the server running for the rest of the test suite to use.

So, if you’re in the position where you have to fork in a cucumber suite and you’re using capybara-webkit, make sure you exit! from the fork, and you’ll save yourself some headaches. If you can help fix this problem for good, pull requests are welcome.

Tagged:

Comments (View)

Font Battle 2011

A recent straw poll of text editor fonts at thoughtbot found these results. Not all thoughtbot precincts have reported in. Perhaps you’d like to explore some new font options for those walls of text you stare at for 8+ hours daily.

Some links to these fonts:

Tagged:

Comments (View)

Keeping a git fork updated

This isn’t muscle memory for me yet so I’m writing it down to refer to it later. It isn’t rocket science but it might help someone else, too.

This is for the case where you have a fork (say, croaky/dotfiles) of a main repo (say, thoughtbot/dotfiles) and you want to keep your fork updated.

Track

After you’ve forked the repo to your Github account, one time:

git clone git@github.com:YOU/REPO.git
cd REPO
git remote add upstream git@github.com:UPSTREAM/REPO.git

YOU should be replaced with your Github name, UPSTREAM means the name of the Github user who manages the upstream repo, REPO is the repo name.

Update

Each time you want to update, from your local master branch:

git fetch upstream
git rebase upstream/master

The goal of the rebase is to have a cleaner history if you have local changes or commits on the repo. It’s the difference between the the left and the right in the image below.

Commit rights upstream?

If you also have commit rights to the upstream repo, you can create a local upstream branch and do work that will go upstream there.

git checkout -b upstream upstream/master

Tagged:

Comments (View)

Streamline your git workflow with aliases

My git workflow is heavily influenced by articles such as Rein’s or the topic branches workflow on the excellent ProGit book. The idea is simple:

1. Start a story with a new branch.

From the master branch, run

git checkout -b feature_x

2. Hack and commit often in that branch

vi file.rb
git add -u
git add .
git status
git commit -m "Make changes"

3. Pull in upstream changes as often as possible

There are many ways to do this. I like this method because it doesn’t require me to checkout or merge the master branch. Instead, just fetch the references from the remote and rebase it against your feature branch. Note that the reference we’re rebasing against is origin/master, so that we only bring in changes from that branch.

git fetch
git rebase origin/master

4. Clean up the history by squashing commits

Once again we fetch changes from the remote, and then we do an interactive rebase against origin/master. This ensures that we don’t modify any changes that already found a home upstream.

git fetch
git rebase -i origin/master

5. Merge into master, run all tests and push upstream

We fetch and rebase again, just in case there have been changes since the last step, and then we merge it all into master and push it out if all tests pass.

git fetch
git rebase origin/master
git checkout master
git merge feature_x
rake
git push

I prefer not to delete the local or remote feature branches right away just in case. Instead, I’ll go on some cleanup missions every once in a while.

Working this way for a while, you start to identify repetitive keystrokes. You can cut down on these keystrokes with shell aliases or functions, or with git aliases. I like using git aliases because the commands themselves provide context: git related commands start with git.

Here are the aliases I use everyday that support this git workflow. Place these in your ~/.gitconfig.

[alias]
  c = commit -m
  a = add
  aa= !git add -u && git add . && git status
  cob = checkout -b
  up = !git fetch origin && git rebase origin/master
  ir = !git rebase -i origin/master
  done = !git fetch && git rebase origin/master && git checkout master && git merge @{-1} && rake && git push

Having those in place, let’s see what the very same workflow looks like:

1. Start a story with a new branch

git cob feature_x

2. Hack and commit often in that branch

vi file.rb
git aa
git c "Make changes"

3. Pull in upstream changes as often as possible

git up

4. Clean up the history by squashing commits

git ir

5. Merge into master, run all tests and push upstream

git done

Now we’re talking. From the convention of how we work with git, we’ve augmented our tools to make it a whole lot easier, and reduce the risk of modifying upstream history by always ensuring we never change it.

But aliases can take you even further. For one, I use a shell alias for git: g=git, so now the commands above simply look like g aa, g c "Make changes", etc. There are also a few useful tidbits you can use. One that comes in handy often is when you want to commit changes as part of the prior commit, and want to keep the original commit message. Similar to creating a new commit and then squashing them.

ca = commit --amend -C HEAD

What about when you’re ready to delete feature branches. You want to remove the local and remote references. Try g rmb feature_x:

rmb = !sh -c 'git branch -D $1 && git push origin :$1' -

Or if you want to see who has contributed to a project, add this alias and just ask with g who

who = shortlog -n -s --no-merges

Finally, it’s a good idea to clean up your git repo every once in a while. This alias will remove remote branch references that no longer exist, cleanup unnecessary git files, remove untracked files from the working tree and clear out your stash - all with a simple g cleanup (hat tip to @qrush)

cleanup = !git remote prune origin && git gc && git clean -dfx && git stash clear

There are many more possibilities. How about you: What are your most useful git aliases?