mitchell vitez blog music art media dark mode

Switching from Snap

This site used to run on Snap, a choice I made quite a few years ago at this point. I think it was a pretty good choice at the time, since it let me tinker with some Haskell but mostly stayed out of the way (if I didn’t get into the parts of Snap that require understanding Lens).

However, I was starting to sense the age of that choice a little bit. There have been two issues posted to the issue tracker in the last three years.

While I don’t update the internals of my site often, one new feature I wanted to add—HTTP basic auth—turned out to be the starting point in ripping out the entire framework and starting fresh.

The one bit of guidance I found was eight years old: HTTP Basic Auth in Snap. It very nearly worked via copy and paste, except for one tiny part:

with auth

I didn’t know what with was, so I assumed maybe it was some kind of Lens? It wasn’t available via a quick Hoogle (neither was any of the other Snap stuff, since it’s not on Stackage—only Hackage). However, if we look it up, (as I did after the fact) that’s what we find.

with :: SnapletLens v v' -> m b v' a -> m b v a

However, I was getting compilation errors on auth still, wasn’t sure where it was coming from, and didn’t really feel like digging in to defining a SnapletLens.

This was the tiny straw that caused me to finally switch web frameworks.

Bye Bye Bash

In retrospect, the way I was building my old site was kind of horrendous. I would write everything in markdown, and render to HTML (technically, .tpl files), but the road in between was pretty wild.

I’ve recovered now, but I’d like to take a look back at a bunch of the things I was doing.

Most of the interesting stuff came from the build.sh file, a bit of bash cobbled together over many years, starting from when I knew hardly anything about programming, and even less about shell scripting.

CSS caching was done by putting the literal string VERSION buried somewhere in my top-level template file.

<link rel="stylesheet" href="/css/main.css?v=VERSION">

Then, I’d use sed to replace it with a random number generated at build time.

VERSION=$RANDOM
...
sed -i .bak "s|VERSION|${VERSION}|g" snaplets/heist/templates/$sect/$base.tpl

I tried to only recompile what was necessary using a --recent flag to the build script.

isrecent=()
if [[ $* == *--recent* ]]; then
  isrecent+=(-mtime -1)
fi

This would later be consumed by a find command, but meant that my blog would suddenly turn into a single post while I was writing it.

find $sect/*.md -type f "${isrecent[@]}" -print0 | xargs -0 ls -t | while read file

I would generate cards that linked to each blog post by reading in a custom comment format at the top of every markdown file. This would output a little line of interpolated HTML and append it to a file.

card() {
  file=$1
  [ -e "$file" ] || continue
  name=${file##*/}
  base=${name%.md}
  title=$(sed -n '2p' < $file)
  desc=$(sed -n '3p' < $file)
  date=$(sed -n '4p' < $file)
  sect2=""
  if [[ $sect != "blog" && $sect != "pages" ]]; then
    sect2=$sect
  fi
  if [[ "$base" != "404" ]]; then
    if [[ "$base" = "index" ]]; then
      base=""
    fi
    echo "<a class='card' id='$date' href='$sect2/$base'><h2>$title</h2><p>$desc</p></a>" >> snaplets/heist/templates/$sect/posts.tpl
  fi
}

Luckily, I had written a bit of Vimscript to autogenerate the desired format for me, and would call it with :Post in vim every time I started writing a new post. Problem solved!

" template for creating a new blog post
command Post call PostGen()
function! PostGen()
  call inputsave()
  let title = input('Title: ')
  let subtitle = input('Subtitle: ')
  call inputrestore()
  call append('.', '')
  call append('.', "# " . title)
  call append('.', '')
  call append('.', '-->')
  call append('.', strftime('%Y-%m-%d'))
  call append('.', subtitle)
  call append('.', title)
  call append('.', "<!---")
  execute 'normal dd Go'
endfunction

One of my favorite tidbits was that I made sure the format of the card html was ordered with the date first, so it was sortable by just sorting a file.

sort -r -o snaplets/heist/templates/posts.tpl snaplets/heist/templates/posts.tpl

On the less bad side, I had more-recently (maybe a couple years ago?) switched to writing Python to create links between consecutive posts and generate an RSS feed. Clearly, I had some troubles with Python package management though.

echo "injecting links"
python3 injectlinks.py
if [[ $? -ne 0 ]] ; then
  echo "link creation failed!!! maybe you need to pip install beautifulsoup4"
  exit 1
fi
echo "building rss feed"
python3 rss.py
if [[ $? -ne 0 ]] ; then
  echo "rss feed creation failed!"
  exit 1
fi

There was evidence of having changed my mind about how to build my Haskell code several times….

# to use ghc directly
# ghc Main
# kill $(lsof -t -i:5555)
# ./Main -p 5555 &
# sleep 2

# to use cabal instead
# cabal build
# kill $(lsof -t -i:5555)
# ./dist-newstyle/build/x86_64-osx/ghc-8.10.7/site-0.1.0.0/x/site/build/site/site -p 5555 &
# sleep 2

# to use nix instead
cabal2nix . > default.nix
nix-build release.nix
kill $(lsof -t -i:5555)
./result/bin/site -p 5555 &
sleep 2

And lastly, a delightful little touch: ringing the bell when all that nonsense was complete.

afplay /System/Library/Sounds/Glass.aiff

I’ve since obviated all of that Bash by switching to Yesod (and rewriting whatever was still necessary to handwrite, but in Haskell this time).

…well, except for one piece. I can’t help from indulging in the occasional bit of character soup. Here’s my latest and greatest script for autorefreshing the post I’m working on as I write it:

file="$1"
genfile=${file#markdown}
htmlfile=generated-html${genfile%.md}.html
echo "$file 👀"
when-changed -1 -s $file "echo \".\\c\"; pandoc --mathjax $file -o $htmlfile; osascript -e 'tell application \"Google Chrome\" to reload (tabs of every window whose URL contains \"localhost:3000\")';"

I really like how this ranges from the impenetrable \".\\c\"; to the pseudoenglish of AppleScript’s tabs of every window whose URL contains "localhost:3000".

Plus it’s got emoji!