init
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
- Rock-solid fundamentals
- Git, builds, issues, and mailing lists.
- Mail-based workflows for nearly everything.
- Ultralight brutalist web interface.
- Fully functional with zero Javascript.
- CI/Build system
- Intuitive UUID-based secrets.
- Plenty of healthy servers: low build times.
- Streamlined configuration. We’ll get into it, but I really like how it leans into shell scripting.
- SSH access to build VMs.
- Easy static sites
- Fully integrated with the excellent CI.
- Zero-config TLS, including custom domains!
- Sourcehut costs money
- To my judgment this is a pro. I like my vendor relationships like I like my tap water: Ice cold, crystal clear, and growth-free. The knowledge that there’s no incentive to extort my cliency helps my sleep quality, which buffs my productivity, which pays for the service.
Limitations
- No third-party content in Pages
- Non-issue for me as I keep my life as text-based as physically possible, but with the 1GB size cap, this is a hard deal-breaker for multimedia creators.
- No user groups
- At time of writing, this feature is still in the proposal stage. It’s the only thing I’m seriously missing. Once Drew gets organizations off the ground he’s going to have a dangerously disruptive platform on his hands.
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.
- Create empty remote repository
- Create/configure dedicated SSH key
- Add public key to https://meta.sr.ht/keys
- Add private key to https://builds.sr.ht/secrets
- 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.