<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title><![CDATA[Nynim.org]]></title>
  <link href="https://nynim.org/atom.xml" rel="self"/>
  <link href="https://nynim.org/"/>
  <updated>2024-11-04T20:05:20-06:00</updated>
  <id>https://nynim.org/</id>
  <author>
    <name><![CDATA[Tony Duckles]]></name>
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Migrating From Octopress 2 to Octopress 3]]></title>
    <link href="/blog/2017/08/26/migrating-from-octopress-2-to-octopress-3/"/>
    <updated>2017-08-26T18:06:26-05:00</updated>
    <id>https://nynim.org/blog/2017/08/26/migrating-from-octopress-2-to-octopress-3</id>
    <category term="Blog" />
      <content type="html"><![CDATA[<p>I recently upgraded this blog to <a href="https://github.com/octopress/octopress">Octopress
3</a>, as part of rebuilding (and
<a href="https://www.docker.com/">Docker</a>-ifying) my blog
<a href="https://jekyllrb.com/">Jekyll</a> build environment. This post is a guide of my
upgrade experience and to talk about various workarounds I needed to make to
get everything working.</p>

<!-- more -->

<h2 id="introduction-to-octopress-3">Introduction to Octopress 3</h2>

<p>With <a href="https://github.com/imathis/octopress">Octopress 2</a>, the <a href="https://nynim.org/blog/2011/12/19/hello-octopress/">paradigm I
used</a> was to fork the canonical
Octopress Git repository and maintain all my posts and theme files as a branch
forked off of the Octopress "master" branch. I then periodically ran <code class="language-plaintext highlighter-rouge">git merge
octopress/master</code> to pull upstream core Octopress infrastructure changes into
my custom branch.</p>

<p>As mentioned in the official "<a href="http://octopress.org/2015/01/15/octopress-3.0-is-coming/">Octopress 3.0 Is
Coming</a>"
announcement, there were various downsides to the Octopress 2 paradigm.  The
main pain-point revolves around how Octopress 2 is basically just the skeleton
of a Jekyll blog that you need to fork and modify – which means that when
you want to take upstream changes of the Octopress 2 infrastructure, you need
to merge those upstream changes into your local forked branch and work through
any merge-conflicts. Sometimes easy, sometimes time-consuming.</p>

<p>With Octopress 3, all of this has been re-thought so that your site is a Jekyll
site first-and-foremost and all the extra Octopress "goodies" are delivered via
gems which can be used as <a href="http://jekyllrb.com/docs/plugins/">Jekyll plugins</a>.
That creates a nice clean separation between your site content versus the Jekyll
site-building tools. Neat!</p>

<h3 id="octopress-3-is-dead-long-live-octopress-3-">Octopress 3 is Dead, Long Live Octopress 3 …?</h3>

<p>Side note: it's unclear what the future of Octopress 3 is.</p>

<p>Octopress 3 development was active and vibrant circa 2015, but all the activity
in the <a href="https://github.com/octopress">Octopress plugin repositories</a> seemed to
tail-off towards the beginning of 2016. It's a shame, but I understand how
these things sometimes go – priorities change, and life comes first.</p>

<p>I opted to embrace Octopress 3 as-is because I was a big Octopress 2 fan and I
really like the Octopress 3 vision.  Also, I wanted the small quality-of-life
features which Octopress added above-and-beyond the default Jekyll scaffolding.</p>

<p>But I did run into some small bumps along the way – more on that later.</p>

<h2 id="setup-docker">Setup Docker</h2>

<p>I created a <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file to compartmentalize Jekyll build
environment:</p>

<figure class="code-highlight-figure"><figcaption class="code-highlight-caption"><span class="code-highlight-caption-title">docker-compose.yml</span></figcaption><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3'</span>
</div></div><div data-line="2" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="na">services</span><span class="pi">:</span>
</div></div><div data-line="3" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="na">jekyll</span><span class="pi">:</span>
</div></div><div data-line="4" class="code-highlight-row numbered"><div class="code-highlight-line">    <span class="na">image</span><span class="pi">:</span> <span class="s">jekyll/jekyll:latest</span>
</div></div><div data-line="5" class="code-highlight-row numbered"><div class="code-highlight-line">    <span class="na">environment</span><span class="pi">:</span>
</div></div><div data-line="6" class="code-highlight-row numbered"><div class="code-highlight-line">      <span class="na">BUNDLE_PATH</span><span class="pi">:</span> <span class="s">/srv/bundle</span>
</div></div><div data-line="7" class="code-highlight-row numbered"><div class="code-highlight-line">    <span class="na">ports</span><span class="pi">:</span>
</div></div><div data-line="8" class="code-highlight-row numbered"><div class="code-highlight-line">      <span class="pi">-</span> <span class="s">4000:4000</span>
</div></div><div data-line="9" class="code-highlight-row numbered"><div class="code-highlight-line">    <span class="na">volumes</span><span class="pi">:</span>
</div></div><div data-line="10" class="code-highlight-row numbered"><div class="code-highlight-line">      <span class="pi">-</span> <span class="s">.:/srv/jekyll</span>
</div></div><div data-line="11" class="code-highlight-row numbered"><div class="code-highlight-line">      <span class="pi">-</span> <span class="s">data-bundle-cache:/srv/bundle</span>
</div></div><div data-line="12" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="na">volumes</span><span class="pi">:</span>
</div></div><div data-line="13" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="na">data-bundle-cache</span><span class="pi">:</span>
</div></div></pre></div></figure>

<p>Some key points:</p>

<ul>
  <li>Use the standard <code class="language-plaintext highlighter-rouge">jekyll/jekyll:latest</code> <a href="https://hub.docker.com/r/jekyll/jekyll/">Docker
image</a>.</li>
  <li>Use a persistent Docker volume as the <code class="language-plaintext highlighter-rouge">$BUNDLE_PATH</code> to avoid needing to
re-download and re-install all the <code class="language-plaintext highlighter-rouge">Gemfile</code> gems for each <code class="language-plaintext highlighter-rouge">docker-compose
run</code> container.</li>
</ul>

<p>Using Docker containers means everything is compartmentalized and that it's
easy to bootstrap my blog build environment onto another machine.  Containers
for the win.</p>

<h2 id="setup-a-new-octopress-3-flavored-jekyll-site">Setup a New (Octopress 3-Flavored!) Jekyll Site</h2>

<p>Create a new directory for your brand-new Jekyll site:</p>
<figure class="code-highlight-figure"><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row unnumbered"><div class="code-highlight-line">$ mkdir blog
</div></div><div data-line="2" class="code-highlight-row unnumbered"><div class="code-highlight-line">$ cd blog
</div></div></pre></div></figure>

<p>Copy your <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file into the new directory.</p>

<p>Use <code class="language-plaintext highlighter-rouge">docker-compose run</code> to start a shell into a new Docker container:</p>
<figure class="code-highlight-figure"><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row unnumbered"><div class="code-highlight-line">$ docker-compose run --rm --service-ports jekyll /bin/sh
</div></div></pre></div></figure>

<p>Install the Octopress gem:</p>
<figure class="code-highlight-figure"><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row unnumbered"><div class="code-highlight-line"># gem install octopress
</div></div><div data-line="2" class="code-highlight-row unnumbered"><div class="code-highlight-line">Fetching: titlecase-0.1.1.gem (100%)
</div></div><div data-line="3" class="code-highlight-row unnumbered"><div class="code-highlight-line">Successfully installed titlecase-0.1.1
</div></div><div data-line="4" class="code-highlight-row unnumbered"><div class="code-highlight-line">...
</div></div><div data-line="5" class="code-highlight-row unnumbered"><div class="code-highlight-line">Parsing documentation for octopress-3.0.11
</div></div><div data-line="6" class="code-highlight-row unnumbered"><div class="code-highlight-line">Installing ri documentation for octopress-3.0.11
</div></div><div data-line="7" class="code-highlight-row unnumbered"><div class="code-highlight-line">Done installing documentation for titlecase, octopress-deploy, octopress-hooks, octopress-escape-code, redcarpet, octopress after 1 seconds
</div></div><div data-line="8" class="code-highlight-row unnumbered"><div class="code-highlight-line">6 gems installed
</div></div></pre></div></figure>

<p>Use the <code class="language-plaintext highlighter-rouge">octopress new</code> command to create a new Octopress-flavored Jekyll site:</p>
<figure class="code-highlight-figure"><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row unnumbered"><div class="code-highlight-line">$ octopress new -f .
</div></div><div data-line="2" class="code-highlight-row unnumbered"><div class="code-highlight-line">Running bundle install in /srv/jekyll...
</div></div><div data-line="3" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Don't run Bundler as root. Bundler can ask for sudo if it is needed, and
</div></div><div data-line="4" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: installing your bundle as root will break this application for all non-root
</div></div><div data-line="5" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: users on this machine.
</div></div><div data-line="6" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: The dependency tzinfo-data (&gt;= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
</div></div><div data-line="7" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Fetching gem metadata from https://rubygems.org/...........
</div></div><div data-line="8" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Fetching version metadata from https://rubygems.org/..
</div></div><div data-line="9" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Fetching dependency metadata from https://rubygems.org/.
</div></div><div data-line="10" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Resolving dependencies...
</div></div><div data-line="11" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Fetching public_suffix 3.0.0
</div></div><div data-line="12" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Installing public_suffix 3.0.0
</div></div><div data-line="13" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Using bundler 1.15.3
</div></div><div data-line="14" class="code-highlight-row unnumbered"><div class="code-highlight-line">...
</div></div><div data-line="15" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Fetching jekyll 3.5.1
</div></div><div data-line="16" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Installing jekyll 3.5.1
</div></div><div data-line="17" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Fetching jekyll-feed 0.9.2
</div></div><div data-line="18" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Installing jekyll-feed 0.9.2
</div></div><div data-line="19" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Fetching minima 2.1.1
</div></div><div data-line="20" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Installing minima 2.1.1
</div></div><div data-line="21" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Bundle complete! 4 Gemfile dependencies, 22 gems now installed.
</div></div><div data-line="22" class="code-highlight-row unnumbered"><div class="code-highlight-line">  Bundler: Bundled gems are installed into /srv/bundle.
</div></div><div data-line="23" class="code-highlight-row unnumbered"><div class="code-highlight-line">New jekyll site installed in /srv/jekyll.
</div></div><div data-line="24" class="code-highlight-row unnumbered"><div class="code-highlight-line">Added Octopress scaffold:
</div></div><div data-line="25" class="code-highlight-row unnumbered"><div class="code-highlight-line"> + _templates/
</div></div><div data-line="26" class="code-highlight-row unnumbered"><div class="code-highlight-line"> +   draft
</div></div><div data-line="27" class="code-highlight-row unnumbered"><div class="code-highlight-line"> +   page
</div></div><div data-line="28" class="code-highlight-row unnumbered"><div class="code-highlight-line"> +   post
</div></div></pre></div></figure>

<p>(<em>Tip: You'll need to pass the <code class="language-plaintext highlighter-rouge">-f</code> flag to <code class="language-plaintext highlighter-rouge">octopress new</code> because you're
trying to install into a directory which already exists</em>)</p>

<p>Edit the <code class="language-plaintext highlighter-rouge">Gemfile</code> (created during <code class="language-plaintext highlighter-rouge">octopress new</code>) and minimally add the
baseline Jekyll and Octopress gem dependencies:</p>
<figure class="code-highlight-figure"><figcaption class="code-highlight-caption"><span class="code-highlight-caption-title">Gemfile</span></figcaption><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="n">source</span> <span class="s2">"https://rubygems.org"</span>
</div></div><div data-line="2" class="code-highlight-row numbered"><div class="code-highlight-line"> </div></div><div data-line="3" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="n">gem</span> <span class="s1">'jekyll'</span>
</div></div><div data-line="4" class="code-highlight-row numbered"><div class="code-highlight-line"> </div></div><div data-line="5" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="n">group</span> <span class="ss">:jekyll_plugins</span> <span class="k">do</span>
</div></div><div data-line="6" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'octopress'</span>
</div></div><div data-line="7" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">end</span>
</div></div></pre></div></figure>

<p>At this point, you have a brand-new empty Jekyll site with a few extra
Octopress-flavored bits (like the <code class="language-plaintext highlighter-rouge">_templates</code> directory), and you can run
<a href="https://jekyllrb.com/docs/usage/">normal Jekyll commands</a> like 'jekyll serve',
<code class="language-plaintext highlighter-rouge">jekyll build</code>, etc.</p>

<h2 id="managing-jekyll-plugin-gems">Managing Jekyll Plugin Gems</h2>

<p>Jekyll supports several ways of <a href="https://jekyllrb.com/docs/plugins/">managing gem-based
plugins</a>.</p>

<p>I opted to manage all my gem plugins via a Bundler group in the <code class="language-plaintext highlighter-rouge">Gemfile</code> file.
This just seemed the most straight-forward approach. It also seemed like a good
idea to track both the <code class="language-plaintext highlighter-rouge">Gemfile</code> file and counterpart <code class="language-plaintext highlighter-rouge">Gemfile.lock</code> file via
source-control (e.g. Git), but your mileage may vary.</p>

<p>There are several neat Octopress 3 gem plugins, but I found that several of the
plugins didn't work out-of-the-box with Jekyll 3.x (<em>because most of the
plugins haven't been maintained in over a year now</em>). But thanks to the
community-effect of GitHub, several other folks have fixed the Jekyll
incompatibility problems and submitted pull requests. You can install the
patched version of the various Octopress 3 gems by specifying an explicit
<code class="language-plaintext highlighter-rouge">git:</code> remote URL (and the specific <code class="language-plaintext highlighter-rouge">branch:</code>) for that particular gem in your
<code class="language-plaintext highlighter-rouge">Gemfile</code>.</p>

<p>Here is the final <code class="language-plaintext highlighter-rouge">Gemfile</code> I ended-up with, with various patched Octopress
gems:</p>
<figure class="code-highlight-figure"><figcaption class="code-highlight-caption"><span class="code-highlight-caption-title">Gemfile</span></figcaption><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="n">source</span> <span class="s2">"https://rubygems.org"</span>
</div></div><div data-line="2" class="code-highlight-row numbered"><div class="code-highlight-line"> </div></div><div data-line="3" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="n">gem</span> <span class="s1">'jekyll'</span>
</div></div><div data-line="4" class="code-highlight-row numbered"><div class="code-highlight-line"> </div></div><div data-line="5" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="n">group</span> <span class="ss">:jekyll_plugins</span> <span class="k">do</span>
</div></div><div data-line="6" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'jekyll-paginate'</span>
</div></div><div data-line="7" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'jekyll-redirect-from'</span>
</div></div><div data-line="8" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'jekyll-sitemap'</span>
</div></div><div data-line="9" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'octopress'</span>
</div></div><div data-line="10" class="code-highlight-row numbered marked-line start-marked-line end-marked-line"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'octopress-code-highlighter'</span><span class="p">,</span> <span class="ss">git: </span><span class="s1">'https://github.com/randycoulman/code-highlighter.git'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'handle-multiline-spans'</span>  <span class="c1"># Fix for multi-line &lt;span&gt;'s (gh:octopress/code-highlighter #8)</span>
</div></div><div data-line="11" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'octopress-codeblock'</span>
</div></div><div data-line="12" class="code-highlight-row numbered marked-line start-marked-line end-marked-line"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'octopress-codefence'</span><span class="p">,</span> <span class="ss">git: </span><span class="s1">'https://github.com/mkleucker/codefence.git'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'fix-warning-deprecated'</span>  <span class="c1"># Fix Jeykll 3 compatibility (gh:octopress/codefence #17)</span>
</div></div><div data-line="13" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'octopress-image-tag'</span>
</div></div><div data-line="14" class="code-highlight-row numbered marked-line start-marked-line"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'octopress-ink'</span><span class="p">,</span> <span class="ss">git: </span><span class="s1">'https://github.com/iphoting/ink.git'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'jekyll-3'</span>  <span class="c1"># Fix Jekyll 3 compatibility (gh:octopress/ink #65)</span>
</div></div><div data-line="15" class="code-highlight-row numbered marked-line"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'octopress-linkblog'</span><span class="p">,</span>  <span class="ss">git: </span><span class="s1">'https://github.com/andrewdavidbell/linkblog.git'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'jekyll3'</span>  <span class="c1"># Fix Jekyll 3 compatibility (gh:octopress/linkblog #7)</span>
</div></div><div data-line="16" class="code-highlight-row numbered marked-line end-marked-line"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'octopress-quote-tag'</span><span class="p">,</span> <span class="ss">git: </span><span class="s1">'https://github.com/NickTomlin/quote-tag.git'</span><span class="p">,</span> <span class="ss">branch: </span><span class="s1">'master'</span>  <span class="c1"># Fix Jeykll 3 compatibility</span>
</div></div><div data-line="17" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="n">gem</span> <span class="s1">'octopress-solarized'</span>
</div></div><div data-line="18" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">end</span>
</div></div></pre></div></figure>

<h2 id="migrating-content">Migrating Content</h2>

<p>You'll need to copy over whatever parts you need from your old Octopress 2 site
to your new Octopress 3 site:</p>

<ul>
  <li>Copy all your posts: <code class="language-plaintext highlighter-rouge">source/_posts</code> -&gt; <code class="language-plaintext highlighter-rouge">_posts</code></li>
  <li>Copy all your other pages</li>
  <li>Copy all your images: <code class="language-plaintext highlighter-rouge">source/images</code> -&gt; <code class="language-plaintext highlighter-rouge">images</code></li>
  <li>Copy any theme files: <code class="language-plaintext highlighter-rouge">source/_includes</code> -&gt; <code class="language-plaintext highlighter-rouge">_includes</code>, etc, etc.</li>
</ul>

<h2 id="installing-a-theme">Installing a Theme</h2>

<p>The original Octopress 2 theme was baked into the Octopress 2 git branch.</p>

<p>You could probably migrate over the old Octopress 2 theme from your old
Octopress 2 site, but now that we're using plain-vanilla Jekyll 3 that means we
can use <em>any</em> of the plethora of Jekyll-based themes.  So I went hunting for a
new theme.</p>

<p>I ended-up choosing the <a href="https://github.com/johnotander/pixyll">Pixyll</a> theme
because it was clean and modern.</p>

<p>I "installed" the Pixyll theme by:</p>

<ol>
  <li>Cloning the Pixyll theme from its GitHub repo to a separate directory.</li>
  <li>Manually copying over the files I wanted from the Pixyll directory into my
Octopress 3 site.</li>
  <li>Tracking the pristine Pixyll theme files in a <a href="https://github.com/tonyduckles/blog_source/commit/b1af1060">baseline
commit</a>.</li>
  <li>Personalizing the theme files as I wanted.</li>
</ol>

<p>I opted to create a "baseline" commit of the Pixyll theme (along with making
note of what Git revision # from the Pixyll repo I was forking at) to (in
theory) make it easier to take and review upstream changes to the Pixyll git
branch: create a local Git branch off that baseline commit, copy in any
upstream changes to the Pixyll theme, and then <code class="language-plaintext highlighter-rouge">git merge</code> forward onto my
local <code class="language-plaintext highlighter-rouge">master</code> branch and work through any merge conflicts.</p>

<p>Here are some of the customizations &amp; personalizations I made along the way:</p>

<ul>
  <li>Updated <code class="language-plaintext highlighter-rouge">_config.yml</code> to set various <code class="language-plaintext highlighter-rouge">site</code> flags which the Pixyll theme
respects.</li>
  <li>Updated <code class="language-plaintext highlighter-rouge">_includes/footer.html</code> to include my name and copyright.</li>
  <li>Updated <code class="language-plaintext highlighter-rouge">_includes/head.html</code> to add the <code class="language-plaintext highlighter-rouge">{% css_asset_tag %}</code> Liquid tag, which was needed to get the <code class="language-plaintext highlighter-rouge">octopress-solarized</code>
plugin to take effect (via the <code class="language-plaintext highlighter-rouge">octopress-asset-pipeline</code> plugin).</li>
  <li>Various tweaks to CSS styling.</li>
</ul>

<h2 id="resources">Resources</h2>

<p>Here are some helpful posts which laid out the vast majority of the migration
process:</p>

<ul>
  <li><a href="http://samwize.com/2015/09/30/migrating-octopress-2-to-octopress-3/">http://samwize.com/2015/09/30/migrating-octopress-2-to-octopress-3/</a></li>
  <li><a href="https://dgmstuart.github.io/blog/2016/01/22/migrating-from-octopress-2-to-3/">https://dgmstuart.github.io/blog/2016/01/22/migrating-from-octopress-2-to-3/</a></li>
  <li><a href="http://www.dracotorre.com/blog/site-updated-octopress-3/">http://www.dracotorre.com/blog/site-updated-octopress-3/</a></li>
</ul>

<p>Happy upgrading!</p>
]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Happy Retirement Google Reader! &rarr;]]></title>
    <link href="http://www.feedly.com/happy.html"/>
    <updated>2013-06-29T12:35:12-05:00</updated>
    <id>https://nynim.org/blog/2013/06/29/happy-retirement-google-reader</id>
    <category term="Web" />
      <content type="html"><![CDATA[<p><img src="https://nynim.org/images/2013/06/happy-retirement-gr.png" alt="Happy Retirement Google Reader!" /></p>
<blockquote>
  <p>After 8 years on the job, Google Reader is retiring on July 1st. Thank you for being there for us all this time!</p>
</blockquote>

<p>Thanks for being a great friend, Google Reader. And I'm happy to see that there
will be plenty of worthy alternatives to fill your absence.</p>
<p><a rel="bookmark" href="/blog/2013/06/29/happy-retirement-google-reader/">&#9875; Permalink</a></p>]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[The Death of Google Reader]]></title>
    <link href="/blog/2013/03/17/the-death-of-google-reader/"/>
    <updated>2013-03-17T13:57:22-05:00</updated>
    <id>https://nynim.org/blog/2013/03/17/the-death-of-google-reader</id>
    <category term="Web" />
      <content type="html"><![CDATA[<p>So, Google is <a href="http://googlereader.blogspot.com/2013/03/powering-down-google-reader.html">killing Google Reader</a>.</p>

<p>When I saw that headline on Wednesday evening, I felt a mixture of emotions:
anger, sadness, and worry. Anger that Google could shut-down a service that
was so personally-valuable to me; sadness that a web-service I've depending
upon for years was going away; worry that there might not be an alternative
service out there that could fill the same role in all the same ways that the
Google Reader ecosystem has.</p>

<!-- more -->

<p>You see, I've been <a href="https://nynim.org/blog/2007/05/19/google-reader/">using Google Reader since 2007</a>.
It's the web-app I use more than any others. I've had a dedicated Google
Reader pinned-tab open in all my Firefox sessions for as long as I can remember.</p>

<p>I use it every single day. It's the way I read the Internet.</p>

<p>Google Reader is more than just a simple web-app: it's the back-end RSS
aggregator service which crawls all the 120+ RSS feeds I follow and centrally
stores all the state about what unread (and starred) articles I have.  That
kind of <em>read-anywhere-sync-to-everywhere</em> workflow is immensely valuable to me
because I want to be able to access my news wherever I am and with whatever
device is most handy: I use the Google Reader web-app on my home laptop, home
desktop, and work desktop machines; I use <a href="http://reederapp.com/">Reeder</a> on my
iPhone; I use <a href="https://play.google.com/store/apps/details?id=com.noinnion.android.greader.readerpro&amp;hl=en">gReader</a>
on my Nexus 7 Android.</p>

<h2 id="community-reactions">Community Reactions</h2>
<p>It's been interesting to read the reactions/backlash from the tech community. I
found some interesting blog-posts via Twitter:</p>

<h3 id="the-social-web-isnt-a-replacement-for-rss">The Social Web Isn't a Replacement for RSS</h3>
<p>Christian Heilmann wrote "<a href="http://christianheilmann.com/2013/03/14/rip-google-reader-id-have-paid-for-you/">RIP Google Reader - I'd Have Paid For You</a>"
where we talks about how the social web will never be a replacement for the
one-stop aggregation service that Google Reader provides:</p>
<blockquote>
  <p>Yes, RSS has been declared dead many times and people keep banging on about the social web and that Facebook, Twitter, Reddit and others have replaced the old style of blogging and having an own feed. But I don’t buy it, sorry. <b>Every social network is full of senseless chatter and organised advertising.</b> Social media experts and PR folk make sure that information about certain products and celebrities get read and retweeted. I don’t care about that. <b>I don’t want it. The same way I don’t watch public access channels or randomly surf channels but instead plan what I want to see on TV.</b> Random exploration and finding things by chance is fun, but it is not helping you to keep up to date – it is the ADHD of information consumption.</p>
</blockquote>

<h3 id="moving-towards-a-walled-off-web">Moving Towards a Walled-Off Web?</h3>
<p>Jonathan Poritsky wrote a fantastic blog-post titled "<a href="http://www.candlerblog.com/2013/03/15/google-and-reader/">Reader's End and Google Today</a>",
where he points out that this could be the start of a disturbing shift in
Google's priorities:</p>
<blockquote>
  <p>The biggest issue doesn’t seem to be the loss of Reader itself, but the recognition <b>that Google’s priorities have shifted</b>
…</p>

  <p>But walls have sprouted up. Google can’t access the massive amounts of data people pour into Facebook and Twitter, so they built Google+ as their own social walled garden. Twitter is exerting control over how users experience their product, which shuts out competitors like Instagram (which is owned by Facebook), which can no longer display images inline in tweets. <b>The Web is getting smaller, not bigger, with each company working to become the umbrella under which you experience the Internet.</b> So Google has taken steps to make sure that the Web as users know it exists under their company banner, and Reader doesn’t fit in with that plan anymore.</p>

  <p>I was once a Google cheerleader. <b>Like many I believed their goal was to make a better Web for everyone</b>, with the one major tradeoff being that they would sell ads instead of charging users. That may once have been true but the Google of 2013 doesn’t want to build a better Web, it wants to build a better Google. I don’t think that goal is aligned with any of my own.</p>
</blockquote>

<p>With this move, Google is seeding a lot of ill-will in the tech community.
This feels very much against the "Do No Evil" slogan which Google touts.
If Google can shutdown a service as beloved as Reader, then it makes you
wonder which services are safe from the chopping-block…</p>

<h2 id="a-new-hope-google-reader-alternatives">A New Hope: Google Reader Alternatives</h2>

<p>Even though Google says that "<em>usage of Google Reader has declined</em>", there's
obviously still a <strong>lot</strong> of people in the tech community who still find great
value in RSS and a Reader-like service. And a lot can happen in the next 3
months leading up to the July 1st shutdown.</p>

<p>So, the imminent death of Google Reader could just be trigger-point needed to
spark another renaissance in RSS readers like what we had circa 2005-2006.</p>

<p>And it looks like we will have several options. <strong><a href="http://www.replacereader.com/">ReplaceReader</a></strong>
is a neat little site I found for folks to suggest replacements for Google Reader.</p>

<p>I expect the biggest challenge (for me personally) in finding a suitable
replacement for will be finding a solution that I can still (easily) access
(and seamlessly sync!) across multiple platforms.  I've gotten spoiled-rotten
by the native iOS and Android Google Reader clients.  And whatever I pick, I
want to make sure that I still have some exit-paths in case <em>that service</em>
closes-up shop for some reason. Some kind of open-source/self-host option could
be nice so that I can control my own data, but then again needing to maintain a
DB-backed website isn't really something I want to do anymore.</p>

<p><strong><a href="http://tt-rss.org/">Tiny Tiny RSS</a></strong> is an open-source self-host option which
looks pretty mature. This looks promising if all you need is a simple web interface.</p>

<p>The most interesting option I've seen so far is <strong><a href="http://www.newsblur.com">NewsBlur</a></strong>.
It's open-source (on Github) and self-host-able (nice to know I have options)
but also has a paid hosted option. And it looks extremely polished and
<strong>simply gorgeous</strong>. It keeps the same simple/functional interface principals as
Google Reader while updating the UI for 2013. And there are iOS and Android
clients so that I can still access my news on whatever device I want.</p>

<p>I expect (or at least hope) there will be a flurry of activity in the RSS
reader space in the next few months leading up to the July 1st shutdown.  It
will be interesting to see what alternatives the community embraces. I plan to
watch the space for a while before committing to any particular option, to see
which options rise to the top.</p>

<p>I plan to write a follow-up post in a few months detailing what option I end-up
going with…</p>

]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Project-Level .ackrc Files]]></title>
    <link href="/blog/2013/03/09/project-level-ackrc-files/"/>
    <updated>2013-03-09T11:56:26-06:00</updated>
    <id>https://nynim.org/blog/2013/03/09/project-level-ackrc-files</id>
    <category term="Coding" />
      <content type="html"><![CDATA[<p><a href="http://betterthangrep.com/">Ack</a> is a fantastic (<em>and portable!</em>) replacement
for <code class="language-plaintext highlighter-rouge">grep</code>. It's aimed at programmers and by default will only search a
white-list of known file-extensions so that it will only search the "code" in a
directory.</p>

<p><code class="language-plaintext highlighter-rouge">ack</code> looks at your <code class="language-plaintext highlighter-rouge">~/.ackrc</code> file to get any customized "default" settings
you want. I use my (<em>user-level</em>) <code class="language-plaintext highlighter-rouge">~/.ackrc</code> to <a href="https://github.com/tonyduckles/dotfiles/blob/master/.ackrc">enable some personalized default options</a>,
e.g. color-ize output, always use a <code class="language-plaintext highlighter-rouge">$PAGER</code>, sort the output by filename, etc.</p>

<p>But sometimes I want to have <strong>directory-level</strong> (or project-level) additional
settings, namely to always exclude/ignore certain directories when searching at
the project-level.</p>

<!-- more -->

<p>For example, here are the exclusions I want for my <a href="https://nynim.org/blog/2011/12/19/hello-octopress/">Octopress</a>
build environment:</p>
<figure class="code-highlight-figure"><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row unnumbered"><div class="code-highlight-line">--ignore-dir=.gist-cache
</div></div><div data-line="2" class="code-highlight-row unnumbered"><div class="code-highlight-line">--ignore-dir=.pygments-cache
</div></div><div data-line="3" class="code-highlight-row unnumbered"><div class="code-highlight-line">--ignore-dir=.sass-cache
</div></div><div data-line="4" class="code-highlight-row unnumbered"><div class="code-highlight-line">--ignore-dir=.themes
</div></div><div data-line="5" class="code-highlight-row unnumbered"><div class="code-highlight-line">--ignore-dir=\_deploy
</div></div><div data-line="6" class="code-highlight-row unnumbered"><div class="code-highlight-line">--ignore-dir=public
</div></div></pre></div></figure>

<p>So, I wrote-up a little <code class="language-plaintext highlighter-rouge">ack</code> wrapper script: <a href="https://github.com/tonyduckles/dotfiles/blob/master/bin/ack-wrapper">ack-wrapper</a>.
It crawls the directory tree looking for an additional "local" <code class="language-plaintext highlighter-rouge">.ackrc</code> file,
starting from the current working directory crawling up through any parent
directories until we find a <code class="language-plaintext highlighter-rouge">.ackrc</code> file (<em>or until we reach either <code class="language-plaintext highlighter-rouge">$HOME</code> or
<code class="language-plaintext highlighter-rouge">/</code></em>).</p>

<p>(<strong>Update 2012-03-10:</strong> <em>It looks like <a href="https://github.com/petdance/ack2">Ack v2.0</a>
supports PWD .ackrc files natively, and has some other neat enhancements to boot!</em>)</p>

<figure class="code-highlight-figure"><figcaption class="code-highlight-caption"><span class="code-highlight-caption-title">https://github.com/tonyduckles/dotfiles/blob/master/bin/ack-wrapper</span><a class="code-highlight-caption-link" href="https://github.com/tonyduckles/dotfiles/blob/master/bin/ack-wrapper">link</a></figcaption><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c">#!/bin/sh</span>
</div></div><div data-line="2" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># Wrapper around 'ack' to crawl all directories from `pwd` to / (or $HOME)</span>
</div></div><div data-line="3" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># looking for a "local" .ackrc file. Useful for setting project-level</span>
</div></div><div data-line="4" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># --ignore-dir settings.</span>
</div></div><div data-line="5" class="code-highlight-row numbered"><div class="code-highlight-line"> </div></div><div data-line="6" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># Search for "local" .ackrc file in CWD or any parents</span>
</div></div><div data-line="7" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="nv">HOME</span><span class="o">=</span>~
</div></div><div data-line="8" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="nv">ackrc</span><span class="o">=</span><span class="s2">""</span>
</div></div><div data-line="9" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="nv">match</span><span class="o">=</span><span class="s2">""</span>
</div></div><div data-line="10" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="nb">dir</span><span class="o">=</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span>
</div></div><div data-line="11" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">while</span> <span class="o">[</span> <span class="s2">"</span><span class="k">$&#x7b;</span><span class="nv">dir</span><span class="k">&#x7d;</span><span class="s2">"</span> <span class="o">!=</span> <span class="s2">"/"</span> <span class="nt">-a</span> <span class="s2">"</span><span class="k">$&#x7b;</span><span class="nv">match</span><span class="k">&#x7d;</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">""</span> <span class="o">]</span><span class="p">;</span> <span class="k">do</span>
</div></div><div data-line="12" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">    if</span> <span class="o">[</span> <span class="nt">-e</span> <span class="s2">"</span><span class="k">$&#x7b;</span><span class="nv">dir</span><span class="k">&#x7d;</span><span class="s2">/.ackrc"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</div></div><div data-line="13" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">        if</span> <span class="o">[</span> <span class="s2">"</span><span class="k">$&#x7b;</span><span class="nv">dir</span><span class="k">&#x7d;</span><span class="s2">"</span> <span class="o">!=</span> <span class="s2">"</span><span class="k">$&#x7b;</span><span class="nv">HOME</span><span class="k">&#x7d;</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</div></div><div data-line="14" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">            </span><span class="nv">match</span><span class="o">=</span>1
</div></div><div data-line="15" class="code-highlight-row numbered"><div class="code-highlight-line">            <span class="nb">echo</span> <span class="s2">"(Include: </span><span class="k">$&#x7b;</span><span class="nv">dir</span><span class="k">&#x7d;</span><span class="s2">/.ackrc)"</span>
</div></div><div data-line="16" class="code-highlight-row numbered"><div class="code-highlight-line">            <span class="nv">ackrc</span><span class="o">=</span><span class="si">$(</span>egrep <span class="s2">"^[^#]"</span> <span class="s2">"</span><span class="k">$&#x7b;</span><span class="nv">dir</span><span class="k">&#x7d;</span><span class="s2">/.ackrc"</span> | <span class="nb">tr</span> <span class="s1">'\n'</span> <span class="s1">' '</span><span class="si">)</span>
</div></div><div data-line="17" class="code-highlight-row numbered"><div class="code-highlight-line">        <span class="k">else</span>
</div></div><div data-line="18" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">            </span><span class="nv">match</span><span class="o">=</span>0
</div></div><div data-line="19" class="code-highlight-row numbered"><div class="code-highlight-line">        <span class="k">fi</span>
</div></div><div data-line="20" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">    else</span>
</div></div><div data-line="21" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">        </span><span class="nb">dir</span><span class="o">=</span><span class="si">$(</span><span class="nb">dirname</span> <span class="s2">"</span><span class="k">$&#x7b;</span><span class="nv">dir</span><span class="k">&#x7d;</span><span class="s2">"</span><span class="si">)</span>
</div></div><div data-line="22" class="code-highlight-row numbered"><div class="code-highlight-line">    <span class="k">fi</span>
</div></div><div data-line="23" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">done</span>
</div></div><div data-line="24" class="code-highlight-row numbered"><div class="code-highlight-line"> </div></div><div data-line="25" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># Add quote-wrapping for any additional args to ensure proper passing to</span>
</div></div><div data-line="26" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># real 'ack'.</span>
</div></div><div data-line="27" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">for </span>arg <span class="k">in</span> <span class="s2">"</span><span class="nv">$@</span><span class="s2">"</span><span class="p">;</span> <span class="k">do</span>
</div></div><div data-line="28" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">    if</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="k">$&#x7b;</span><span class="nv">ackrc</span><span class="k">&#x7d;</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</div></div><div data-line="29" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">        </span><span class="nv">ackrc</span><span class="o">=</span><span class="s2">"'</span><span class="k">$&#x7b;</span><span class="nv">arg</span><span class="k">&#x7d;</span><span class="s2">'"</span>
</div></div><div data-line="30" class="code-highlight-row numbered"><div class="code-highlight-line">    <span class="k">else</span>
</div></div><div data-line="31" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">        </span><span class="nv">ackrc</span><span class="o">=</span><span class="s2">"</span><span class="k">$&#x7b;</span><span class="nv">ackrc</span><span class="k">&#x7d;</span><span class="s2"> '</span><span class="k">$&#x7b;</span><span class="nv">arg</span><span class="k">&#x7d;</span><span class="s2">'"</span>
</div></div><div data-line="32" class="code-highlight-row numbered"><div class="code-highlight-line">    <span class="k">fi</span>
</div></div><div data-line="33" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">done</span>
</div></div><div data-line="34" class="code-highlight-row numbered"><div class="code-highlight-line"> </div></div><div data-line="35" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># Build command to eval</span>
</div></div><div data-line="36" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="nv">cmd</span><span class="o">=</span><span class="s2">"command ack </span><span class="k">$&#x7b;</span><span class="nv">ackrc</span><span class="k">&#x7d;</span><span class="s2">"</span>
</div></div><div data-line="37" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-t</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</div></div><div data-line="38" class="code-highlight-row numbered"><div class="code-highlight-line">    <span class="c"># If stdin is a pipe, use cat to redirect stdin to stdout and pipe</span>
</div></div><div data-line="39" class="code-highlight-row numbered"><div class="code-highlight-line">    <span class="c"># that data into ack.</span>
</div></div><div data-line="40" class="code-highlight-row numbered"><div class="code-highlight-line">    <span class="nv">cmd</span><span class="o">=</span><span class="s2">"cat | </span><span class="k">$&#x7b;</span><span class="nv">cmd</span><span class="k">&#x7d;</span><span class="s2">"</span>
</div></div><div data-line="41" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="k">fi</span>
</div></div><div data-line="42" class="code-highlight-row numbered"><div class="code-highlight-line"> </div></div><div data-line="43" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># Eval the final command</span>
</div></div><div data-line="44" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="nb">eval</span> <span class="s2">"</span><span class="k">$&#x7b;</span><span class="nv">cmd</span><span class="k">&#x7d;</span><span class="s2">"</span>
</div></div></pre></div></figure>

<h3 id="usage">Usage</h3>

<ul>
  <li>Grab the latest version of the <a href="https://github.com/tonyduckles/dotfiles/blob/master/bin/ack-wrapper">ack-wrapper</a> script,
drop it somewhere in your <code class="language-plaintext highlighter-rouge">$PATH</code> (<em>I like having a <code class="language-plaintext highlighter-rouge">~/bin/</code> directory</em>), and
make sure it's executable (<code class="language-plaintext highlighter-rouge">chmod 755 path/to/ack-wrapper</code>).</li>
  <li>Update your <code class="language-plaintext highlighter-rouge">.bashrc</code> to <code class="language-plaintext highlighter-rouge">alias ack=ack-wrapper</code>, so that running <code class="language-plaintext highlighter-rouge">ack ...</code>
will first call the wrapper script, search for any "local" <code class="language-plaintext highlighter-rouge">.ackrc</code> files,
insert any additional options found into the original supplied command-line
arguments, and finally call the (real) <code class="language-plaintext highlighter-rouge">ack</code> executable.</li>
</ul>
]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Switching to Airvoice Wireless: A Cheaper iPhone Cellular Plan]]></title>
    <link href="/blog/2013/03/06/switching-to-airvoice-wireless-a-cheaper-iphone-cellular-plan/"/>
    <updated>2013-03-06T19:41:47-06:00</updated>
    <id>https://nynim.org/blog/2013/03/06/switching-to-airvoice-wireless-a-cheaper-iphone-cellular-plan</id>
    <category term="Finance" />
      <content type="html"><![CDATA[<p>For the past two and a half years, I've been rocking my <a href="http://en.wikipedia.org/wiki/IPhone_4">iPhone 4</a>.
It's held-up well over the years. My previous phone, an <a href="http://en.wikipedia.org/wiki/IPhone_3G">iPhone 3G</a>,
was pretty darn sluggish by the end of its two year tenure and I eagerly
awaited the point where I was eligible for a phone-upgrade so that I could
upgrade to the (<em>at that time</em>) brand-new iPhone 4.  I just always assumed that
was the "<em>way it worked</em>": sign-up for a new two-year contract, get a
subsidized price on a nice shiny-new piece of phone-hardware, and don't really
think twice about the recurring $80+/month phone-bill.</p>

<p>But I didn't realize there were other alternatives…</p>

<!-- more -->

<h2 id="mobile-network-virtual-operators-mvnos">Mobile Network Virtual Operators (MVNOs)</h2>
<p>While reading though the back-archives of one of my <a href="https://nynim.org/blog/2013/03/03/mr-money-mustache-from-zero-to-hero-in-one-blog-post/">new favorite finance blogs</a>,
I came across an interesting article, "<a href="http://www.mrmoneymustache.com/2012/10/11/our-new-10-00-per-month-iphone-plans/">Our New $10.00 Per Month iPhone Plans</a>",
talking about switching to a monthly pre-paid plan and paying only $10/month
for an iPhone cellular plan.  I had heard mention of some of these "pre-paid"
cellular vendors before but had never really looked into them much or really
understand what they were:</p>

<blockquote>
  <p>Many of these new options are called Mobile Network Virtual Operators (MNVOs), and they are in fact just re-selling access to the bigger carriers’ networks. So you get the same reception, coverage, and reliability as you had before.</p>
</blockquote>

<p>I had never realized! Same cellular network/service, you're just paying a
different middle-man. And there's <a href="http://en.wikipedia.org/wiki/List_of_United_States_mobile_virtual_network_operators">quite a long list</a>
of these MVNO's, lots of options for each of the major US mobile operators:
AT&amp;T, Sprint, Verizon, and T-Mobile. Reading the comments in that article was
where a lot of the gold was; there's lots of great information in there. A lot
of these MVNO's have been around for quite a while and are pretty
well-established.</p>

<p>So, since my iPhone 4 hardware was still giving my reasonable performance,
I decided to skip the expected hardware-upgrade and stick with my current
handset and try to milk it for all its worth.</p>

<h2 id="choosing-a-company--plan">Choosing a Company &amp; Plan</h2>
<p>After doing some more reading, I ending-up choosing <a href="http://www.airvoicewireless.com/">Airvoice Wireless</a>
because they seem to be one of the favorite (and well-established) AT&amp;T-based
MVNO's and they have <a href="http://www.airvoicewireless.com/plans-2/">lots of different plan options</a>.</p>

<p>Since my phone is <em>already</em> setup to run on AT&amp;T's GSM network, you wouldn't
even need a carrier-unlock to be able to jump to one of the AT&amp;T MVNO's because
it's still using the same carrier network behind-the-scenes.  Though, truth be
told, since I was out of my 2-year contract with AT&amp;T, I did make use of AT&amp;T's
<a href="http://www.att.com/esupport/article.jsp?sid=KB414532&amp;cv=820&amp;title=What%20are%20the%20eligibility%20requirements%20for%20unlocking%20iPhone%3F#fbid=eGlx2oAwj-R">free carrier-unlock</a>
before ditching my AT&amp;T plan so that I could use my handset when traveling
internationally should the opportunity arise…</p>

<p>I considered going the ultra-frugal <a href="http://www.airvoicewireless.com/plans-2/10-plan/">$10/month Talk &amp; Text</a>
plan but that seemed a bit too restrictive. I'm coming from my AT&amp;T plan which
had 450 anytime minutes (<em>though I only used on average ~100 minutes per month
and had a huge rollover pool</em>) and my grandfathered-in Unlimited data plan from
my original iPhone 3G based plan. I usually averaged around 200MB-300MB of data
per month, so I wasn't really reaping much from my Unlimited data-plan.  The
ultra-frugal $10/month plan would mean I'd <em>really</em> need to scrutinize my
cellular data usage (<em>$0.33/MB adds up quick!</em>), as in <em>turning-off</em> cellular
data most of the time and only turning it on when I absolutely needed. That was
just a bit too extreme for me — too much penny-pinching.</p>

<p>So, to make my transition from AT&amp;T to Airvoice Wireless as painless as
possible, I opted for their <a href="http://www.airvoicewireless.com/plans-2/unlimited-plans-with-data/40-unlimited-plan-with-data/">$40/month Unlimited Plan with Data</a>
which has unlimited talk &amp; text (<em>neither of which I use much of</em>) and
500MB of data per month.</p>

<h2 id="making-the-switch">Making the Switch</h2>
<p>Here's a quick summary of the setup/transition process…</p>

<ul>
  <li>Bought a Airvoice Wireless SIM card through their website. It arrived in a
few business days.</li>
  <li>Activate the SIM card on Airvoice Wireless's website, picked which plan I
wanted to use, got a phone number assigned to my new account (<em>or you could
have ported your existing cell number to Airvoice</em>), and put some money into
my account.</li>
  <li>Using <a href="http://amjath.com/2010/08/how-to-make-a-micro-sim-from-a-normal-sim.html">some print-out templates</a>
I found online (<em>and putting on my arts-and-crafts hat</em>), I trimmed the
normal-sized SIM card to the micro-SIM size to fit my iPhone SIM card tray.</li>
  <li>Drop the new SIM card into my iPhone. No need to turn-off or power-cycle.
It connected to the Airvoice Wireless network immediately. You'll notice
the <code class="language-plaintext highlighter-rouge">AIRVOICE WIRELESS</code> vendor banner on the upper-left of your iPhone screen.</li>
  <li>To get cellular data working, you need to install the correct Access Point
Name (APN) settings for Airvoice Wireless. Just connect to Wi-Fi, fire-up
Mobile Safari on your iPhone, navigate to <a href="http://www.unlockit.co.nz/">http://www.unlockit.co.nz/</a>,
select "Airvoice", and click Install. It will prompt you to install a new
Profile with the APN settings for Airvoice. With the correct APN settings
in-place, I turned off Wi-Fi and the 3G data worked as normal.</li>
</ul>

<p>Easy-peasy.</p>

<h2 id="initial-observations">Initial Observations</h2>
<p>It's only been a few weeks so far, but all-in-all the transition has
been extremely painless.</p>

<h3 id="pros">Pro's:</h3>

<ul>
  <li><strong>I cut my phone-bill in <u>half</u></strong>: $85/month -&gt; $40/month. Yowza!</li>
  <li><strong>Same cellular service</strong>, since I'm still effectively using AT&amp;T's network.</li>
  <li><strong>Not locked into another contract.</strong> I can switch to another vendor whenever
I want, if for some reason I don't like Airvoice. That kind of agility is
refreshing.</li>
</ul>

<h3 id="cons">Con's:</h3>

<ul>
  <li><strong>No integrated iPhone visual voicemail</strong>, at least that I know of.  (<em>Though
this doesn't bother me much because I ported my number to Google Voice as
part of this transition, but that's a story for another time…</em>)</li>
  <li><strong>Need to remember to login every 30 days to renew your service.</strong> This could
admittedly be a bit of a drag, but it's easy to add a monthly reminder, and a
few extra minutes worth of work per month seems well worth the savings of
$40/month x 12 months = ~$500/year.</li>
  <li><strong>No fancy website.</strong> AT&amp;T had a pretty decent website with all kinds of
historical usage statistics. I get none of that now. Airvoice is a smaller
operation and I'm okay with that. If I want to get usage stats, I just need
to do it myself using the usage-counters on my iPhone.</li>
</ul>

<p>Even if some months I do happen to go over my 500MB/month cap, I would simply
drop another $40 in my account early and that would restart the 30 day
expiration over again.  As I understand it, a given prepayment expires either:
after 30 days or after you've used up your quota, whichever comes first.</p>

<p>If I find the $40/month plan is either too limiting or I'm consistently not
using everything I'm paying for, I can always easily switch to a different
plan/tier. Based on the Airvoice Wireless <a href="http://www.airvoicewireless.com/TermsOfServiceAll.aspx">terms of service</a>,
it sounds like they won't refund you a partial-month at all; you need to make a
cut at the end of your 30 day cycle (to make the most of your money) and you
need to call customer-service to switch your account over to the new plan. I
(obviously) haven't tried this yet but it sounds pretty painless.</p>

<p>And by saving <strong>$500/year</strong> on my cell phone bill, that's basically the price
of a brand-new (unlocked) flagship phone. So, I could <em>still</em> upgrade my
hardware and still come-out ahead because I'm not tied into an expensive
contract with one of the big cellular companies.</p>

<p>It will be interesting to see how this plays-out over the next few months, to
see what happens to my service at the end of a 30-day cycle (e.g. <em>how apparent
will it be that my service has cut off and that I need to pump more money into
my account?</em>), to see how often I use up my 500MB quota before the 30 days are
up, etc. I'm just darn excited to have interesting new options/alternatives.</p>
]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Mr. Money Mustache: From Zero to Hero in One Blog Post &rarr;]]></title>
    <link href="http://www.mrmoneymustache.com/2013/02/22/getting-rich-from-zero-to-hero-in-one-blog-post/"/>
    <updated>2013-03-03T15:07:14-06:00</updated>
    <id>https://nynim.org/blog/2013/03/03/mr-money-mustache-from-zero-to-hero-in-one-blog-post</id>
    <category term="Finance" />
      <content type="html"><![CDATA[<p>A few months ago, one of my friends introduced me to this blog:
<a href="http://www.mrmoneymustache.com/">Mr. Money Mustache: Early Retirement through Badassity</a>.
(<em>The blog title is sheer awesome-ness!</em>) I quickly fell in-love with nearly
everything he was writing about.  Lots of great financial and lifestyle advice
there.</p>

<p>He's been able to put to clear words what I've been starting to realize lately:</p>

<blockquote>
  <p>The bottom line is this: by focusing on happiness itself, you can lead a much better life than those who focus on convenience, luxury, and following the lead of the financially illiterate herd that is the TV-ad-absorbing Middle Class of the United States today (and most of the other rich countries).  Happiness comes from many sources, but none of these sources involve car or purse upgrades. No matter what the herd or the TV set tells you, this is the truth.</p>
</blockquote>

<p>Living within your means and cutting-back on needless spending means lower
monthly expenses. By cutting those recurring expenses, you're able to save
more money.  The more money you save, the more compounding interest and
investing works in your favor. By learning to live more frugally, you're
changing your <strong>lifestyle and mindset</strong> and you end-up not <em>needing</em> as much
money for your post-retirement lifestyle. It becomes this snowball effect and
you may even be able to <a href="http://www.mrmoneymustache.com/2012/01/13/the-shockingly-simple-math-behind-early-retirement/">retire a lot earlier than you expected</a>.</p>

<p><a rel="bookmark" href="/blog/2013/03/03/mr-money-mustache-from-zero-to-hero-in-one-blog-post/">&#9875; Permalink</a></p>]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Using Kerberos (GSSAPI) Auth with OpenSSH in Cygwin on Windows]]></title>
    <link href="/blog/2012/08/25/using-kerberos-gssapi-auth-with-openssh-in-cygwin-on-windows/"/>
    <updated>2012-08-25T17:19:00-05:00</updated>
    <id>https://nynim.org/blog/2012/08/25/using-kerberos-gssapi-auth-with-openssh-in-cygwin-on-windows</id>
    <category term="UNIX" />
      <content type="html"><![CDATA[<p>On my Windows machines at both work and home, I like to run <a href="http://www.cygwin.com/">Cygwin</a>
to get a UNIX-like environment on Windows: <code class="language-plaintext highlighter-rouge">screen</code>, <code class="language-plaintext highlighter-rouge">ssh</code>, <code class="language-plaintext highlighter-rouge">grep</code>, etc.
I use SSH's <a href="https://hkn.eecs.berkeley.edu/~dhsu/ssh_public_key_howto.html">public key authentication</a>
pretty extensively to get password-less authentication to make it dead-easy
(and quick!) to SSH around to different machines.</p>

<p>On some of the non-UNIX machines at work, I couldn't get SSH public-key auth
working, but those machines <em>do</em> support Kerberos auth (binding to Active
Directory). Based on my Google searches, all I could find were articles talking
about compiling OpenSSH from source to get a working Kerberos-enabled version
of OpenSSH on Cygwin. So, that made it sound like it would be a pain to get
this working.  But, after doing some more playing around, I found this was
actually easy to setup once you understand the various pieces. Since I couldn't
find any helpful information online when I first tried to get this working, I
figured I'd write up what worked for me in case that helps other people.</p>

<!-- more -->

<h2 id="installation">Installation</h2>

<h4 id="step-1---install-the-openssh-package">Step 1 - Install the "openssh" package</h4>

<p>Install the <code class="language-plaintext highlighter-rouge">openssh</code> package. This gets you the OpenSSH client tools, e.g. <code class="language-plaintext highlighter-rouge">ssh</code>,
<code class="language-plaintext highlighter-rouge">ssh-agent</code>, etc.</p>

<h4 id="step-2---install-the-heimdal-package">Step 2 - Install the "heimdal" package</h4>

<p>Install the <code class="language-plaintext highlighter-rouge">heimdal</code> package, which supplies an implementation of the Kerberos
tools. This gets you things like <code class="language-plaintext highlighter-rouge">kinit</code>, <code class="language-plaintext highlighter-rouge">klist</code>, etc.</p>

<h4 id="step-3---optional-configure-your-krb5conf-file">Step 3 - (Optional) Configure your "krb5.conf" file</h4>
<p>You may want to configure your <code class="language-plaintext highlighter-rouge">/etc/krb5.conf</code> file to list a default realm
so that you don't need to specify it when doing the <code class="language-plaintext highlighter-rouge">kinit</code> later on:</p>

<figure class="code-highlight-figure"><figcaption class="code-highlight-caption"><span class="code-highlight-caption-title">/etc/krb5.conf</span></figcaption><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="o">[</span>libdefaults]
</div></div><div data-line="2" class="code-highlight-row numbered"><div class="code-highlight-line">  default_realm <span class="o">=</span> REALM_NAME
</div></div></pre></div></figure>

<p>Alternatively, rather than fiddling with <code class="language-plaintext highlighter-rouge">/etc/krb5.conf</code> on my Cygwin
install, I opted to use the <code class="language-plaintext highlighter-rouge">KRB5_CONFIG</code> environment variable (see the
<code class="language-plaintext highlighter-rouge">kinit</code> manpage) to point to a <code class="language-plaintext highlighter-rouge">~/.krb5.conf</code> file instead to keep my
Kerberos config confined to my <code class="language-plaintext highlighter-rouge">$HOME</code> directory (since I
<a href="https://nynim.org/blog/2011/12/20/home-sweet-home/">keep my $HOME directory under version control</a>).</p>

<h4 id="step-4---modify-your-sshconfig-file">Step 4 - Modify your ".ssh/config" file</h4>

<p>Modify your <code class="language-plaintext highlighter-rouge">~/.ssh/config</code> file to enable GSSAPI authentication:</p>

<figure class="code-highlight-figure"><figcaption class="code-highlight-caption"><span class="code-highlight-caption-title">~/.ssh/config</span></figcaption><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row numbered"><div class="code-highlight-line">GSSAPIAuthentication yes
</div></div></pre></div></figure>

<h2 id="usage">Usage</h2>
<ul>
  <li>Before you <code class="language-plaintext highlighter-rouge">ssh</code> to a remote machine where you want to use Kerberized credentials,
simply run <code class="language-plaintext highlighter-rouge">kinit</code> to acquire a new Kerberos ticket. (Pro tip: you can run <code class="language-plaintext highlighter-rouge">klist</code> to
list all your active Kerberos tickets and their expiration dates.)</li>
  <li>With the <code class="language-plaintext highlighter-rouge">GSSAPIAuthentication</code> directive in your <code class="language-plaintext highlighter-rouge">.ssh/config</code> file, that should
enable GSSAPI authentication for free. There's also a <code class="language-plaintext highlighter-rouge">-K</code> param to the <code class="language-plaintext highlighter-rouge">ssh</code> command
which talks about enabling GSSAPI auth and forwarding, which I'm not entirely sure
what that controls, but my guess is that it's for opting into GSSAPI auth mode if
you <strong>don't</strong> have that directive in your <code class="language-plaintext highlighter-rouge">.ssh/config</code> file.</li>
</ul>

<p>I hope this helps someone else who's trying to get Kerberized SSH working on Cygwin.
Happy SSH'ing!</p>
]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[All-In-One Home File Server: VMware ESXi, OpenIndiana, and ZFS]]></title>
    <link href="/blog/2012/06/04/all-in-one-home-file-server-vmware-esxi-openindiana-and-zfs/"/>
    <updated>2012-06-04T21:25:00-05:00</updated>
    <id>https://nynim.org/blog/2012/06/04/all-in-one-home-file-server-vmware-esxi-openindiana-and-zfs</id>
    <category term="Storage" /><category term="Hardware" />
      <content type="html"><![CDATA[<p>Just over a year ago, I built a new home file-server since I was quickly
outgrowing my existing storage capacity. Rather than just dropping more HDD's
into the existing hardware I had running, I succumbed to geek techno-lust and
opted to explore new technologies: <strong>ZFS</strong> and <strong>VMWare ESXi</strong>.</p>

<!-- more -->

<h2 id="going-virtualized">Going Virtualized</h2>
<p>Virtualization is huge in the enterprise-space these days. After talking with
some friends from work about what kind of virtualization strategies we're using
for our in-house datacenter, I became enticed by the idea of running a "bare metal"
<a href="http://en.wikipedia.org/wiki/Hypervisor">hypervisor</a>: install virtualization
software as the primary OS on the physical hardware and spinning up VM's for
different logical needs.</p>

<p>Over the years, I've accumulated a fair amount of old computers. Back in the day,
those extra computers gave me an opportunity to try-out new OSes/software: playing
with different Ubuntu configurations, tinkering with different *BSD flavors, etc.
But once my spare-time dried up, all that old computer hardware just became <em>clutter</em>
taking-up space and it seemed like such a pain to fire up an old computer just to
tinker with some new configuration.</p>

<p>So, going virtualized held a lot of (obvious) appeal to me: minimize my physical
hardware (less power consumed, less physical space, etc.) while letting me easily
tinker with new configurations.</p>

<h2 id="zfs-powered">ZFS-Powered</h2>
<p>Based on <a href="https://nynim.org/blog/2011/03/05/zfs-the-last-word-in-filesystems/">previous research</a>,
I knew that I really wanted to move to some kind of <strong>ZFS-backed</strong> storage solution,
to protect/maintain the integrity of my ever-increasing digital collection.</p>

<p>After following the "zfs-discuss" mailing list for a few months, given my simple
home-needs a simple mirrored configuration (along with proper backups) seemed like
the <a href="http://constantin.glez.de/blog/2010/01/home-server-raid-greed-and-why-mirroring-still-best">best solution</a>.
If I need more space, I can expand horizontally: add another pair of drives (of
whatever size is appropriate) to expand my pool. It was this easy expansion
which pushed me towards a mirrored configuration rather than a RAID-Z style
configuration. The trouble with RAID-Z is that you can't add new devices to a
vdev after you've initially created it; the only way to "grow" a RAID-Z vdev is
by replacing <strong>all</strong> of the individual drives (<em>one at a time, waiting for each
to resilver</em>) and setting the "autoexpand" property on the pool so that ZFS
will auto-expand the pool based on the new common maximum size of each of the
individual drives. But, that's a whole lot of moving parts (<em>pun intended</em>) and
replacing all the drives in the pool isn't quite my idea of easy expansion.
Mirroring just seems easier, given that I don't need lots of individual drives
for raw performance reasons.</p>

<h2 id="all-in-one-solution">All-in-One Solution</h2>
<p>Somewhere along the line in my research, I stumbled upon <a href="http://www.napp-it.org/napp-it/all-in-one/index_en.html"><strong>napp-it</strong></a>,
which introduced me to this idea of an "all-in-one" fileserver: using VMWare
ESXi as a bare-metal hypervisor, having a Solaris-based VM running on the ESXi
datastore which will control the mass-storage ZFS pool, and then exporting an
NFS share back out to ESXi so that you can store the bulk of the VMs on
ZFS-backed storage. It's a bit complicated at first glance, but it performs
great (thanks to hardware-passthrough) and lets me easily manage the bulk of my
VMs on ZFS-backed storage.</p>

<p>To make use of ESXi's hardware-passthrough support, you need to run server-grade
hardware. For me, that meant going with an Xeon-based CPU with appropriate motherboard
chipset. But that also gave other server-grade wins like using ECC memory and IPMI
(easy remote-control of the console, which is <em>awesome</em>).</p>

<h2 id="shopping-list">Shopping List</h2>
<p>Here is the hardware I ended-up going with:</p>

<ul>
  <li><a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16819117225">Intel Xeon X3440 Lynfield 2.53GHz</a></li>
  <li><a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16813182211">Supermicro X8SIL-F-O</a></li>
  <li>2x <a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16820139077">Kingston 4GB DDR3 ECC Unbuffered</a></li>
  <li><a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16816117157">Intel SASUC8I (LSI 1068E) SATA/SAS card</a></li>
  <li><a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16817151088">SeaSonic X650 Gold PSU</a></li>
  <li><a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16811352004">Fractal Design Define R3 Mid-Tower Case</a></li>
  <li>2x <a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16822145475">2TB Hitachi Deskstar 5K3000</a> (primary pool)</li>
  <li>2x <a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16822145475">2TB Hitachi Deskstar 5K3000</a> (backup pool)</li>
  <li>2x <a href="http://www.newegg.com/Product/Product.aspx?Item=N82E16822145475">2TB Hitachi Deskstar 5K3000</a> (off-site backup pool)</li>
</ul>

<p>The Xeon chipset was needed for some of the hardware passthrough features in
ESXi, e.g. passing through the HBA card directly to the Solaris-based VM so
that ZFS can have direct access to the physical drives.</p>

<p>The 650W power-supply ended-up being way more than I needed. The server uses
less than 100W when idle, though that PSU is still quite efficient even with
such low power draw.</p>

<h2 id="looking-back">Looking Back</h2>
<p>The setup has been awesome so far. Even just getting to play around with
server-grade hardware has been an eye-opening experience. Being able to
remote-control the server, e.g. mounting an ISO remotely over the Java-based
client and installing the OS on the computer all without needing to hook-up a
keyboard or monitor, has been a revelation.  (<em>Goodbye old CRT monitor that I
used to keep around for my server closet!</em>)</p>

<p>I love the flexibility of ESXi. I have an Ubuntu VM for development/testing, a
FreeBSD VM for running Mediatomb (PS3 media-server), etc. It even let me play
around with different Solaris flavors while trying to figure out what OS I
wanted to use for the ZFS back-end. It's just so easy to spin-up new VMs to
test an idea or just to play around.</p>

<p>I started out using Solaris 11 Edition (free), which ran fine for the better
part of a year. I tried upgrading to Solaris 11/11 earlier this year only to
find that Solaris 11 apparently doesn't play nicely with ESXi. From there, I
jumped over to using <a href="http://openindiana.org/">OpenIndiana</a>, the open-source
spin-off from the now-defunct OpenSolaris lineage.</p>

<p>ZFS has been a huge win too. Taking nightly snapshots makes it dead-easy to
look back in time to see how day was several months ago. The snapshots also
make it easy to send the incremental differences to a backup pool. The built-in
CIFS server makes it dead-easy to mount the shares on Windows, and the
filesystem snapshots are easily accessible via the "Previous Versions" tab in
Windows Explorer. I also really love the idea of the "pool". I can create
different filesystems to group/organize my data (e.g. photos vs. music vs.
backups), enable compression on a per-filesystem basis, set quotas per
filesystem, etc.</p>

<p>I really love the "all-in-one" idea: ZFS reliability combined with the
flexibility of VMWare ESXi.</p>
]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[svn2svn: Replaying SVN History]]></title>
    <link href="/blog/2012/02/01/svn2svn-replaying-svn-history/"/>
    <updated>2012-02-01T21:00:00-06:00</updated>
    <id>https://nynim.org/blog/2012/02/01/svn2svn-replaying-svn-history</id>
    <category term="Coding" />
      <content type="html"><![CDATA[<p>I've found myself in a (potentially unique) situation where we have a
<em>gi-normous</em> <a href="http://subversion.apache.org/">Subversion</a> repository at work and
we've been exploring ways on how to trim off some of the fat but still keep all
the logical history so that we could still use things like <code class="language-plaintext highlighter-rouge">svn blame</code> to
drill-down into code-history. Our central SVN repository is some 4-5 years old
and a whopping 300GB+ on-disk. (Yowza!) What we'd really like to do is dump
just the <code class="language-plaintext highlighter-rouge">/trunk</code> history out to a new repo and roll forward with that, ditching
any historical baggage from old topic branches (<code class="language-plaintext highlighter-rouge">/branches</code>). The trouble is,
I haven't been able to find any tools to do this.</p>

<p>So, I ended-up writing my own tool to do this. But, first, some back-story…</p>

<!-- more -->

<h2 id="existing-tools">Existing Tools</h2>
<p>In doing some searches for variations around "<em>svn repo filter</em>", I found a lot
of people pointing to the "<a href="http://svnbook.red-bean.com/en/1.7/svn.ref.svndumpfilter.html">svndumpfilter</a>"
utility as the tool-of-choice. Sadly, it doesn't seem to be quite "smart" enough
to do what I'd like it to do. It seems to be aimed at (namely) filtering an
<code class="language-plaintext highlighter-rouge">svnadmin dump</code> stream, taking only certain paths in the SVN "filesystem". That
works fine if you're trying to take an isolated folder/project from the repository,
but if that folder/project has ever been merged into from any paths <em>outside</em> of
the target filter path, then things fall apart. The "trouble" are the copy-from's…</p>

<p>For example, say that you have a repo with a typical trunk/tags/branches setup.
Say you create a new topic branch (e.g. <code class="language-plaintext highlighter-rouge">svn copy /trunk /branches/my-fix &amp;&amp; svn co /branches/my-fix</code>)
and happily work on your sandboxed branch. Say that you decide to rename some of
the pre-existing files/folders, so you run the appropriate <code class="language-plaintext highlighter-rouge">svn move</code> commands and
happily commit those changes to your branch. Once everything is working happily,
you go to merge these changes into <code class="language-plaintext highlighter-rouge">/trunk</code> and that all works great. After the
commit, if you run a <code class="language-plaintext highlighter-rouge">svn log -v -l1 /trunk</code> to look at the details of the most
recent commit to trunk, you'll see something like this:</p>

<figure class="code-highlight-figure"><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row unnumbered"><div class="code-highlight-line">A /trunk/Project/RenamedFolder (from /branches/my-fix/Project/RenamedFolder@12345)
</div></div></pre></div></figure>

<p>…which only describes the (top-level) folder rename, not any add/modifications/renames/etc
that might have happened <em>inside</em> that folder on the branch. At the full repo-level,
SVN can get away with just doing a "<code class="language-plaintext highlighter-rouge">svn copy</code>" from the branch to trunk, so that
when you do an "<code class="language-plaintext highlighter-rouge">svn log</code>" on the new "RenamedFolder" path it will walk from trunk
back into that originating topic branch and follow the rename (copy or move) that
happened within.</p>

<p>The <code class="language-plaintext highlighter-rouge">svnadmin dump</code> will show the same "copy-from" action as <code class="language-plaintext highlighter-rouge">svn log -v</code> did,
since the dump stream will need to be able to recreate that same ancestry. When
<code class="language-plaintext highlighter-rouge">svndumpfilter</code> sees this, it throws up its hands and returns an error because
it doesn't know how to <em>recreate</em> the logical history that happened between
when that branch originally forked from trunk versus the final state of that branch.
That is, assuming the copy-from path even has any ancestry back to trunk…</p>

<h2 id="if-you-want-something-done-right">If You Want Something Done Right…</h2>
<p>In my searches, I had found a few folks that had hacked together there own
solutions. None of those quite fit my problem at hand, so naturally I decided
to start working on my own solution.</p>

<p>The closest fit I found was a project named "<a href="http://code.google.com/p/svn2svn/">svn2svn</a>"
hosted on Google Code. It had copied parts of the "<a href="https://bitbucket.org/andialbrecht/hgsvn/overview">hgsvn</a>"
project (<em>sychronizing between Mercurial and Subversion repositories</em>) and
slapped them together in a way that worked for the original author's needs.
It used "<code class="language-plaintext highlighter-rouge">svn log</code>" to walk the entire history of a given path in a source
repository and then manually replayed those changes to a working-copy of
some path in a target repository. Revision by revision, it would replay
the delta, recreating the history of just the source path in the target repo.
This was <em>oh-so-close</em> to what I wanted, except that it also didn't correctly
handle the copy-from case. And the repository I really wanted to replay was
<em>littered</em> with copy-from's, since I'm trying to replay just the history from
<code class="language-plaintext highlighter-rouge">/trunk</code> and we create topic-branches for <em>everything</em>.</p>

<h2 id="enter-svn2svn">Enter svn2svn</h2>
<p>So, I started with the <a href="http://code.google.com/p/svn2svn/">svn2svn</a> project,
got familiar with the code, and started hacking to extend the code to solve my
problem at hand.</p>

<p>The net-result is my own (nearly completely rewritten) take on the problem,
a project which I'm also calling <strong><a href="https://nynim.org/code/svn2svn/">svn2svn</a></strong>.</p>

<p>I've made several enhancements to the original script:</p>

<ul>
  <li>
    <p><strong>Full ancestry (copy-from) support.</strong> This was the tricky part. It took
several different iterations/rewrites to get something which worked for all
the different edge-cases. The general idea here is that we can use <code class="language-plaintext highlighter-rouge">svn log</code>
to walk backwards through the ancestry on a copy-from case: if we can trace
back our ancestry to same source path we're replaying, then we can do an <code class="language-plaintext highlighter-rouge">svn
copy</code> from that original parent and then do <code class="language-plaintext highlighter-rouge">svn export</code> to update the
contents of all the files to match the final copy-from version. There's also
some extra recursion that needs to happen here, to handle cases where child
files got renamed inside of a parent-renamed folder. There are other
edge-cases like files getting replaced inside a parent-renamed folder.</p>
  </li>
  <li>
    <p><strong>Use revprops for source-tracking and resume support.</strong> Subversion has
revision properties, key+value pairs that are associated with a particular
revision/commit. I'm setting some <code class="language-plaintext highlighter-rouge">svn2svn:*</code> rev-props to track the source
URL, source repository UUID (i.e. in case source URL now points at a
physically different repo), and source revision #. This is all needed for
proper resume support, since for the ancestry support we have to maintain a
mapping-table of source_rev -&gt; target_rev, so that when we find a copy-from
from some revision # in the source repo, we can map this to the equivalent
revision # in the target repo so we can do an equivalent <code class="language-plaintext highlighter-rouge">svn copy</code> command.</p>
  </li>
  <li>
    <p><strong>Better verbosity output, and optional debug output.</strong> As I was playing with
the rewrite, I quickly found I needed better debug output, to see which shell
commands were being run and to display just general debug/status messages as
we do all this new complicate ancestry-walking logic. Bonus: the debugging
messages have colored output, using <a href="http://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes">ANSI escape
codes</a> which all
self-respecting terminal emulators should respect.</p>
  </li>
  <li>
    <p><strong>All commits (including initial import) go through the same central
code-path, which means we could run a (client-side) pre-commit script to
scrub the contents of the target working-copy before each commit.</strong> This is
where the power of doing the manual replay of changes really starts to shine.
We have full control over the pending changes, which means that if your
original trunk history had some files which you didn't want to transition
into the new replayed repo then you could easily <code class="language-plaintext highlighter-rouge">svn rm</code> those files from
the working-copy before the commit happens. That could be as simple as
excluding certain fixed paths, but it could be a lot more flexible like
searching the entire working-copy tree and removing any files which match
a certain file-name. Heck, you could even modify the working-copy
file-contents at this point…or add brand-new files if you want. We're
<em>replaying</em> the SVN history here will full control of the target content,
so this opens a lot of interesting options.</p>
  </li>
</ul>

<p>Check-out the <a href="https://nynim.org/code/svn2svn/">svn2svn project page</a> for more details.</p>

<p>Also, I have the project mirrored on <a href="https://github.com/tonyduckles/svn2svn">Github</a>
so please feel free to fork the project, send me issues/enhancements, or
send me pull-requests for any tweaks you've made.</p>
]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Home Sweet $HOME]]></title>
    <link href="/blog/2011/12/20/home-sweet-home/"/>
    <updated>2011-12-20T19:35:00-06:00</updated>
    <id>https://nynim.org/blog/2011/12/20/home-sweet-home</id>
    <category term="UNIX" />
      <content type="html"><![CDATA[<p><img class="right" src="https://nynim.org/images/2011/12/img_Terminal.png" width="180" height="180" alt="Terminal" />
About a year and a half ago, I stumbled upon Ryan Tomayko's "<a href="https://github.com/rtomayko/dotfiles">dotfiles</a>"
repository on Github. The moment I saw it, I thought it was a <em>great</em> idea:
the idea of managing all your <code class="language-plaintext highlighter-rouge">$HOME</code> directory "dot-files" in a Git repository.
That single idea led me on a personal crusade to better understand all the
different configuration files that live in your UNIX (<em>Linux, Mac OSX, etc.</em>)
home directory, and the end-result was creating my own "<a href="https://github.com/tonyduckles/dotfiles">dotfiles</a>"
Git repository for synchronizing/tracking/deploying my dot-files between the
various machines I work upon.</p>

<p>I learned a lot of neat stuff along the way, including some config options
which I never knew were there and some tricks which really optimized my
command-line shell experience.</p>

<!-- more -->

<h2 id="using-git">Using Git</h2>
<p>I found it to be a very natural fit to use Git to track the edits to my dot-files.
It makes it dead-easy to see what changes I've made since the last commit and
easy to commit those changes and push them to a central Git repository.</p>

<p>Git also makes it dead-easy to "bootstrap" my dot-files environment into a
brand-new home directory:</p>

<figure class="code-highlight-figure"><figcaption class="code-highlight-caption"><span class="code-highlight-caption-title">Dotfiles bootstrap</span></figcaption><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># Clone the repo into ~/dotfiles/</span>
</div></div><div data-line="2" class="code-highlight-row numbered"><div class="code-highlight-line">git clone git://github.com/tonyduckles/dotfiles.git dotfiles
</div></div><div data-line="3" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># Move all the contents (including .git, but excluding the specal "." and ".." directories)</span>
</div></div><div data-line="4" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># into ~, overwriting any pre-existing files</span>
</div></div><div data-line="5" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="nb">mv </span>dotfiles/.??<span class="k">*</span> <span class="nb">.</span>
</div></div><div data-line="6" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># Clean-up the now-orphaned ~/dotfiles/ directory</span>
</div></div><div data-line="7" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="nb">rm</span> <span class="nt">-rf</span> dotfiles
</div></div></pre></div></figure>

<h2 id="bin">~/bin/</h2>
<p>Ryan has a <code class="language-plaintext highlighter-rouge">~/bin/</code> directory in his dot-files repo with all kinds of nifty
little utilities.</p>

<p>There's a bunch of great Git-related utilities in there, e.g.:</p>

<ul>
  <li><a href="https://github.com/rtomayko/dotfiles/blob/rtomayko/bin/git-up">git-up</a> - Like <code class="language-plaintext highlighter-rouge">git-pull</code> but show a short and sexy log of changes immediately after merging (git-up) or rebasing (git-reup).</li>
  <li><a href="https://github.com/rtomayko/dotfiles/blob/rtomayko/bin/git-incoming">git-incoming</a>/<a href="https://github.com/rtomayko/dotfiles/blob/rtomayko/bin/git-outgoing">git-outgoing</a> - Shows difference between the current branch vs. the upstream branch.</li>
</ul>

<p>And there's also some great general-purpose utilities in there, e.g.:</p>

<ul>
  <li><a href="http://betterthangrep.com/">ack</a> - A tool like <code class="language-plaintext highlighter-rouge">grep</code>, designed for programmers with large trees of heterogeneous source code. Very handy for recursive file-searching.</li>
</ul>

<h2 id="gitconfig">~/.gitconfig</h2>
<p>Ryan has lots of nifty tricks in his <code class="language-plaintext highlighter-rouge">.gitconfig</code> file, tying in short-cuts for
calling the helper utilities in <code class="language-plaintext highlighter-rouge">~/bin/</code>. Also, you can setup Git to use ANSI
colors for various sub-commands (diff, status), which is <em>very</em> handy.</p>

<h2 id="inputrc">~/.inputrc</h2>
<p>There's a lot of neat options in the <code class="language-plaintext highlighter-rouge">.inputrc</code> file, for binding key-sequences
to various command-line options. For example:</p>
<figure class="code-highlight-figure"><figcaption class="code-highlight-caption"><span class="code-highlight-caption-title">.inputrc snippet</span></figcaption><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row numbered"><div class="code-highlight-line">C-n: history-search-forward
</div></div><div data-line="2" class="code-highlight-row numbered"><div class="code-highlight-line">C-p: history-search-backward
</div></div></pre></div></figure>
<p>…are the <strong>two single-most time-saving key-bindings</strong> that I stumbled upon. This
lets you type a partial command and then use <code class="language-plaintext highlighter-rouge">Ctrl-n</code> and <code class="language-plaintext highlighter-rouge">Ctrl-p</code> to
completion-match the rest of the line based on your command history. Awesome!</p>

<p>I find this incredibly useful to completion-match hostnames for <code class="language-plaintext highlighter-rouge">ssh</code> commands or other
longer commands which I often type. Completion-matching for-the-win!</p>

<h2 id="vim-vimrc-and-vim">Vim: .vimrc and .vim/</h2>
<p>Before setting out on this project, I had used Vi some but hadn't really explored
(or understood) the full-power that was <a href="http://www.vim.org/">Vim</a>. Digging
into the <code class="language-plaintext highlighter-rouge">.vimrc</code> file, reading-up on all the various config options, and
digging into all the <code class="language-plaintext highlighter-rouge">~/.vim/</code> plugins which Ryan had was an eye-opening
experience. I came to realize just how much you could extend and customize
Vim to meet your needs. Simple things like getting syntax-highlighting
and color-schemes set by default made for an oh-so-much more pleasant
Vim experience.</p>

<p>This all led me to learning a lot more about Vim and becoming much more proficient
using Vim. It's now my text-based editor of choice. Once you get to know the
key-bindings and the various commands, the command-chaining that you can do
in Vim is incredibly powerful.</p>

<h2 id="sharing-is-fun">Sharing is Fun!</h2>
<p>If you're at all interesting in learning more about customizing your Unix
shell environment, looking at other people's dot-files is a great learning
experience. And publishing your dot-files on Github is a great way to
share your shell-environment with the world so that others can learn
and explore.</p>
]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Hello Octopress!]]></title>
    <link href="/blog/2011/12/19/hello-octopress/"/>
    <updated>2011-12-19T21:53:00-06:00</updated>
    <id>https://nynim.org/blog/2011/12/19/hello-octopress</id>
    <category term="Blog" />
      <content type="html"><![CDATA[<p>A few days ago I stumbled upon a neat project named <strong><a href="http://octopress.org/">Octopress</a></strong>:</p>
<blockquote>
  <p>Octopress is an obsessively designed framework for Jekyll blogging. It’s easy to configure and easy to deploy. Sweet huh?
– <cite><a href="https://github.com/imathis/octopress">Octopress</a></cite></p>
</blockquote>

<p>Sweet indeed! The more I read about it, the more intrigued I became. After a
few hours spent exploring the code, importing the posts/pages from my Wordpress
install into Octopress, and tweaking the default theme to my taste, I've taken
the jump and moved my site over to using the Octopress platform.</p>

<!-- more -->

<p>Octopress is a simple blogging platform aimed at hackers. It's built around the
<a href="http://github.com/mojombo/jekyll">Jekyll</a> engine, which is the blog-aware static
site generator that powers <a href="http://pages.github.com/">Github Pages</a>.  The "database"
for the site is simply a collection of flat text files using
<a href="http://daringfireball.net/projects/markdown/">Markdown</a> (or Textile) mark-up. You
can setup either "posts" or "pages", all as you'd expect. When you're ready to
deploy, you use <code class="language-plaintext highlighter-rouge">Rakefile</code> automation to "generate" the static site files.</p>

<p>It also has a great default theme (IMHO) – very clean and simple – which was
actually my original attraction. It was only after reading more about the project
that I realized it had huge appeal to my geek-side too.</p>

<h2 id="why-octopress">Why Octopress?</h2>
<p>It was Matt Gemmell's "<a href="http://mattgemmell.com/2011/09/12/blogging-with-octopress/">Blogging With Octopress</a>"
post which resonated the most with me:</p>
<blockquote>
  <p>WordPress is excellent, but it’s over-featured for what I need, and its PHP/MySQL guts are opaque. I don’t really like the idea of all my writing being inside a big database either; it’s a single point of failure, and that makes me uneasy.
– <cite><a href="http://mattgemmell.com/2011/09/12/blogging-with-octopress/">Matt Gemmell</a></cite></p>
</blockquote>

<p>For what I do with my website, Wordpress was overkill. It ended-up being
<em>Yet-Another-Thing-I-Needed-To-Maintain-Security-Patches-On™</em>.</p>

<p>I was immediately attracted to the simplicity which Octopress provides: editing
pages and posts as plain-text text-files, all static files so no security-patches
or vulnerabilities to worry about, and easily portable and backup-able. It combines
a lot of things which I have grown to love: I can keep all my content in a Git
repository so that I can publish from multiple locations (<em>if need be</em>), I can
work on the site entirely in text (<code class="language-plaintext highlighter-rouge">vim</code> + <code class="language-plaintext highlighter-rouge">screen</code>), and it's dead easy to
deploy and backup.</p>

<h2 id="migrating-from-wordpress">Migrating from Wordpress</h2>
<p>It really didn't take that long to migrate everything over to Octopress.</p>

<p>It took a few hours to get a cleaned-up import of my old Wordpress content.
Based on the recommendation of others, I used <a href="https://github.com/thomasf/exitwp">exitwp</a>
to create Jekyll-style posts based on my Wordpress content. This went fine for
the most part, but there were some parts in the Wordpress export file that I
needed to fiddle to keep the <code class="language-plaintext highlighter-rouge">exitwp</code> script from crashing. The end result
was that I had a directory full of simple <code class="language-plaintext highlighter-rouge">*.markdown</code> files which represented
all the content from my Wordpress site. I spent a few hours going through those
files and cleaning up the Markdown syntax until it matched what I wanted. Some
of my original Wordpress posts were a mixture of Wordpress mark-up and raw HTML,
and that threw the <code class="language-plaintext highlighter-rouge">exitwp</code> parser for a loop in some places. It did a great
job overall though.</p>

<p>From there, I spent some time looking through the guts of the Octopress source
code getting acquainted with things and seeing how I might be able to fiddle
with some of its inner fiddly-bits. It's written with native support for
customization, trying to keep a clear separation between the core "code" of
the site (<em>which can be overwritten during a future upgrade</em>) versus any
"customization" files which the user might make changes too. It's a fantastic
paradigm and one that you don't often see too much of in projects.</p>

<p>Last but not least, I spent some time tweaking the theme to my tasting (<em>…a
bit of pepper here, a bit of salt there…</em>). As I mentioned above, it natively
carves out files which are earmarked for user-customizations, where you can
tweak the CSS (SASS), colors, page/sidebar dimensions, etc. It's all just
extremely well-thought-out and a joy to use and extend.</p>

<h2 id="ending-thoughts">Ending Thoughts</h2>
<p>This has been a fun pet project for me.  It's the first time I've used a Ruby environment.
It's also the first time I've played with <a href="http://sass-lang.com/">SASS</a> (<code class="language-plaintext highlighter-rouge">*.scss</code>),
and boy is it going to be hard to go back to writing plain-old CSS.
SASS's <a href="http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html">color functions</a>
are ridiculously slick: being able to do things like <code class="language-plaintext highlighter-rouge">desaturate(lighten($nav-bg, 8), 15)</code>
in a <code class="language-plaintext highlighter-rouge">*.scss</code> file is <em>awesome</em> and makes tweaking a site's color-scheme
oh-so-much easier.</p>

<p>I'm a bit sad to have lost the lifestream (<a href="https://github.com/dcramer/wp-lifestream/">wp-lifestream</a>)
functionality from my old Wordpress site. But then again I'm already doing
full exports of most of my lifestream'd websites (Google Reader, Delicious),
so it might not be too hard to generate static lifestream pages using a
<code class="language-plaintext highlighter-rouge">cronjob</code> or something. That's a project for another day…</p>

<h2 id="useful-links">Useful Links</h2>
<ul>
  <li><a href="http://mattgemmell.com/2011/09/12/blogging-with-octopress/">Blogging With Octopress</a> -
A fantastic and well-thought-out introduction and overview of Octopress.</li>
  <li><a href="http://zanshin.net/2011/08/11/switching-to-octopress/">Switching to Octopress</a> -
A great end-to-end summary of the Wordpress-to-Octopress transition process.</li>
  <li><a href="http://www.meatleasing.com/octopress-hidden-features/index.html">Octopress Hidden Features</a> -
Pointing out some of subtle features of Octopress.</li>
  <li><a href="https://github.com/thomasf/exitwp">exitwp</a> -
Tool for converting Wordpress export XML to the Jekyll blog engine.</li>
  <li><a href="http://www.vim.org/scripts/script.php?script_id=3835">vim-octopress</a> -
Vim syntax highlighting mode for Octopress-flavored <code class="language-plaintext highlighter-rouge">*.markdown</code> files.</li>
</ul>

]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[ZFS: The Last Word in Filesystems]]></title>
    <link href="/blog/2011/03/05/zfs-the-last-word-in-filesystems/"/>
    <updated>2011-03-05T18:56:01-06:00</updated>
    <id>https://nynim.org/blog/2011/03/05/zfs-the-last-word-in-filesystems</id>
    <category term="Storage" /><category term="UNIX" />
      <content type="html"><![CDATA[<p>I've been spending a lot of time lately reading-up on different options since
my current home file-server setup is slowly running out of disk-space. Sure, I
could just throw some new HDD's in there, but the inner-geek in me really
wants to setup a new system that has both redundancy and scalability –
something more robust and future-proof.</p>

<p>In reading through different online forums, the topic of ZFS-based systems
kept coming-up again and again. I had heard the term "ZFS" thrown around
before but I had never really spent the time to read-up on it. It's just
another filesystem, right? How fancy can it be?</p>

<p>Well, <strong>ZFS is just damn cool</strong>. ZFS is <em>lot</em> more than "<em>just another
filesystem</em>"…</p>

<!-- more -->

<ul>
  <li>It's a
<a href="http://blogs.sun.com/bonwick/entry/rampant_layering_violation">filesystem, logical volume manager, and software RAID subsystem</a>
all wrapped-up into one end-to-end system.</li>
  <li>It does <a href="http://blogs.sun.com/bonwick/entry/zfs_end_to_end_data">end-to-end checksums</a>,
making sure the raw data read off the physical disk matches the same data
that was previously written to disk.</li>
  <li>It is a transactional file system, which guarantees a consistent on-disk
data-state even with catastrophic failures like a power loss.</li>
  <li>You can do data scrubbing online, as opposed to the normal Linux solution
of ext2/ext3/ext4 where you can only "fsck" a filesystem offline.</li>
  <li>It uses a copy-on-write strategy which makes doing filesystem snapshot'ing
a breeze. For example, you could setup a cronjob to take nightly snapshots
of your filesystem to facilitate incremental backups, i.e. allowing you to
go back in time and recover old-states of files.</li>
  <li>It has <a href="http://blogs.sun.com/bonwick/entry/raid_z">RAID-Z support</a>, which
is similar to RAID-5/RAID-6 except without the potentially-fatal
"<em>RAID-5 write hole</em>" problem, in part thanks to the copy-on-write strategy.</li>
  <li>You can <a href="http://dlc.sun.com/osol/docs/content/ZFSADMIN/gbchx.html">replicate ZFS filesystems</a>
(<code class="language-plaintext highlighter-rouge">zfs send</code> and <code class="language-plaintext highlighter-rouge">zfs receive</code>), snapshots and all, making it dead-easy to
backup ZFS filesystems to remote machines.</li>
</ul>

<p>Very, very cool stuff! The more I read about it, the more I wanted it for my
next-generation file-server solution. In particular, I really love the idea of
end-to-end checksums. I want to make darn sure that the OS can realize when
the disks are serving up garbage-data or if somehow bits have mysteriously
been corrupted on-disk (as unlikely as that might be).</p>

<p>In reading about all these various historical problems that ZFS sets out to
address, I'm really surprised that more OSes haven't tried to adopt superior
filesystem technology. The classic problem for so many Windows-users seems to
be that their installs get "corrupt", likely usually due to folks forcibly
powering-off their computers before the hard-drives have a chance to flush
their write-buffer to disk. And then folks get to cope with BSOD's or broken
Windows installs. Sad times for everyone. Filesystems seem like such a
foundational part of the OS that you'd expect companies would spend the time
to make them as robust as possible. I read some rumors that Apple was planning
to incorporate ZFS into OSX a few years back, but apparently that fell apart
for some reason. Sad…</p>

<p>Kudos to the Sun folks for designing such a fantastic filesystem and then
open-sourcing it!</p>

]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Backup your Google Apps (or Gmail) e-mail over IMAP using imapsync]]></title>
    <link href="/blog/2010/08/08/backup-your-google-apps-or-gmail-e-mail-over-imap-using-imapsync/"/>
    <updated>2010-08-08T00:35:18-05:00</updated>
    <id>https://nynim.org/blog/2010/08/08/backup-your-google-apps-or-gmail-e-mail-over-imap-using-imapsync</id>
    <category term="Storage" /><category term="E-mail" /><category term="UNIX" />
      <content type="html"><![CDATA[<p>I have confidence in Google's ability to keep their e-mail service up-and-
running and keep proper backups of data, but my e-mail history is <em>my</em> data
and I like to have my own copy of it. Since Google provides access to your
Google Apps (and Gmail) e-mail over IMAP, you can do all kinds of things using
standard tools, e.g. synchronize your Gmail e-mail to a local mailbox using
IMAP.</p>

<p>I run an Ubuntu box at home and it was easy to install the <code class="language-plaintext highlighter-rouge">dovecot-imapd</code>
package to get an IMAP server installed. Since my box is behind my
router/firewall, I'm wasn't that concerned with tweaking Dovecot's default
configuration, but I'm sure you could fiddle with the config to ensure that
Dovecot only binds to <code class="language-plaintext highlighter-rouge">127.0.0.1</code>.</p>

<p>From there, it's just a matter of using <code class="language-plaintext highlighter-rouge">imapsync</code>, just like I ended-up
using previously to
<a href="https://nynim.org/blog/2010/08/07/moving-e-mail-from-gmail-to-google-apps-over-imap-using-imapsync/">initially transfer all my e-mail to my Google Apps account</a>.</p>

<!-- more -->

<p>Here's the script:</p>

<figure class="code-highlight-figure"><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># Sync e-mail from "username@yourdomain.com" to localhost, using IMAP</span>
</div></div><div data-line="2" class="code-highlight-row numbered"><div class="code-highlight-line">imapsync <span class="nt">--host1</span> imap.gmail.com <span class="nt">--user1</span> username@yourdomain.com
</div></div><div data-line="3" class="code-highlight-row numbered"><div class="code-highlight-line">         <span class="nt">--passfile1</span> /path/to/gmail_mirror.passfile1 <span class="se">\</span>
</div></div><div data-line="4" class="code-highlight-row numbered"><div class="code-highlight-line">         <span class="nt">--host2</span> localhost <span class="nt">--user2</span> username
</div></div><div data-line="5" class="code-highlight-row numbered"><div class="code-highlight-line">         <span class="nt">--passfile2</span> /path/to/gmail_mirror.passfile2 <span class="se">\</span>
</div></div><div data-line="6" class="code-highlight-row numbered"><div class="code-highlight-line">         <span class="nt">--ssl1</span> <span class="se">\</span>
</div></div><div data-line="7" class="code-highlight-row numbered"><div class="code-highlight-line">         <span class="nt">--useheader</span> <span class="s1">'Message-Id'</span> <span class="nt">--skipsize</span> <span class="nt">--allowsizemismatch</span> <span class="se">\</span>
</div></div><div data-line="8" class="code-highlight-row numbered"><div class="code-highlight-line">         <span class="nt">--syncinternaldates</span> <span class="nt">--noauthmd5</span> <span class="nt">-nofoldersizes</span><span class="se">\</span>
</div></div><div data-line="9" class="code-highlight-row numbered"><div class="code-highlight-line">         <span class="nt">--split1</span> 100 <span class="nt">--split2</span> 100 <span class="se">\</span>
</div></div><div data-line="10" class="code-highlight-row numbered"><div class="code-highlight-line">         <span class="nt">--regextrans2</span> <span class="s1">'s/\[Gmail\]/username\@somedomain/'</span> <span class="se">\</span>
</div></div><div data-line="11" class="code-highlight-row numbered"><div class="code-highlight-line">         <span class="nt">--include</span> <span class="s2">"All Mail|Sent Mail"</span> <span class="nt">--delete2</span> <span class="nt">--expunge2</span>
</div></div></pre></div></figure>

<p>The <code class="language-plaintext highlighter-rouge">--regextrans2</code> option rewrites IMAP folder-names on-the-fly, so that my
local IMAP folder structure can be different than the structure on Gmail's
server. For example, the top Gmail IMAP folder is <code class="language-plaintext highlighter-rouge">[Gmail]</code> which wasn't all
that useful for me, so instead I rewrote that top-level folder to be
<code class="language-plaintext highlighter-rouge">username@somedomain</code> so that the local folder name (e.g. in <code class="language-plaintext highlighter-rouge">~/mail/</code>) would
match the source e-mail address.</p>

<p>You can also use the <code class="language-plaintext highlighter-rouge">--include</code> option to decide which IMAP folders to copy.
I opted to just copy "All Mail" and "Sent Mail", which gives me a copy of all
my mail but doesn't preserve any information about the labels I might have had
assigned to those messages in Gmail.</p>

<p>The initial copy will definitely take a few hours (or more), depending on how
much e-mail you have in your Gmail account. But this works great for me and
stores the mail in "mbox" format locally so I can even access the mail locally
via mutt/alpine/etc.</p>

]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Moving E-Mail from Gmail to Google Apps over IMAP using imapsync]]></title>
    <link href="/blog/2010/08/07/moving-e-mail-from-gmail-to-google-apps-over-imap-using-imapsync/"/>
    <updated>2010-08-07T23:31:49-05:00</updated>
    <id>https://nynim.org/blog/2010/08/07/moving-e-mail-from-gmail-to-google-apps-over-imap-using-imapsync</id>
    <category term="Storage" /><category term="E-mail" /><category term="UNIX" />
      <content type="html"><![CDATA[<p>Two years ago, I transitioned from using regular Gmail (i.e. "…@gmail.com")
to setting up Google Apps for "nynim.org" (i.e. "…@nynim.org"). I had found
the following article back then which gave me helpful hints on how to use
<code class="language-plaintext highlighter-rouge">imapsync</code> to push data to my new Google Apps e-mail address:</p>

<p><a href="http://gemal.dk/blog/2008/04/08/completed_the_gmail_migration/">http://gemal.dk/blog/2008/04/08/completed_the_gmail_migration/</a></p>

<p>I adapted that script for my own needs, and I was able to successfully copy
all the mail from my regular Gmail account to my new Google Apps account.</p>

<!-- more -->

<p>Here is the final script I ended-up with:</p>

<figure class="code-highlight-figure"><div class="code-highlight"><pre class="code-highlight-pre"><div data-line="1" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c">#!/bin/sh</span>
</div></div><div data-line="2" class="code-highlight-row numbered"><div class="code-highlight-line"><span class="c"># Copy all e-mail from source@gmail.com to target@yourdomain.com (Google Apps)</span>
</div></div><div data-line="3" class="code-highlight-row numbered"><div class="code-highlight-line">imapsync <span class="se">\</span>
</div></div><div data-line="4" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="nt">--host1</span> imap.gmail.com <span class="nt">--port1</span> 993 <span class="nt">--user1</span> <span class="nb">source</span>@gmail.com <span class="se">\</span>
</div></div><div data-line="5" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="nt">--passfile1</span> /path/to/gmail_mirror.passfile1 <span class="nt">--ssl1</span> <span class="se">\</span>
</div></div><div data-line="6" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="nt">--host2</span> imap.gmail.com <span class="nt">--port2</span> 993 <span class="nt">--user2</span> target@yourdomain.com <span class="se">\</span>
</div></div><div data-line="7" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="nt">--passfile2</span> /path/to/gmail_mirror.passfile2 <span class="nt">--ssl2</span> <span class="se">\</span>
</div></div><div data-line="8" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="nt">--useheader</span> <span class="s1">'Message-Id'</span> <span class="nt">--skipsize</span> <span class="se">\</span>
</div></div><div data-line="9" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="nt">--syncinternaldates</span> <span class="nt">--noauthmd5</span> <span class="nt">-nofoldersizes</span> <span class="se">\</span>
</div></div><div data-line="10" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="nt">--split1</span> 100 <span class="nt">--split2</span> 100 <span class="se">\</span>
</div></div><div data-line="11" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="nt">--maxage</span> 50 <span class="se">\</span>
</div></div><div data-line="12" class="code-highlight-row numbered"><div class="code-highlight-line">  <span class="nt">--regexmess</span> <span class="s1">'s/Delivered-To: source\@gmail.com/Delivered-To: target\@yourdomain.com/g'</span>
</div></div></pre></div></figure>

<p>The <code class="language-plaintext highlighter-rouge">--syncinternaldates</code>, <code class="language-plaintext highlighter-rouge">--useheader 'Message-Id'</code>,
and <code class="language-plaintext highlighter-rouge">--skipsize</code> options are all recommended by the imapsync FAQ
(search for "Gmail"):</p>

<p><a href="http://www.linux-france.org/prj/imapsync/FAQ">http://www.linux-france.org/prj/imapsync/FAQ</a></p>

<p>I opted to use the <code class="language-plaintext highlighter-rouge">--passfile1/passfile2</code> options rather than passing in a
plain-text password in via a command-line param for two reasons: first because
anyone with access to your system can use <code class="language-plaintext highlighter-rouge">ps</code> to view active processes and
hence would see your password plain-as-day; second because it just better
abstracts the script-logic from the password-text, and we can control the
file-permissions of those password files.</p>

<p>All your Gmail labels should sync-over <em>automagically</em>. Since this is going
Google-to-Google, because Google presents labels as separate IMAP folders in
their Gmail IMAP implementation, the process above should sync and preserve
all your Gmail labels for free.</p>

<p>It's been a two years since I've used this script, but I seem to remember it
working pretty painlessly. I wanted to share it here for anyone else who might
be looking to do this same thing.</p>

]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[What to Wear-a?]]></title>
    <link href="/blog/2009/12/28/what-to-wear-a/"/>
    <updated>2009-12-28T23:20:00-06:00</updated>
    <id>https://nynim.org/blog/2009/12/28/what-to-wear-a</id>
    <category term="Quick Notes" /><category term="Humor" /><category term="Video Games" />
      <content type="html"><![CDATA[<p><a href="http://kotaku.com/5430723/what-to-wear+a"><img src="https://nynim.org/images/2010/08/500x_marioscloset.jpg" alt="What-To-Wear-a?" /></a></p>

<p>(<em>via <a href="http://kotaku.com/5430723/what-to-wear+a">kotaku.com</a></em>)</p>
]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Autocomplete Me]]></title>
    <link href="/blog/2009/11/22/autocomplete-me/"/>
    <updated>2009-11-22T23:21:00-06:00</updated>
    <id>https://nynim.org/blog/2009/11/22/autocomplete-me</id>
    <category term="Quick Notes" /><category term="Humor" />
      <content type="html"><![CDATA[<p><a href="http://autocompleteme.com/"><img src="https://nynim.org/images/2010/08/autocompletemecom-howtoraise.jpg" alt="Autocomplete Me" /></a></p>

<p>(<em>via <a href="http://autocompleteme.com/">autocompleteme.com</a></em>)</p>

<p>Google auto-complete hilarity.</p>
]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[GeoDefense]]></title>
    <link href="/blog/2009/11/13/geodefense/"/>
    <updated>2009-11-13T19:57:00-06:00</updated>
    <id>https://nynim.org/blog/2009/11/13/geodefense</id>
    <category term="Quick Notes" /><category term="Video Games" />
      <content type="html"><![CDATA[<p><a href="http://www.appolicious.com/games/apps/24846-geodefense-lite-critical-thought-games"><img src="https://nynim.org/images/2010/08/geodefense_1250610916.jpg" alt="GeoDefense" /></a></p>

<p>(<em>via <a href="http://www.appolicious.com/games/apps/24846-geodefense-lite-critical-thought-games">appolicious.com</a></em>)</p>

<p>After hearing some rave-reviews from various places over the past few days,
I picked-up GeoDefense for the iPhone this evening. Wow, it’s a blast! Nice
balanced game-play, good amount of challenge, great graphics. Well worth
the $2.</p>
]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Nynim.org: New & Improved]]></title>
    <link href="/blog/2008/12/20/nynim-org-new-improved/"/>
    <updated>2008-12-20T12:04:51-06:00</updated>
    <id>https://nynim.org/blog/2008/12/20/nynim-org-new-improved</id>
    <category term="Blog" />
      <content type="html"><![CDATA[<p>I've resurrected my nearly abandoned website and breathed new life into it!
I'm now running <strong><a href="http://www.sweetcron.com/">SweetCron</a></strong>, an open-source
project for rolling you own personalized lifestream.</p>

<!-- more -->

<p>I find that I don't always have the inspiration or motivation to write blog
posts that often, but I do have a Web 2.0 foot-print that other people might
want to watch, whether it be sharing interesting news tidbits on
<a href="http://www.google.com/reader/shared/09011817463293213634">Google Reader</a> to
bookmarking useful webpages on <a href="http://delicious.com/tduckles">Del.icio.us</a>,
to what songs I love on <a href="http://www.last.fm/user/tonyduckles">Last.fm</a>.</p>

<p>Hence, the birth of this lifestream: all my various online activities
aggregated and presenting in a unified "lifestream". This gives others an easy
way to check-out what I'm up to. If you have any comments or feedback, drop me
an e-mail!</p>

]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Maintaining Online Identities]]></title>
    <link href="/blog/2007/09/11/maintaining-online-identities/"/>
    <updated>2007-09-11T20:47:44-05:00</updated>
    <id>https://nynim.org/blog/2007/09/11/maintaining-online-identities</id>
    <category term="Security" />
      <content type="html"><![CDATA[<p>I stumbled across this neat site called <a href="http://www.clipperz.com/">Clipperz</a> a
few weeks back. On the surface, it's "<em>a free and anonymous online password
manager</em>". This was immediately interesting to me because I'd really like to
find a (secure!) web-based password manager.</p>

<!-- more -->

<p>More and more lately, I've been
trying to find web apps that can replace the desktop applications I use on a
daily basis. It's just so incredibly convenient to have the portability to be
able to do everything you need from inside a web-browser, and more importantly
to have the persistent-state data stored on the server-side rather than on
each individual machine I use throughout the day. I'm already a huge fan of
webapps like <a href="http://del.icio.us/tduckles">del.icio.us</a> (<em>for tracking all my
bookmarks</em>) and <a href="http://www.google.com/reader/">Google Reader</a> (<em>for all my
RSS aggregation and reading needs</em>), and finding a web-based password manager
would be ideal. It really becomes a chore remembering what username or e-mail
address I registered with at various websites: Amazon, eBay, Newegg, Google,
Yahoo, credit cards, travel sites, online bill pay, banks, etc. (Oy!) It's
just a lot of identities to juggle. I've thought about using
<a href="http://keepass.info/">KeePass</a> or something, but that's a desktop app and I'd
have to keep my database in-sync between work and home.</p>

<p>What really interested me about Clipperz is their claim at being a "zero-
knowledge" web-application: they only store encrypted data on their server-
side, and use Javascript to decrypt the data client-side based on the username
and password which you supply. Your password is never sent to the server at
all; it's merely used to decrypt the data locally. That's a pretty slick idea,
IMO. But, I'm also a little nervous about trusting some third-party website
will all my personal sensitive online account information. So, I'm tempted to
sit down and write my own webapp to recreate that wheel, except that it's all
code I can vouch for and know that it's not secretly sending my data off to
some third-party site. You can already find open-source Javascript-based
implementations of the various crypto algorithms that I'd need to use. And it
seems like a simple AJAX-based webapp to save/load data from the server.</p>

<p>Also really intriguing is Clipperz' one-click-sign-in feature. They have a
Javascript bookmark which pulls HTML FORM information off a given login page
and then allows you to link the various FORM INPUT fields with the appropriate
data-fields on the "card" for that website (e.g. username, password). The one-
click sign-in then just has to do a HTTP POST to the desired web-address with
the correct FORM data to act just like the real login page. It's simple
enough, but that seems like the real time-saver here. Not only could you have
a webapp which stores all your personal data, but it also provides a quick
launchpad to login any site you need. That just takes it a step further and
makes the webapp almost a blackbox: you don't really <em>need</em> to know what your
username and password is anymore because the webapp database knows it and can
feed it to the target website's login form for you.</p>

<p>This just seems like a really awesome idea, and I'm really tempted to just use
Clipperz natively so that I don't have to re-invent the wheel, but I'm still
just nervous about using someone else's website as a database to store all my
personal sensitive data. It's basically just a trust issue, and I don't think
I have any good reason to trust that their Javascript code will never do
anything malicious. I'd much rather control the data myself on my private
webserver. If only Clipperz was a SourceForge project… ;)</p>

]]></content>
    </entry>
  
    <entry>
    
    
    
    
    
    
    <title type="html"><![CDATA[Simple Off-Site Backups...?]]></title>
    <link href="/blog/2007/09/11/simple-off-site-backups/"/>
    <updated>2007-09-11T20:23:38-05:00</updated>
    <id>https://nynim.org/blog/2007/09/11/simple-off-site-backups</id>
    <category term="Storage" />
      <content type="html"><![CDATA[<p>Lately I've been consumed with the idea of getting more serious about backing-
up all the data I have. I tend to be a digital pack-rat: my music collection,
digital photos I've taken, all the songs I've ever recorded, various tarballs
filled with code-stuffs for college projects I did, etc. There's a wealth of
nostalgia there, and I realized that I really wouldn't want to lose a lot of
that stuff.</p>

<!-- more -->

<p>I already use a quasi-RAID system: I have two identical hdd's in my file-
server box and I rsync the "master" drive to the "slave" drive every so often.
Not only does this provide me some amount of rollback-ness (i.e. because I
only rsync so often), but the decision to not RAID-mirror the drives was
intentional: if the filesystem or partition table on the RAID somehow became
corrupted, all my data could be lost.</p>

<p>But…what if my house burns down? (*gasp* Oh noes!!) Yes, that would be sad
indeed! So, rather than just providing a single layer of redundancy locally,
if I really want to invest in the survival of my important data, I really need
to spread that data around; I need to diversify.</p>

<p>I've looked around at various web-based back-up solutions like
<a href="http://aws.amazon.com/s3">Amazon's S3 service</a>, but those don't seem very optimal for me
because of the amount of data I want to backup (~150GB) and because it seems
like it would be a PITA to do a full restore over my home cable internet pipe.
Not to mention the monthly fees, paying someone else to store my data safe and
sound. But, dare I trust my important (and partially sensitive) data to a
stranger?</p>

<p>Currently, I'm tempted by a seemingly simple solution: just get an external
USB hdd, mirror my data once locally, and then throw it at a friend's house
and use rsync to keep it up-to-date. The main cost involved is the cost of the
new hdd; there's no monthly fee because I already need to pay for my internet-
access. And I can even return the favor by hosting drives on my end too. And
this could even be expanded to a multiple people, if you wanted to back-up
your data in multiple off-sites. This seems almost too easy to me, but it
seems perfectly effective. Anything, it's making use of the hidden geek-
factor: you're a geek and you have geek friends, so why not make the most of
it and use them for geeky endeavors like helping each other backup each
other's data? ;)</p>

<p>The main problem I have with this plan is that my data wouldn't be encrypted
at all. I'm not sure how paranoid I really need to be about my friends
snooping around my data. Though, I think this is basically the general idea
as: what would happen if someone stole your computer? So, that's really more
an argument that I should be locally encrypting my data so that even if prying
eyes were to get at my local/master copy, they still wouldn't be able to do
much with it.</p>

<p>Has anyone else given thought to getting more serious about backing up their
data?</p>

]]></content>
    </entry>
  
</feed>
