Evan Sims

Using Sublime Text 2 (OS X) for git Commit Messages

Turns out you can invoke Sublime Text 2 to write your git commit messages, if you so desire.

mkdir -p ~/bin
ln -s "/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl" ~/bin/subl
git config --global core.editor "subl -w"

Try using git commit -a on a dirty repo. If you hit an error, open your ~/.bash_profile and add:

PATH=$PATH:~/bin
export PATH

Open a fresh shell and try again.

Useful? Maybe, for some. An interesting exercise none the less.

Shortlink

Automated Cloud Backups with SpiderOak

Almost 9 months after the fact, my post about automating server backups to SpiderOak has proven to be popular amongst Googlers, so I thought it was time to update it with the latest build of my script.

If you don’t have SpiderOak yet, you can download it using this link to start with 3GB of space. If you decide you want to purchase a plan for more storage space, I recommend checking out their Facebook page where they currently have a promo code there worth 20% off all of their plan options.

You can find instructions for setting up the Linux client on their site.

Next, download, configure and save my backup script to your server. The latest version of this script can be found on my /scripts repo at GitHub, and I warmly welcome any contributions users make to it, be it bug fixes or new features.

#!/bin/bash

#  Automated Cloud Backups to SpiderOak
#  -----
#  Feel free to use this in any environment and
#  modify it to your heart's content.
#  -----
#  Author: Evan Sims <hello@evansims.com>
#  Updates: https://github.com/evansims/scripts/

##### START OF CONFIGURATION ####

  # MySQL Database:
  mysqlUser=""
  mysqlPass=""

  # MongoDB Database:
  mongoUser=""
  mongoPass=""
  mongoHost=""

  # Backup target directory:
  backup="/home/username/backups/"

  # Backup these directories or files:
  folders[0]="/home/username/files/"
  folders[1]="/var/logs/"

  # If you're doing backups to a git repo and want to commit
  # these updates, enable this.
  gitCommit=0
  gitPushRepo="" # git push $gitPushRepo

  # 7 day backups are the default.
  # Your $backup directory will be filled with Monday, Tuesday, etc. folders.
  # Each weekday folder will be overwritten when the backup cycles around.
  stamp=$(date -u +%A)
  backupCurrent=$backup$stamp

  # OR uuncomment the following lines for ~30 day backups.
  # Your $backup directory will be filled with folders labled 01 through 31
  # representing each day of the month.
  #stamp=$(date -u +%d)
  #backup=$backup$stamp

  # OR uncomment the following lines for unlimited backups.
  # Your $backup directory will have a new folder created for each day
  # a backup is built. Keep an eye on space usage if you're going to use
  #this method.
  #stamp=$(date -u +%F)
  #backup=$backup$stamp

##### END OF CONFIGURATION ####

temp="$PWD/backup.tmp"
echo "Starting Backup..."

if [ ! -d "$backupCurrent" ]; then
        mkdir "$backupCurrent"
fi

if [ ! -d "$backupCurrent/mongodb" ]; then
        mkdir "$backupCurrent/mongodb"
fi

if [ ! -d "$backupCurrent/mysql" ]; then
        mkdir "$backupCurrent/mysql"
fi

if [ -z $mongoUser ] && [ -z $mongoPass ]; then
  echo "-----"
  echo -ne "Exporting MongoDB collections ... "
  mongodump -h $mongoHost -u $mongoUser -p $mongoPass -o "$backupCurrent/mongodb" 2>"$temp" >/dev/null
  tar -cjf "$backupCurrent/mongodb.tar.bz2" "$backupCurrent/mongodb/"* 2>"$temp"
  rm -R "$backupCurrent/mongodb/"*
  mv "$backupCurrent/mongodb.tar.bz2" "$backupCurrent/mongodb/mongodb.tar.bz2"
  echo "Done."
fi

