October 8, 2015

Using Hugo data files

As part of migrating to Hugo I discovered that I could use YAML (or TOML or JSON) files to store data in that could be used when building the site. This comes in quite handy and can be leveraged to build dynamic content easily.

I had some Ruby code in Nanoc that would generate a list of GitHub repositories on the site (which you can find here). Hugo doesn’t have any support for these things so I modified my script to generate YAML files in the data directory. Original repositories are stored in data/github/originals and forks I’ve got are stored in data/github/forks. Then using the Hugo data construct I can access them via the built-in template data paths $.Site.Data.github.forks and $.Site.Data.github.originals, as demonstrated below. This is a semi-manual process as you have to run the Ruby script before generating your site content; since my GitHub repository list doesn’t change all that often this isn’t a big deal but it would be easy enough to run this script as part of a script that does the full site generation before publishing the content.

The bits that make it work

The script

The Ruby code that generates the data lives in scripts/:

require 'net/https'
require 'json'
require 'yaml'
require 'fileutils'

USERID = "youruserid"
# Expects two lines: username, and password
AUTH_FILE = "/path/to/github/auth/textfile"
DATA_DIR = "/path/to/hugo/site/data/github"

api_url = "https://api.github.com/users/#{USERID}/repos?per_page=1000"
url = URI.parse(api_url)
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

req = Net::HTTP::Get.new(url.request_uri)
f = File.open(AUTH_FILE)
username = f.gets.chomp
password = f.gets.chomp
f.close
req.basic_auth(username, password)
response = http.request(req)
repositories = JSON.parse(response.body)

FileUtils.mkdir_p(DATA_DIR)
repositories.map do |repository|
  output_directory = "#{DATA_DIR}/#{repository['fork'] ? 'forks' : 'originals'}"
  FileUtils.mkdir_p(output_directory)
  filename = "#{output_directory}/#{repository['name']}.yaml"
  puts "Writing #{filename}"
  File.open(filename, 'w') do |f|
    f.puts repository.to_yaml
  end
end

The template for /code

This file resides at layouts/section/code.html:

{{ template "meta/header.html" }}

<h2>Code</h2>

<h3>Original Projects</h3>
<ul class="repositories">
{{ range $.Site.Data.github.originals }}
   {{ partial "repository.html" . }}
{{ end }}
</ul>


<h3>Forks of things</h3>
<ul class="repositories">
{{ range $.Site.Data.github.forks }}
   {{ partial "repository.html" . }}
{{ end }}
</ul>

{{ template "meta/footer.html" }}
The partial for a repository

This file resides at layouts/partials/repository.html:

<li>
  <a class='{{ if eq .fork true }}fork{{ else }}original{{ end }}'
     href="{{ .html_url }}">{{ .name }}</a>
  &mdash; {{ .description }}
</li>