Everything is Gray

A place to put stuff.

Python One-Liner for Determining Java Vendor

| Comments

Here’s a Python one-liner that prints the Java vendor on OS X clients, (e.g., Apple or Oracle).

1
python -c 'import os,plistlib; jv = plistlib.readPlist(os.path.join(os.path.realpath("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin"), "Contents/Info.plist"))["CFBundleIdentifier"].split(".")[1]; print jv.capitalize()'

Why?

…Because we can.

Querying AD Group Membership Status With Ldapsearch

| Comments

Here’s a way to query Active Directory for group membership status with the ldapsearch utility. In my case, this was used to check if a user is authorized before attempting to mount an SMB share at login on OS X clients. The -Q option causes ldapsearch to use SASL quiet mode and not prompt for a password, which works if a Kerberos ticket is present.

    #!/bin/bash

    console_user="$(/usr/bin/stat -f%Su /dev/console)"
    ldap_uri="ldap://example.com"
    search_base="DC=example,DC=com"
    group="Some Group"
    is_member_of_group=`/usr/bin/ldapsearch -LLL -Q -H "$ldap_uri" -b "$search_base" "sAMAccountName=$console_user" sAMAccountName | $grep "OU=$group"` # -Q prevents the authentication prompt, works if kerberos ticket exists

    if [[ "${#is_member_of_group}" -ne "0" ]]; then
        echo "$console_user is a member of $group."
        # Do something about it
    else
        echo "$console_user is NOT a member of $group."
        # Do something else
    fi

    exit $?

Ghost in the Cloud: Deploying the JSS on Amazon EC2

| Comments

The fine folks at JAMF Software have posted a video of the talk I gave at this year’s JNUC. I had a great time in Minneapolis and hope to attend again next year.

The slides for this talk are available on my GitHub if you want to follow along.

It’s not uncommon in the “lean startup” world for a company to have a strong aversion to onsite infrastructure. This presents IT administrators with the challenge of hosting and scaling their infrastructure elsewhere. Although there are many similarities between hosting the JSS locally and in “the cloud”, there are some key differences to be aware of to ensure a successful deployment.

Option (Left|right) Arrow Navigation in iTerm2

| Comments

