init

#tech #site

Setting up this blog was lots of Fun™ so it seems appropriate to christen it with a process rundown. This is less a technical manifest than it is praise of some good tooling and a bit of encouragement to anyone who’s not yet taking advantage of modern conveniences to run their web presence. Basic familiarity with text-based systems is presumed.

Sourcehut Pages

Having tried before to get into site generation, I’ve repeated the mistake of cannon-balling the generator rabbit hole, becoming immediately overwhelmed by the odd limitations of simple systems and the titanic scope of richer ones, then shelving the project in disgrace. This time I managed to get up and running by working backwards from the deployment end with Sourcehut’s static publishing feature. I might write a separate post about how much I love Sourcehut; here I’ll just summarize the pros/cons most important to me.

Advantages

Limitations

External Configuration

You can find properly detailed instructions in the Quickstart, but I’ll briefly describe my steps to illustrate the size of the job.

  1. Create empty remote repository
  2. Create/configure dedicated SSH key
  3. Add public key to https://meta.sr.ht/keys
  4. Add private key to https://builds.sr.ht/secrets
  5. Set registrar @ IN A record to Sourcehut’s dedicated IP

That’s it! Everything else is in-repo.

Deployment

I’ll break down the bare minimum .build.yml file to be triggered on git push which we’ll extend later as we get into Hugo. If you’re interested, you can find my production build here, but bear in mind that I plan to make substantial modifications so it won’t reflect this post for long. At time of writing this site is built with the latest version of the Hugo static site generator available on Alpine Linux.

image: alpine/latest
packages: [hugo]

A grant string of the format service/scope:access generates a one-time Bearer token for the upload. Sourcehut services are separated by subdomain, enabling the principle of least privilege. The available services are Git, HG, Builds, Todo, Lists, Pages, Paste, and Meta. We’re using the Pages service, which applies the PROFILE, SITES, and PAGES scopes.

oauth: pages.sr.ht/PAGES:RW

The build script sees only the UUID of the private SSH key added in step 4 of the external configuration.

secrets:
- 06066c98-eb0e-4a2a-ac81-b4e1832512d9

The key is used for SSH access to the repository. This is overkill for a public repo, but it’s good practice, decent future-proofing, and super fly. Note that all submodules are automatically cloned recursively. This is relevant to Hugo sites because their project structure is ideal for using submodules to implement themes. It’s especially relevant to my site because I use a private submodule for content. More on that later.

sources:
- git@git.sr.ht:~vintnes/vintn.es

Finally, tasks is a list of shell scripts, each with their own login and $OAUTH2_TOKEN generated by our oauth grant string. acurl is a simple builtin cURL wrapper which applies the token to the --oauth2-bearer option.

tasks:
- build: |    
    cd vintn.es
    hugo --minify
    tar -cvz public |
      acurl -f https://pages.sr.ht/publish/vintn.es -F content=@-

Hugo

My interest (read: skill) in frontend web design is limited to my immediate web presence needs. I chose this generator for its fast rendering and extensible theme system: features which dramatically accelerate “development.” The downside is that Hugo’s feature set far exceeds my needs so attacking specific problems can get complicated by the depth of all the subsystems I won’t be getting into here. The purpose of this write-up is to encourage experimentation and this site will remain a work in progress for a while. I’ll cover my first impressions and mistakes, show off some toys, and revisit the topic later.

I started with the Etch theme by Lukas Joswiak. It provides a minimal foundation that’s extensible enough that I’ll probably keep hacking on it until I’m ready to build my own. If your goal is to get published right now and you don’t intend to spend one unnecessary second fretting about the unmitigated disaster area that is the web, getting started is trivial, but specific steps depend on your chosen theme. Have a pleasant scroll through the official themes, pick something you like the look of, and follow the instructions. If you’re anything like me, you’ll immediately feel the urge the start changing things you don’t understand.

Customizing Themes

Hugo themes are much more than wallpaper. They’re extensive, modular systems which can change every aspect of site generation. Crucially, the user can also customize every aspect of a theme without actually editing the downloaded files: a phenomenal development asset. Let’s imagine a skeletal project which has installed a theme called “foobar”. Directories irrelevant to this example are collapsed with a trailing forward slash.