if [ -z $mysqlUser ] && [ -z $mysqlPass ]; then
  echo "-----"
  echo -ne "Preparing to backup MySQL ... "
  databases=( $(mysql -u"$mysqlUser" -p"$mysqlPass" --skip-column-names --batch -e "show databases;" 2>"$temp") );
  echo "found ${#databases[@]} databases.";

  for i in ${databases[@]}; do
          if [ $i != "information_schema" ] && [ $i != "mysql" ] && [ $i != "phpmyadmin" ]; then
                  echo -ne "Optimizing and backing up database $i ... "
                  mysql -u"$mysqlUser" -p"$mysqlPass" -D "$i" --skip-column-names --batch -e "optimize table $i" 2>"$temp" >/dev/null
                  mysqldump -u"$mysqlUser" -p"$mysqlPass" --opt $i | bzip2 -c > "$backupCurrent/mysql/$i.sql.bz2"
                  echo "Done."
          fi
  done
fi

echo "-----"
for f in "${folders[@]}"
do
        if [ -d "$f" ]; then
                echo -ne "Backing up directory $f ... "
                d=$(basename $f)
                d="$d.tar.bz2"
        elif [ -f "$f" ]; then
                echo -ne "Backing up file $f ... "
                d=${f##*/}
                d="$d.tar.bz2"
        else
                echo "Error! $f could not be found."
                exit 1
        fi

        tar -cjf "$backupCurrent/$d" -C / ${f:1}
        echo "Done."
done

if [ ! -s $temp ]; then
        rm -f "$temp"
fi

if [ -z $gitCommit ]; then
  cd $backup
  echo "-----"
  echo "Committing changes to git ... "
  git add .
  git commit -m "Backup $stamp"

  if [ -z $gitPush ]; then
    git push $gitPush
  fi

  echo "Done."
fi

echo "-----"
echo -ne "Pushing data to SpiderOak ... "

SpiderOak --empty-garbage-bin 2>"$temp" >/dev/null
SpiderOak --batchmode 2>"$temp" >/dev/null
SpiderOak --purge-historical-versions all 2>"$temp" >/dev/null
SpiderOak --vacuum 2>"$temp" >/dev/null

echo "Done."

echo "-----"
echo "Backup Complete!"

exit 0

Setup this script to run as a Cron task once a day, during a period of time that your machine won’t be being actively used. In the case of a web or database server, I’d recommend graphing your access logs and seeing when you have the lowest point of traffic on average, and run it during this time.

Although this script is designed for Linux, and makes use of the SpiderOak Linux client’s command interface, I believe the OS X client supports the same commands. The script could potentially be modified to support Mac with relative ease.

I hope folks out there find this useful. Have fun.

Shortlink

Life Without Google Reader

The recent Google Reader update brought the new “universal design” we’d all been anticipating, but it also unexpectedly removed all of the internal/inline sharing and commenting functions my friends and I (and I’m sure many others) had used on a daily basis for years. Reader has swept it all out in favor of a generic +1 button, the equivalent of a tweet or Facebook like button on any blog.

To be fair, I like the idea of Google integrating Google+ into Reader. I really dig Google+ as a social network. My issue is the way they’ve gone about it. Whereas previously in Reader I could read my articles, share what I like, see what my friends shared, and comment on it all from within a single unified interface– now I must go to two wholly different sites to do it. And quite frankly Google+ has far too much noise (as do all social networks) to be a fair alternative to what we had in Reader before. Google+ should have been a baked-in augmentation to Reader. When I share something in Reader, share it on Google+ too. When someone comments on it in either location, show it in both places. Google got this right with Buzz. It might have been the only thing they got right with Buzz.

So, with no reason to keep using Reader, I decided to export my OPML and move on to green pastures.

I’ve ended up using the outstanding Fever by Shaun Inman. It’s allowed me to port all of my feed consumption to an internal, locally hosted environment. I really love this for a variety of reasons, privacy being a big one. I combine it with Fluid for dock notifications.

I’m using a Pinboard feed to share stories of interest to my friends, allowing them to still get my bizarre finds in their reader of choice’s stream, right beside their other feeds. In Fever, I assigned a sharing hotkey to ‘P’ to quickly share the stories I’m digging.

For shares that I think are worthy of a highlight, I take the extra effort to share those on Google+, Facebook and Twitter— though depending on the content I may not share them on all 3 at the same time. (I’ve learned that my followers on each of these networks are fairly unique in the types of topics they’ll respond to. I’m not interested in boring anyone.) I’m using Buffer to try and throttle things a bit and not overload anyone.

Inversely, to consume my friends’ shares, I’m using a very clever appspot tool that generates RSS feeds from Google+ activities. Drop in your friend’s G+ ID and boom, instant RSS. So once again I can see their shares alongside my feeds.

As a nice bonus, because Fever’s “Hot” algorithm figures out what the hottest news of the day/week/whatever is based on the links it’s parsing in my feeds, my friend’s shares actually help to personalize my headline news each day for my tastes. That’s pretty brilliant. It’s spot on at finding things I’m interested in. Much better than Reader’s “magic” sorting function.

The obvious issue in this situation is the lack of integrated commenting. I’ve been working on a Chrome extension for injecting a custom Facebook Comments thread into Fever and Google Reader to let my friends and I communicate on a story-by-story basis, but it’s far from an ideal solution. In the meantime, I guess Google has won, as we’re primarily using Google+ to discus things. Which is fine. I just wish I didn’t have to leave my reader to do it.

Shortlink

Fixing World of Warcraft with Synergy

Continuing my trend of tips blogging recently, here’s one more: a fix for playing World of Warcraft on Synergy clients. If you’ve ever tried playing Warcraft on a synergy client, you’ll no doubt have encountered the “crazy camera issue” in which, when attempting to rotate the perspective, the camera spins radically out of control around your character. It makes playing Warcraft practically impossible in a Synergy setup. In fact, you could encounter this issue in any number of games that use “mouse look” control, such as Deus Ex: Human Revolution. Here’s a quick fix that worked for me using a Mac OS X Lion (10.7.1) server and a Windows 7 SP1 client running the game.

Note: I strongly recommend using 1.4.3 on the PC and no higher than 1.3.6 on a Mac, as the Mac builds are currently suffering from a bizarre issue where keyboard input will randomly lock up and cease to work.The developer’s haven’t tracked down the cause yet, but 1.3.6 is the latest Mac build that doesn’t contain the bug. At the time of this writing 1.4.3 (beta branch) and 1.3.7 (stable branch) are presently the latest builds, but both contain said bug, as do the latest nightlies.

This fix comes down to two server-side tweaks: enabling relative mouse movements and screen locking. You need to edit your synergy.conf file to include support for these. Users with GUI wrappers for synergy will need to consult their package’s documentation on how to do this. Here’s what my configuration file looks like:

section: screens
  Juggernaut.local:
  Monolith:
    super = alt
    alt = super
end

section: links
  Juggernaut.local:
    right = Monolith
  Monolith:
    left = Juggernaut.local
end

section: options
  screenSaverSync = true
  heartbeat = 1200
  relativeMouseMoves = true
  keystroke(NumLock) = lockCursorToScreen(toggle)
end

If you look at the last two lines in the options section, these are what you want to add to your config. I should note that while it’s documented that, by default, Synergy *should* use Num Lock as the lockCursorToScreen toggle without any config options, this was not the case for me under OS X. I could not bind anything to the num lock, in fact, on my Mac server. Instead, I’m using the NumLock key for this.

After adding relativeMouseMoves and the lockCursorToScreen toggle, boot up your synergy systems and load Warcraft on your client PC. Try to rotate the camera and you should still get the “crazy camera” bug. Now tap Num Lock to enable screen locking. Walla! No more crazy camera.

The only downside to this fix is that in order to move input back to your server machine (or any other client machines) you must press Num Lock to unlock the screen again. Annoying, but at least Warcraft is finally playable on your synergy clients now.

Shortlink

Revert 10.7 Lion’s “clamshell” behavior to that of Snow Leopard

Prior to 10.7 Lion I was able to reliably use my MacBook Pro with an external monitor as it’s only enabled display by simply closing the lid, attaching the display and pressing an input key on an external keyboard. When heat was a concern, I could reopen the lid after this and the internal display would remain off.

Lion changed this behavior so that now, whenever the lid on the MBP is closed, the machine will always go to sleep. Likewise if the lid is up, the internal display will always be on. There does not appear to be any power setting to change this behavior, much to my displeasure.

Here’s a quick Terminal hack that will force Lion to behave like it’s predecessors:

sudo nvram boot-args="iog=0x0"

Reboot and you’re done. Why Apple changed this without a option to toggle it back is beyond me. If you ever want to revert the change, just reset your NVRAM.

Shortlink

Resetting your Chrome user profile

As this topic comes up pretty frequently in discussions, I’m posting this information here for quick linkage/reference in the future. If you’re experiencing bizarre behavior in Chrome, especially slow downs, crashes or unresponsive tabs, resetting your user profile for Chrome often helps resolve the issues.

Chrome supports multiple user profiles to allow multiple OS users or, by using some about:flags magic, hot swapping between profiles in the same session. Keep in mind that deleting these profiles will wipe out any bookmarks, cookies, etc. attached to that profile. If you’re using Chrome Sync, there’s no need to back up anything as it will all be restored for you the next time you start Chrome and login to the Sync system.

To reset your user profile, shut down Chrome and then open the following directory path:

On Windows XP: C:\Documents and Settings\Your User Name\Local Settings\Application Data\Google\Chrome\User Data\
On Windows Vista, 7 or 8: C:\Users\Your User Name\AppData\Local\Google\Chrome\User Data\
On Mac OS X: ~/Library/Application Support/Google/Chrome/
On Linux: ~/.config/google-chrome/

Delete the profile you believe is damaged. Most users will simply have a “Default” user profile, which is what you’ll want to delete. Restart Chrome and optionally login to Sync to restore your bookmarks and other goodies.

Shortlink

Selectively filtering Google+ streams based on user interests

Google+ is a pretty great service. In a lot of ways it’s more of a spiritual successor to FriendFeed than it is a Facebook or Twitter “killer,” in that it makes sharing larger bodies of content and holding discussions around that content much easier, meaningful and personal. This is thanks in no small part to the social organization that Circles introduces, a concept Paul Adams (formerly of Google and now Product Manager at Facebook, somewhat ironically) introduced in his “The Real Life Social Network v2″ presentation.

Circles make it easy to logically organize your family, friends, colleagues, etc. into groups and target the content you share only to those specific people. For example, a lot of my friends are gamers, but my family doesn’t want to hear about the latest gaming trends. When I share a story like this, I can choose to just share it with my gaming buddies and not my parents. It’s perfect for these sorts of simple things.

To be fair, the concept of Circles is not unique. Facebook’s grouping functionality has been around for a long time and can be used for exactly the same purposes. The major difference with Google’s approach is that they’ve baked this concept into the very core of their product. It isn’t an afterthought, nor is it hidden behind a Preferences link two levels deep. Circles are the platform from which you do all interactions in Google+.

However, we don’t always communicate in a generic manner that can easily be targeted to social groups. Let’s take my Twitter account as an example. I have a bit over 600 people currently following me there. However, for the vast majority of them, I know nothing about what made them follow me. I have a lot of varied interests. What was it that made John Q follow me? I can’t read his mind. It never fails that when I tweet about a niche topic that really intrigues me, I’ll lose 1 or 2 followers. I’m not talking about something super polarizing like politics, just niche things like fixing a rare bug in Xcode, or patch notes for a game.

Why not let users decide what they want to hear about? Give them some control to filter their stream. Using the John Q example, let’s say he follows me on Google+ because of my gaming news, but has a real dislike of MMOs. I know he likes gaming, so he gets lumped into my Gaming circle. Simple, right? But I share a lot of MMO news. I don’t want to alienate him from the stuff that does interest him with the stuff that doesn’t.

How do we solve this?

If we look at Google+ like a blog rather than a social network (which, let’s face it, Google+’s format lends itself to the concept of a blog much more than it does a microblogging system like Twitter), we can immediately think of some filtering tools we could use. If you want to follow my blog, for example, you can choose to use a general RSS feed with everything I post, or just my programming thoughts, or just my gaming stuff, and so on. You can pick and choose. How could we implement this into a social network?

Chris Messina pioneered hash tagging: effectively a way of categorizing tweets and making them easily searchable, filterable, associable. It took off, and now Twitter supports the method natively. Many Twitter tools let you filter your stream based on these hash tags, displaying or hiding them as you see fit. If you’ve ever tried to use twitter while SXSW is going on, you no doubt already know how to take advantage of these tools.

Could Google implement support for something like this into Google+ in a manner that is simple and compatible with Circles? I think so, and I think it would fit in well without overly complicating the user experience.

On implementation idea: Google could add a second field on the share interface, beneath the Circle selection, that allowed you to define tags for the share. If I’m linking a post about System Administration Appreciation Day, for example, I could target my Tech Enthusiast circle, and tag it with sysadmin, it, office, and so on. Not everyone in my Enthusiast circle works in an office, and I’m sure some of those who do don’t share my appreciation for sysadmins, so when they see my share they could open the “Tags” menu in the footer of the post, and selectively pick terms to filter out of their stream in the future. If they chose to stop seeing shares tagged with sysadmin (“globally mute”?), my post would instantly fade out from their stream, and all future shares on the topic would no longer appear for them.

No doubt some will argue that this could be accomplished entirely using Circles, and they’re quite right, if I personally knew everyone who follows me and I knew precisely how to categorize their interests. But this makes the assumption that I do, and it’s unreasonable to assume I ever will. Likewise, as a user, I don’t want people to assume what my interest are or are not. I might harbor a secret love for YouTube cat videos (ahem), but you might not know that and forget to put me into your “lolcats r awusum” Circle. No, Circles are designed to group people based on their social connections or interests on a very generic level, and it’s unreasonable to think that anyone will want to micromanage their network to the point of filtering people into extremely niche groupings such as my previous John Q example.

Others might argue: who cares? Aren’t I thinking a bit too much into this? To many this will be a non-issue, I agree. However, to me, Google+ is a great experiment in creating a social network from a blank slate with an emphasis on clarity, choice, and organized thought, and I am already encountering instances of people I really enjoy following, but who are too noisy about topics I don’t want to hear about.

So, there’s some random, messy thoughts of mine on the issue. Talk amongst yourselves.

Shortlink

Introducing WoW Instant

After several months of work, I’m very pleased to finally open the doors on WoW Instant. Instant is a rapid search engine for not only quickly looking up spells and items, but finding upgrades, comparing gear based on class and spec-specific usability weights, and building and sharing collections of items with guildmates.

I began working on Instant in late 2010 as an excuse to challenge myself and learn more about the Warcraft databases, and towards learning a variety of web technologies that ultimately became the foundation for what you see today (namely MongoDB, HTML5 and CSS3.)

I hope you enjoy what I’ve launched with today, but rest assured I’m not finished yet. This is just the tip of the iceberg, but I do hope you like it and please let me know if there’s any features in particular you’d like to see me implement going forward.

Shortlink

Painless nightly server backups with Dropbox or SpiderOak

Update! I’ve posted an updated version of this script right here. Enjoy.

If you run a server, you need to be doing backups. There’s no ifs, ands or buts about it. I don’t care how small or big a site you run. It’s important. Whether it’s a hardware failure, a software bug that obliterates your database, a hacker or just negligence on your hosting provider’s side (all of which has happened to me) I assure you something will eventually go wrong. And you will wish to God you’d been doing them.

Over the years I’ve tried a lot of backup and sync solutions, but the three I’ve settled on using in my home, office and server environments are rsync, Dropbox and SpiderOak. Rsync I use mostly for my internal network sync or pushing code to my servers, so I’ll leave that one for a later post. In this post I’ll focus on the server end.

A while back I wrote a custom bash script for managing my backups, which I have setup to run as a cron task each night. It grabs all the user home directories, optimizes and exports the MySQL databases, and copies my MongoDB stores. It neatly compresses all of them and places them in a backup directory. It then fires up Dropbox or SpiderOak to push the backups to the cloud, and ultimately sync it all back to my local machines here in the office. It works flawlessly and gives me a lot of peace of mind.

Because Dropbox and SpiderOak both provide Linux CI binaries, this is all in all a pretty simple solution to setup and maintain. I will note that I switched to SpiderOak for my setup over Dropbox as I found the SpiderOak network to be more reliable, and their Linux binaries to be much more dependable. I also like that I can run SpiderOak on demand with the –batchmode flag rather than have it constantly taking up server memory when I’m not using it. SpiderOak’s attention to security and it’s encryption policies are also greatly appreciated, especially in light if tge recent discoveries about Dropbox’s own policies.

Step 1: Install Dropbox or SpiderOak

Both products offer Linux installation guides on their Wikis. These setup instructions have changed several times even in recent months, so I won’t go through the effort of echoing them here and likely leading you astray when they change down the road.

Dropbox instructions here, and SpiderOak here and here.

Step 2: Backup Script

Here is a modified version of my script. You can use it as a template to customize and build upon for your own needs.

#!/bin/bash

mysqlUser="MYSQL ROOT USER"
mysqlPass="MYSQL ROOT PASSWORD"

backup="/path/to/backups/"

folders[0]="/home/user1/"
folders[1]="/home/user2/"
folders[2]="/some/other/path/"

temp="$PWD/backup.tmp"

echo "Starting Backup..."

stamp=$(date -u  +%F)
backup=$backup$stamp

if [ ! -d "$backup" ]; then
  mkdir "$backup"
fi

echo "-----"
echo -ne "Preparing to backup MySQL tables... "
databases=( $(mysql -u"$mysqlUser" -p"$mysqlPass" --skip-column-names --batch -e "show databases;" 2>"$temp") );
echo "found ${#databases[@]} databases.";

for i in ${databases[@]}; do
  if [ $i != "information_schema" ] && [ $i != "mysql" ] && [ $i != "phpmyadmin" ]; then
    echo -ne "Optimizing and backing up database $i ... "
    mysql -u"$mysqlUser" -p"$mysqlPass" -D "$i" --skip-column-names --batch -e "optimize table $i" 2>"$temp" >/dev/null
    mysqldump -u"$mysqlUser" -p"$mysqlPass" --opt $i | bzip2 -c > "$backup/db_$i.sql.bz2"
    echo "Done."
  fi
done

echo "-----"

for f in "${folders[@]}"
do
  if [ -d "$f" ]; then
    echo -ne "Backing up directory $f ... "
    d=$(basename $f)
    d="$d.tar.bz2"
  elif [ -f "$f" ]; then
    echo -ne "Backing up file $f ... "
    d=${f##*/}
    d="$d.tar.bz2"
  else
    echo "Error! $f could not be found."
    exit 1
  fi

  tar -cjf "$backup/$d" -C / ${f:1}
  echo "Done."
done

if [ ! -s $temp ]; then
  rm -f "$temp"
fi

echo "-----"
echo "Pushing data to SpiderOak..."

SpiderOak --batchmode

echo "-----"
echo "Backup Complete!"
exit 0

Note that if you choose to use Dropbox or installed SpiderOak as a daemon, you’ll want to remove lines 62 through 65. I just prefer to use SpiderOak on-demand.

Step 3: Schedule It

Setup a Cron task to run the script. Be sure to think about user permissions for the script and run the task under the appropriate user/group.

* 3 * * * /path/to/backup.sh

This will run the backup every morning at 3am.

Step 4: Profit?

Aside from occasionally purging old backups you no longer need from your storage directory and SpiderOak, you should be golden. The script will run, the backups will be created and synced to your service of choice, and you’ll have one less thing to worry about.

  • If you’d like to give SpiderOak a try, you can sign up using this link for 3GB of free backup storage.
  • If you’d rather use Dropbox, you can sign up using this link for 2.25GB of free backup storage.

Hopes this helps someone out there. If you have any suggestions on improvements, please share them in the comments.

Shortlink

SASS Mixin: Universal RGBA support

Requires SASS 3 and CSS3 PIE for legacy Internet Explorer support. Yes, even IE6.

@mixin rgba-background($color, $opacity) {
    position: relative;

    background: $color;
    background: rgba($color, $opacity);
    -pie-background: rgba($color, $opacity);

    @include PIE;
}

@mixin PIE {
    behavior: url("#{$basepath}/css/pie.htc");
}

Example usage:

#example { @include rgba-background(#0070FF, 0.5); }

#example2 { @include rgba-background(mix(#000, #0070FF), 0.5); }

Customize as necessary. Enjoy.

Shortlink