Today I decided to switch to iTerm2 on OS X. Shortly after making this decision I was frustrated by not being able to “jump” between words with the option-arrow shortcut. Fortunately, the solution is rather simple — just modify the option-left/right arrow shortcuts in Preferences > Profiles > Keys. The “Action” should be “Send Escape Sequence” + b for jumping backwards and f for jumping forward, (e.g., ^[b, and ^[f).

I know I’ve tried iTerm in the past, and I’m pretty sure not being able to do this right away kept me from continuing to use the tool. After making this change everything has been swell, though.

iTerm2 Preferences

Determining Guest Login Status

| Comments

The other day I was presented with the challenge of determining whether Guest login was enabled on 150+ Macs. Fortunately, my client was using the Casper Suite, so I was able to whip up an Extension Attribute rather quickly. Here’s what I came up with to get the status:

cURL Speed Test

| Comments

Have you ever needed to run a quick download speed test, but were too lazy to open up a new browser window and use one of the Web-based tools? Or perhaps you just prefer to use the command-line like me. Never fear, because appending this line to your ~/.bash_profile will allow you test your download speed from within your login shell by simply typing speedtest.

On Migrating From WordPress to Octopress

| Comments

Having recently made the decision to migrate my blog from WordPress to Octopress I decided I would share my findings in hopes that someone else will benefit from them, and so I can refer back to this post should I need to do something similar in the future.

The primary reason I decided to move to a statically generated site is because I found myself spending more time worrying about whether my site was running and was secure than I did writing. My site was hacked last year because I was running an outdated version of WordPress, and since moving to a new host, (Amazon EC2), I have frequently had to restart MySQL and Apache just to keep the site up. This could be partially due to the fact that I was running the site on a micro instance, but now that my blog is static I don’t have to worry about that any longer. Another reason why I chose Octopress for my blogging platform is that I like to tinker, and being able to use tools that I’m already familiar with and use every day was a huge plus, (Vim, Git, and Markdown, specifically).

So, without further ado1, here are the steps I took to mirgrate from WordPress running on an Amazon EC2 instance to Octopress running on Heroku.

Prepping Your Environment

I performed the migration using a fairly stock OS X 10.8 machine. If you’re using a Mac, you should probably be using Homebrew to manage packages.

1
2
3
brew update
brew doctor
brew install rbenv

The next thing you should do is add the following to your ~/.bash_profile.

1
2
3
4
5
# To use Homebrew's directories rather than ~/.rbenv add to your profile:
export RBENV_ROOT=/usr/local/var/rbenv

# To enable shims and autocompletion add to your profile:
if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi

Then source ~/.bash_profile.

Now install Ruby 1.9.3 with rbenv.

1
2
3
4
5
6
7
8
brew install ruby-build
brew update
brew tap homebrew/dupes
brew install automake apple-gcc42
rbenv install 1.9.3-p194
rbenv rehash
rbenv global 1.9.3-p194
ruby --version # Should now return 1.9.3

Setting Up Your Local ~/Sites Directory and Installing Octopress

On OS X 10.8 the ~/Sites folder no longer exists in the home directory by default. No problem though, because we can create it, and we’ll even get the correct permissions and pretty icon when we do so.

I also recommend checking out Anvil for Mac. This will allow you to easily manage your local site and give you a .dev domain for testing. Best of all, it’s free.

1
2
3
4
5
6
7
8
mkdir ~/Sites
cd ~/Sites
git clone git://github.com/imathis/octopress.git <sitename>
cd <sitename>
gem install bundler
rbenv rehash
bundle install
rake install

If you took my advice and installed Anvil for Mac, do the following to create a symlink to your site in the ~/.pow directory.

1
2
3
4
cd ~/.pow
ln -s ~/Sites/<sitename> <sitename>
cd -
rake generate && rake watch

Use rack-rewrite for Redirects

This is useful for redirecting old URLs to your new Octopress site, especially if you used date-based permalinks for archives on WordPress, (something like example.com/2012/02/22), and you want those to redirect to /archives on your new site.

Add this to the end of your Gemfile.

Gemfile
1
gem 'rack-rewrite'

Add this to config.ru.

config.ru
1
require 'rack-rewrite'

Under $root = ::File.dirname(__FILE__) in config.ru add your redirects, (these are just some examples). Of particular interest are the regular expressions that redirect dates in yyyy-mm-dd format to /archives.

config.ru
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Rack::Rewrite do
    r301 /.*/,  Proc.new {|path, rack_env| "http://#{rack_env['SERVER_NAME'].     gsub(/www\./i, '') }#{path}" },
      :if => Proc.new {|rack_env| rack_env['SERVER_NAME'] =~ /www\./i}
    r301 %r{^/tag/art/?$}, '/category/art/'
    r301 %r{^/tag/design/?$}, '/category/design/'
    r301 %r{^/tag/os-x/?$}, '/category/os-x/'
    r301 %r{^/tag/science/?$}, '/category/science/'
    r301 %r{^/tag/technology/?$}, '/category/technology/'
    r301 %r{^/tag/uncategorized/?$}, '/category/uncategorized/'
    r301 %r{^/tag/web-design/?$}, '/category/web-design/'
    r301 %r{^/tag/web-development/?$}, '/category/web-development/'
    r301 %r{^/tag/wordpress/?$}, '/category/wordpress/'
    r301 %r{^/[0-9]{4}/(1[0-2]|0[1-9])/(3[01]|[12][0-9]|0[1-9])/?$}, '/archives/'
    r301 %r{^/[0-9]{4}/(1[0-2]|0[1-9])/?$}, '/archives/'
    r301 %r{^/[0-9]{4}/?$}, '/archives/'
end

Install rack-rewrite.

1
2
3
4
bundle install
bundle
bundle show rack-rewrite
rake generate && rake watch

Now restart your local server, (turn the slider on and off with Anvil).

Create _heroku Directory for Deployment

I opted to keep the generated Heroku site in a separate repository so I can keep public in my .gitignore. This will help keep your commit history clean, as not (nearly) every file that Git is tracking will change every time you re-generate your site.

1
2
3
4
5
6
cd ~/Sites/<sitename>
mkdir _heroku
cp config.ru _heroku/
cp Gemfile _heroku/
cd _heroku
mkdir public

Create an HTML file in _heroku/public so you can deploy to Heroku.

1
2
touch ~/Sites/<sitename>/_heroku/public/index.html
echo '<html><p>Hello world.</p></html>' > ~/Sites/<sitename>/_heroku/public/index.html

Make the Gemfile in _heroku only include these lines.

1
2
3
4
source "http://rubygems.org"

gem 'sinatra', '~> 1.4.2'
gem 'rack-rewrite'

Add this task to your Rakefile. This will result in your site being deployed to Heroku when you type rake deploy. Don’t run rake deploy at this time, though, because you haven’t set up Heroku yet.

Rakefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
desc "Deploy a basic rack app to heroku"
multitask :heroku do
puts "## Deploying to Heroku"
    (Dir["#{deploy_dir}/public/*"]).each { |f| rm_rf(f) }
    system "cp config.ru #{deploy_dir}/"
    system "cp -R #{public_dir}/* #{deploy_dir}/public"
    puts "\n## Copying #{public_dir} to #{deploy_dir}/public"
    cd "#{deploy_dir}" do
        system "git add ."
        system "git add -u"
        puts "\n## Committing: Site updated at #{Time.now.utc}"
        message = "Site updated at #{Time.now.utc}"
        system "git commit -m '#{message}'"
        puts "\n## Pushing generated #{deploy_dir} website"
        system "git push heroku #{deploy_branch}"
        puts "\n## Heroku deploy complete"
    end
end

…then update these variables, (in your Rakefile).

1
2
3
deploy_default = "heroku"
deploy_branch = "master"
deploy_dir = "_heroku"

Signing up for and Configuring Heroku

Now it’s time to sign up for a free Heroku account. The Octopress documentation recommends installing the heroku gem, but that’s deprecated so download and install the Heroku Toolbelt instead.

1
2
3
4
ssh-keygen # Generate a public/private key pair
heroku create # Enter your Heroku credentials
<number_for_public_key> <ENTER>` # Use the public key that was created when you ran `ssh-keygen`
heroku rename <sitename> # Also make sure that `url:` in _config.yml matches http://<sitename>.herokuapp.com

Now you can do your first deploy to Heroku!

1
2
3
4
5
6
7
cd ~/Sites/<sitename>/_heroku
git init .
git add .
git config branch.master.remote heroku
git commit -am "Initial commit."
rake generate && rake watch
rake deploy

Exporting your WordPress Posts and Comments

Log on to your WordPress Admin and export your content in XML format. You can find this under Tools > Export. Keep this data handy because we’ll be using it in the next step.

The next steps I took were to sign up for a free Disqus account, install the (Disqus) WordPress plugin, and export my comments to Disqus from the plugin options. This will ensure that your comments are preserved after migrating to Octopress, (as long as your permalinks don’t change). Also be sure to add your Disqus shortname to _config.yml.

Importing your WordPress data into your Octopress Site

There are a number of tools available for importing your WordPress posts into Octopress as Markdown files, but the one I ended up using was exitwp. You can follow these steps to import your WordPress site with exitwp.

1
2
3
4
5
6
7
8
9
10
11
12
13
brew install python
printf '\nexport PATH=/usr/local/share/python:$PATH' >> ~/.bash_profile
source ~/.bash_profile
pip install virtualenv
pip install virtualenvwrapper
source /usr/local/share/python/virtualenvwrapper.sh
mkvirtualenv --no-site-packages octopress # Don't symlink site packages so your environment is more isolated
brew install libyaml
pip install pyyaml
pip install BeautifulSoup4
pip install html2text
cd ~/Documents
git clone https://github.com/thomasf/exitwp

Now, copy your WordPress XML files to ~/Documents/exitwp/wordpress-xml, then run xmllint on your export file and address any errors you encounter. You should also customize what you want exported in ~/Documents/exitwp/config.yaml, (e.g., whether or not you want images to be downloaded and included in your build directory). To run the tool type python exitwp.py in the terminal from within the ~/Documents/exitwp directory.

You should now see your converted site in ~/Documents/exitwp/build, which you can copy to your source directory.

Remove /blog/ from Octopress URL

Follow these steps if you don’t want /blog/ to appear in your Octopress URL.

Update the permalink setting in _config.yml.

_config.yml
1
permalink: /:year/:month/:day/:title/

Move the contents of the blog directory.

1
2
3
mv ~/Sites/<sitename>/source/blog/archives ~/Sites/<sitename>/source/archives
mv ~/Sites/<sitename>/source/blog/articles/index.html ~/Sites/<sitename>/source/articles/index.html
rm -rf ~/Sites/<sitename/source/blog

Update the navigation in source/_includes/custom/navigation.hmtl.

1
2
3
4
5
6
7
8
# Change this
<li><a href="/blog/archives">Archives</a></li>
# to this
<li><a href="/archives">Archives</a></li>
# ...and this:
<li><a href="/">Blog</a></li>
# to this:
<li><a href="/">Home</a></li>

Update the Archives link in source/index.html.

1
2
3
4
# Change this:
<a href="/blog/archives">Blog Archives</a>
# to this:
<a href="/archives">Archives</a>

Update the category base URL in _config.yml.

1
2
3
4
5
6
7
8
# Change this:
category_dir: blog/categories
# to this:
category_dir: category
# ...and this:
pagination_dir: blog
# to this:
pagination_dir:

Update the Archives page title in source/archives/index.html.

1
2
3
4
# Change this:
title: Blog Archive
# to this:
title: Archives

Change Author Name After Importing from WordPress

Another thing that bothered me for a while about my WordPress blog was that the author metadata for posts and pages reflected the ‘admin’ shortname. This is because I left the default username when initally setting up WordPress years ago. I’m sure there’s an easy way to change this, but now that my blog consists of plain text files I have the freedom to use whatever tools I choose to make changes. Here’s a perfect example of that using find and sed.

1
cd ~/Sites/<sitename>/source && find . -name "*.markdown" -print0 | xargs -0 sed -i '' -e 's/author:\ admin/author:\ Forename\ Surname/g'

Using Google Fonts

If you love Google Fonts add this to the top of source/_includes/custom/head.html, (just be sure to use your own favorite fonts!).

1
<link href='http://fonts.googleapis.com/css?family=Archivo+Narrow:400,700' rel='stylesheet'  type='text/css'>

Prevent Comment Text from Wrapping on Small Screens

One of the first things I noticed with the default Octopress theme is that the Disqus comments text that appears above posts was wrapping on smaller screens, which caused it to overlap with the post title. To fix this, I added the following to sass/custom/_styles.scss.

_styles.scss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
article {
    header {
        p {
            &.meta {
                font-size: .7em;
            }
        }
    }
}

@media only screen and (min-width: 480px) {
    article {
        header {
            p {
                &.meta {
                    font-size: .9em;
                }
            }
        }
    }
}

404

You might also want a custom 404 page. Fortunately, that’s pretty easy too.

1
2
3
4
5
6
cd ~/Sites/<sitename> && rake new_page[404]
cd source/404
mv index.markdown 404.markdown
mv 404.markdown ../
cd ../
rm -rf 404

Then update the yaml front matter in 404.markdown.

Fancybox

I followed a post on Ewal.net to get Fancybox working with Octopress. The only difference is that I didn’t add a reference to jQuery in the head because it’s already present in the default installation of Octopress.

To add Fancybox support to an image simply add this to your post.

1
{% img fancybox /dir/foo.jpg Title %}
source/javascripts/anchor.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$(function() {
    $('.entry-content').each(function(i){
        var _i = i;
            $(this).find('img.fancybox').each(function(){
            var img = $(this);
            var title = img.attr("title");
            var classes = img.attr("class");
            img.removeAttr("class");
            img.wrap('<a href="'+this.src+'" class="' + classes + '" rel="gallery'+_i+'" /   >');
            if (title != "")
            {
                img.parent().attr("title", title);
            }
        });
    });
    $('.fancybox').fancybox();
});

Open Vim when running rake new_post and make new posts un-published by default

Add the following to your Rakefile if you want rake new_post to open in your editor and new posts to be un-published by default. Replace Vim with your favorite editor if you dare. :)

Rakefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
desc "Begin a new post in #{source_dir}/#{posts_dir}"
task :new_post, :title do |t, args|
    if args.title
        title = args.title
    else
        title = get_stdin("Enter a title for your post: ")
    end
    raise "### You haven't set anything up yet. First run `rake install` to set up an    Octopress theme. "
    unless File.directory?(source_dir)
    mkdir_p "#{source_dir}/#{posts_dir}"
    filename = "#{source_dir}/#{posts_dir}/#{Time.now.strftime('%Y-%m-%d')}-#{title.     to_url}. #{new_post_ext}"
    if File.exist?(filename)
        abort("rake aborted!") if ask("#{filename} already exists. Do you want to        overwrite?", ['y', 'n']) == 'n'
    end
    puts "Creating new post: #{filename}"
    open(filename, 'w') do |post|
        post.puts "---"
        post.puts "layout: post"
        post.puts "title: \"#{title.gsub(/&/,'&amp;')}\""
        post.puts "date: #{Time.now.strftime('%Y-%m-%d %H:%M')}"
        post.puts "published: false"
        post.puts "comments: true"
        post.puts "categories: "
        post.puts "---"
    end
    system "vim \"#{filename}\""
end

Finalize DNS

The last step for me was to transfer my DNS records from Amazon Route 53 to Hover, (my registrar). Heroku has a farily detailed article on using custom domains, so I recommend reading that.

Here are the relevant Heroku commands. Don’t forget to update the url: in _config.yml one last time, (e.g., remove herokuapp from URL).

1
2
cd ~/Sites/<sitename>/_heroku && heroku domains:add <sitename>.com
heroku domains:add www.<sitename>.com

  1. Yes, I spelled that correctly. Google it.

Automatically Post Images to Tumblr With Automator

| Comments

For the past few months I have been drawing every day on 2.5 x 3.5 inch pieces of Bristol board as part of my most recent ‘kooBtrA’ project. kooBtrA = ArtBook in reverse, as I plan to self publish a full-color book containing all the images sometime after one year passes.

As you might imagine, finding the time to draw every day can be challenging. Not to mention the fact that I’ve also been posting these images to my Tumbleog; which includes scanning, resizing, and logging into Tumblr to post the image along with a description and tag. This became mundane after the first few days of posting, so I decided to set time aside to automate this process, as doing so would surely save me time in the long run.

I did a bit of research and discovered that Tumblr offers the ability to send posts via email. This immediately gave me the idea of using Automator) to streamline this process, as Automator offers a very straightforward ‘New Mail Message’ Action. This would allow me to create an Image Capture Plugin that grabs the image that was scanned and include it in the email message, right? Well, yes, but with one caveat. With Snow Leopard’s introduction came a few changes, one of which removes the ability to scan to a specified folder while using an Image Capture Plugin, (the image is saved to the ~/Pictures folder, then the Workflow runs). The workaround in my case was to use a Folder Action, which would result in the Workflow running any time a new file is added to the specified folder.

What I had been doing until this point was scanning the image to a subfolder of my ~/Documents folder; scaling the image to 300px max on either side depending on orientation; then uploading the image to Tumblr.com along with a description and tag. The Folder Action Workflow I ended up producing cuts the time this took in half.

The first thing I did was create two variables. One for the name of the post and one for the number of the post. You can achieve this by right or control clicking in the variables window at the bottom of the application window and choosing ‘New variable.’ I left the value blank, as this will vary with each post. The next step was to start dragging actions over to the right of the application window. The first is an ‘Ask for Text’ Action, which asks for the name of the post, then sets the value of the variable. The same goes for the number of post. The Workflow then asks for the Finder items to act on, then copies them to a new location and scales them to 300px. Finally, the ‘New Mail Message’ Action is added, which utilizes the name and number variables that were set previously, as well as receives the resized image from the previous Action. The last Action that was added was ‘Send Outgoing Messages,’ which is fairly self explanatory.

Below is the printed Workflow for your reference should you wish to accomplish something similar. If you have any suggestions as to improving this Automator Workflow please leave a comment. For more information regarding Tumblr’s post via email option please visit http://www.tumblr.com/docs/en/email_publishing.