─┬─config.toml
 ├─archetypes/
 ├─assets───css─┬─REDUNDANT.css  # modified from theme
 │              └─PROJECT.css
 ├─content/
 ├─layouts/
 └─themes───foobar─┬─theme.toml
                   ├─archetypes/
                   ├─assets───css─┬─REDUNDANT.css
                   │              └─THEME.css
                   └─layouts/

Notice that themes have the same directory structure as the parent project into which they are installed. Consider the CSS assets: PROJECT.css and THEME.css are both evaluated. Only the parent project’s copy of REDUNDANT.css is evaluated. To customize part of a theme, simply copy the relevant file into the parent project and modify it there. In this example, these assets are available to Hugo:

├ ···
├─assets───css─┬─PROJECT.css
├ ···          ├─REDUNDANT.css  # modified from theme
               └─THEME.css

Why all the theatrics? Of course you could just download a theme and edit it directly, but the best themes are actively maintained. This precedence model lets us separate the theme’s version control from our own. For example, I noticed that Lukas added internationalization support to Etch, which I immediately appropriated for nerd cred by setting all the date formats to ISO-8601, the only correct date format. I updated the theme, copied the new path i18n/en.yaml into the parent project, and made my changes to the new file. I’m able to track theme updates independently from my changes by using submodules.

Submodules and Private Content

Hugo themes are usually installed as Git Submodules. This is not a prosaic guide to submodules. Like the rest of Git, they’re designed to meet the simultaneously cooperative needs of thousands of people developers, but routine use requires only basic familiarity. In the simplest case, the special git file .gitmodules tracks the name, path, and URL of installed submodules. Git ignores individual files under these paths; only the current submodule revision is tracked.

cd $site_root

# theme installation
git submodule add "$theme_repo_url" themes/$theme_name
# submodule name, path, URL, and revision are committed
git commit .gitmodules themes

# (maintainer updates remote theme)

# local theme update
git submodule update themes/$theme_name
# only the submodule revision is committed
git commit themes

If you take a look at the source tree for this site, you can see that the content directory is also a submodule. Keeping that repository private lets me manage my drafts with Git without worrying about unfinished writing being public record or locking down the site code. The build system has access to the private repository via SSH key. I also use this strategy for the private (read: embarrassing) parts of my dotfiles.

This is slightly more complicated to work with. It is generally discouraged to modify a submodule “in-place” (installed in the parent). I do it all the time. If your use-case is any more complicated than mine (multiple submodule branches, many simultaneous editors, using submodules as package management (as they were intended)) then I can’t recommend it with a clean conscience, but for privacy demarcation in a small project it’s awesome.

The following commands presume the existence of a separate remote repository with the appropriate content structure.

cd $site_root
#[ -e content ] && rm -r content # remove any existing file/directory
git submodule add "$content_repo_url" content
git commit .gitmodules content

Never commit a “dirty” revision of a submodule. That’s when a submodule change is committed directly to its parent repository. All changes to the submodule must be committed to the child repository first. This creates a “clean” revision; the hash of that revision is then recorded by the parent.

cd $site_root
hugo new posts/pasta.md # creates /content/posts/pasta.md frontmatter
# note that your theme may employ a different content structure
git diff content # view dirty revision

cd content
echo 'Slap me silly! I stan some semolina spaghetti, son.' >> posts/pasta.md
git commit posts -m 'Commit content changes to submodule.'

cd $site_root
git diff content # view clean revision
git commit content -m 'Commit new submodule revision to parent repository.'

Or you could, you know, not publish your site code. The suggestion feels sacrilegious. I’ve benefited so much from free software that I feel compelled to participate wherever tolerable, but I also spend a lot of time thinking about how this stuff will be perceived. This mixture of my patented lazy perfectionism with the compulsion to share the organization of my thoughts is a recipe for wasted time. Then again,

As if you could kill time without injuring eternity.
──Henry David Thoreau

That’s all for now. I’m no longer sure who my target audience is and this content is pretty scattered for one post. I’ve made sporadic forays into ideas like responsive styles, ambient syntax highlighting, and advanced deployment. More on that later.