I first heard about the idea of a /colophon page from Slash Pages. I was not familiar with this concept, but apparently that is the term used to define the section of a printed work that describes the publisher and/or publishing process. We can of course adopt this to web pages, describing the development process and technologies involved.

I have had many iterations of this site over the years, but most of them have been based on GitHub Pages. This is an offering by GitHub where you get a free personal static website at a URL matching your username. It is incredibly easy to use and is a perfect fit for a simple purpose like a personal blog.

I originally started with artisanal, hand-written HTML. This was about as painful to maintain as you might guess. After a few years of that I migrated over to Hugo (I was going through a bit of a phase with Go). This served me reasonably well, but I was always a little uneasy with the seemingly perpetual v0 nature of the project. This did not directly impact me at first though, so I put those concerns to the side. But after a while I got a new laptop and when I was setting up my development environment I realized I was about 100 minor versions behind. When I finally upgraded Hugo it broke my site in a dozen tiny ways, and I decided it was time to revisit this project. I had been playing with the idea of my own static site generator for some time and this was the moment that pushed me over the edge.

I had heard of Pandoc before and was interested in trying it out but never had a compelling use-case. I thought this was an interesting idea: use Pandoc to hand-roll a static site generator. I always felt like Hugo had just a few too many bells and whistles for my needs. To complete this migration though I needed to nail down my core requirements:

After a bit of testing I landed on 3 scripts:

  1. build.ps1: Pulls together the .md files in the source repo and converts them to .html via Pandoc.
  2. serve.ps1: A simple helper script to run a local web server to test your changes as you go.
  3. deploy.ps1: Run a build and then copy over the latest .html and asset files from /public/ to a sibling GitHub Pages repo, then push that to master.
build.ps1
$root = $PSScriptRoot
$out = "$root/public"

$svgPath = "$root/assets/img/Hexaflake_Logo.svg"
$logoSvg = Get-Content -Raw -Path $svgPath
$navFile = "$root/nav.html"
$navHtml = @"
<header>
<a href='/' id='logo'$logoSvg</a>
<nav>
<a href='/'>Home</a>
<a href='/blog.html'>Blog</a>
<a href='/colophon.html'>Colophon</a>
<a href='/blogroll.html'>Blogroll</a>
</nav>
<button id='theme-toggle-button' onclick="toggleTheme()">Theme</button>
</header>
"@
$navHtml | Out-File $navFile -Encoding utf8

function Get-PostMeta($mdFile) {
    $json = pandoc $mdFile.FullName --template="$root/templates/metadata.json" -t html 2> $null # TeX rendering warnings will always emit here so ignoring
    $meta = $json | ConvertFrom-Json
    $meta | Add-Member -NotePropertyName BaseName -NotePropertyValue $mdFile.BaseName
    return $meta
}
function Convert-File($mdFile, $outFile) {
    pandoc $mdFile `
        --standalone `
        --template="$root/templates/page.html" `
        --include-before-body="$navFile" `
        --mathjax `
        -o $outFile
}

if (Test-Path $out) {
    Get-ChildItem $out -Recurse | Remove-Item -Recurse -Force
} else {
    New-Item $out -ItemType Directory | Out-Null
}

Copy-Item "$root/assets" "$out/assets" -Recurse

$pages = Get-ChildItem "$root/content/pages/*.md"


foreach ($p in $pages) {
    Convert-File $p.FullName "$out/$($p.BaseName).html"
}

$posts = Get-ChildItem "$root/content/posts/*.md"
$metas = $posts | ForEach-Object { Get-PostMeta $_ }
$metas = $metas | Where-Object { $_.draft -ne "true" } | Sort-Object { [datetime]$_.date } -Descending

foreach ($m in $metas) {
    $srcFile = Join-Path "$root/content/posts" "$($m.BaseName).md"
    Convert-File $srcFile "$out/posts/$($m.BaseName).html" $navFile
}

$links = $metas | ForEach-Object {
        $formattedDate = ([datetime]$_.date).ToString('yyyy-MM-dd')
        "<li><span><i><time datetime='$formattedDate'>$formattedDate</time></i></span><a href='posts/$($_.BaseName).html'>$($_.title)</a></li>"
    }
$blogMd = "# Posts`n`n<ul class='blog-posts'>$($links -join '')</ul>"
$blogMd | Out-File "$root/_blog_tmp.md" -Encoding utf8
Convert-File "$root/_blog_tmp.md" "$out/blog.html"
Remove-Item "$root/_blog_tmp.md"
Remove-Item $navFile
serve.ps1
$root = $PSScriptRoot

Write-Host "Running fresh build..."
& "$root/build.ps1"

# start server in a background job, rooted in public/
$serverJob = Start-Job -ScriptBlock {
    param($dir)
    Set-Location $dir
    # python -m http.server 1313
    # You may need the following on macOS:
    python3 -m http.server 1313
} -ArgumentList "$root/public"

Write-Host "Server running at http://localhost:1313 (job id $($serverJob.Id))"

try {
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = "$root/content"
    $watcher.IncludeSubdirectories = $true
    $watcher.Filter = "*.md"

    Write-Host "Watching for changes... Ctrl+C to stop."
    while ($true) {
        $result = $watcher.WaitForChanged([System.IO.WatcherChangeTypes]::All, 1000)
        if ($result.TimedOut) { continue }
        Write-Host "Change detected: $($result.Name) - rebuilding..."
        & "$root/build.ps1"
    }
}
finally {
    Stop-Job $serverJob
    Remove-Job $serverJob
}
deploy.ps1
param([string]$additionalMessage) 

"Deploying updates for GitHub pages site"
$currentDirectory = Get-Location
$today = Get-Date
$message = "Rebuilding site and publishing on $today"
if ($additionalMessage) {
    $message = $message + ": " + $additionalMessage
}

$root = $PSScriptRoot
Write-Host "Running fresh build..."
& "$root/build.ps1"

"Copying files from .\public into ..\nobleator.github.io"
if (-Not (Test-Path ..\nobleator.github.io)) {
    throw "..\nobleator.github.io must exist relative to this script!"
}

Copy-Item -Path .\public\* -Destination ..\nobleator.github.io -Recurse -Force

"Navigating to ..\nobleator.github.io"
Set-Location ..\nobleator.github.io
"Adding changes"
git add .
"Committing changes"
git commit -m "$message"
"Pushing changes to master"
git push origin master
"Navigating to ${currentDirectory}"
Set-Location $currentDirectory

"Deployment complete"

And there you have it, a very simple static site built with Pandoc!