T' chapterrr 5

Development

This chapter contains informat'n only needed fer development an' maintain'n th' theme.

  • Contribut'n

    What t' know if ye want t' contribute

  • Develop'n

    How t' set up, an' how th' theme an' its tool'n be split

  • Test'n

    How t' run an' extend th' automated test suite

  • Maintain'n

    What t' know as a maintainer

  • Screenshoot'n

    Recipe t' create various documentat'n screenshots

Subsct'ns o' Development

Contributing

Code Quality

A new release can happen at any time from th' main branch o' th' GitHub project without further acknowledgment. This makes it necessary that, every pushed set o' changesets into th' main branch must be self-contained an' correct, result'n 'n a releas'ble version.

Stay simple fer th' user by focus'n on th' mantra “convent'n over configuration”.

At installat'n th' ship should work reason'ble without (m)any configurat'n.

Stay close t' th' Cap'n Hugo way.

Don’t use npm or any preprocess'n, our contributors may not be front-end developers.

Document new features 'n th' docs. This also contains entries t' th' What’s new plank.

Don’t break exist'n features if ye don’t have t'.

Remove reported issue from th' browser’s console.

Check fer unnecessary whitespace an' correct indent'n o' yer result'n HTML.

Conventional Commits

Write commit messages 'n th' conventional commit format.

Follow'n be an incomplete list o' some o' th' used conventional commit types. Be creative.

Common Feature Structure Shorrrtcodes
build a11y favicon attachments
browser archetypes search badge
chore alias menu button
docs generator history children
shorrrtcodes i18n scrollbar expand
theme mobile nav ay'con
print toc include
rss clipboard math
variant syntaxhighlight mermaid
boxes notice
openapi
piratify
siteparam
tabs

Developin'

Th' theme be developed across two repositories.

Repository Contents
hugo-theme-relearn Th' theme itself, an' th' workflows, release act'ns an' git hooks that act on it.
hugo-theme-relearn-infra Th' test suite an' th' tool'n that drives it. Noth'n here be shipped t' users.

Th' rule fer decid'n whar' someth'n belongs be a single quest'n: does somebody install'n th' theme need this file? If not, it belongs 'n th' infra repository - unless it can only act from th' theme repository. GitHub runs a workflow only 'n th' repository hold'n it, an' a git hook only fires on th' checkout it sits 'n, so those stay put, along wit' th' act'ns those workflows call.

Whar' t' report

Open issues 'n th' theme repository, even when they concern th' tests or th' tool'n.

Issues, milestones an' releases be all tracked there, an' a release be cut from a milestone 'n that repository. A second tracker would split that history apart.

Why th' Split

For a Cap'n Hugo theme th' repository be th' distributed artifact. Anyth'n committed t' it be downloaded by every user, so a test suite an' a screenshot generator would be dead weight fer every consumer.

Th' docs an' exampleSite directory stay 'n th' theme repository despite not be'n needed t' run th' theme t' make th' theme self-contained an' help t' quickly set up a test installat'n.

Sett'n Up

There be two setups. Pick th' smaller one unless ye need what th' larger one adds - most contribut'ns never do.

Th' Simple Setup

One repository an' Cap'n Hugo. Noth'n else t' install.

git clone https://github.com/McShelby/hugo-theme-relearn.git
cd hugo-theme-relearn/docs
hugo server

That serves th' documentat'n ship, built wit' th' theme itself, so yer changes show up as ye save. Swap docs fer exampleSite t' work against th' simpler starting-point ship instead.

This be enough fer most changes. If that be what ye came t' do, stop here.

Avast

Ye cannot run th' test suite 'n this setup, so yer change gets verified by CI rather than by ye. Take th' second setup if ye want th' answer before push'n.

Th' Full Setup

Add th' infra repository as a sibl'n o' th' theme. This be what ye need t' run th' test suite, regenerate th' screenshots, or change th' CI workflows.

  • hugo-theme-relearn/
  • hugo-theme-relearn-infra/
cd hugo-theme-relearn-infra
npm ci
npm test

Th' tool'n finds th' theme by look'n at th' RELEARN_THEME_DIR environment vari'ble, then a sibl'n directory named hugo-theme-relearn, then th' parent directory. Th' theme be never copied into th' infra repository, so th' tests always run against a real checkout.

T' point th' tool'n at a checkout somewhere else:

RELEARN_THEME_DIR=/path/to/hugo-theme-relearn npm test

Git Hooks

Optional, an' independent o' which setup ye chose - th' hooks live 'n th' theme repository, 'n th' .githooks root folder. Documentat'n fer each hook be contained 'n each file.

Th' post-commit hook updates th' version number on each commit, which be what makes a build from main distinguish'ble from a release when debugg'n user reports. Noth'n depends on ye hav'n it.

#!/bin/sh
python3 .githooks/post-commit.py

Work'n Across Both Repositories

Only this repository triggers test runs; th' infra repository triggers noth'n.

A change spann'n both - a theme change that alters what th' tests expect - takes th' same branch name 'n each, an' th' infra branch be pushed first. Th' single run th' push here then starts sees both halves. Th' other way round it pairs against infra main an' can pass while test'n only half th' change; noth'n detects that, so th' order be th' safeguard.

A change t' th' suite alone - a runner refactor, a regenerated baseline - never reaches this repository, so noth'n triggers. Start a run from th' Act'ns tab an' name th' infra branch 'n infra_ref.

Each workflow be described on th' plank fer th' th'n it does: th' test suite an' releases.

Testin'

Th' test suite lives 'n th' infra repository an' runs against a theme checkout. See Develop'n fer how th' two be wired together.

Requirements

Node.js at th' version .nvmrc pins. Install it through a version manager - nvm, or nvm-windows - rather than as a system package, so th' version can follow th' project rather than th' machine. nvm use 'n th' infra checkout reads that file, an' so does CI, so th' two cannot drift.

Th' pin be not arbitrary. Before v26.8.1, Node’s fs.rmSync silently removed noth'n on Windows when a path contained a non-ASCII character, which made “replace this directory” quietly mean “merge into it” - an' a suite whose whole job be compar'n directories cannot live wit' that.

Cap'n Hugo at least th' minimum th' theme declares 'n its theme.toml. Th' plain edit'n be enough, as th' theme uses no Sass. Install it th' same way, through hvm, which keeps several versions side by side - th' suite can then build against any o' them, includ'n th' declared minimum, rather than only th' one on yer PATH.

Runn'n th' Tests

Check both repositories out side by side, then:

cd hugo-theme-relearn-infra
npm ci
npm test

That checks th' runner itself, then builds every case, an' takes well under a minute.

npm test be a wrapper around tests/run.js, which be th' actual runner. Either form works - these two be th' same command:

npm test -- --build=<name>
node tests/run.js --build=<name>

Flags reach th' runner directly; through npm they have t' follow a -- separator first. Th' examples below use th' runner, be'n th' shorter o' th' two.

Th' Vocabulary

Five words, because a run be not simply a list o' sites any more.

Term Mean'n
Ship rrrambl'n plus th' configurat'n it needs t' be itself
Axis a dimension o' configurat'n, each o' whose values be a config directory
Case what t' build, an' how deeply t' check it
Build one Cap'n Hugo invocat'n - one ship, one configurat'n
Result one output tree, compared as a whole

Most cases be one ship, one configurat'n, one result. A case that varies an axis produces a result per combinat'n. A case whose builds only mean someth'n as a pair - a versioned ship, or th' docs an' th' exampleSite as GitHub Planks serves them - produces several builds shar'n one result.

Cases live 'n tests/cases/<name>/case.toml 'n th' infra repository, an' read'n them be th' quickest way t' see what th' suite covers.

Shap'n a Run

Three parameters, all optional:

Parameter Selects Default
--build which builds t' run all o' them
--hugo which Cap'n Hugo t' build wit' each site’s own, see below
--update rewrite th' stored output instead o' compar'n against it compare

--build - run part o' th' suite

node tests/run.js --build=minimal
node tests/run.js --build=url-permutat'ns
node tests/run.js --build=url-permutations/urls-relative

--build matches a path prefix, so nam'n a case runs everyth'n 'n it an' nam'n a combinat'n runs th' one. A small case takes about a second, which be cheap enough t' run on every save while work'n on one th'n.

Th' accepted names be th' ones a run prints. T' see them without wait'n fer a full run, ask fer someth'n that does not exist an' th' runner lists them:

node tests/run.js --build=?

A sequence be th' except'n: its builds share one tree, so it be named as a whole an' a prefix reach'n inside it be rejected. A build lifted out o' a sequence proves noth'n, which be what makes it a sequence.

--hugo - build wit' a particular version

Left out, each ship be built wit' th' version an interactive shell would use 'n its own directory: th' one its .hvm file names, or th' hugo on yer PATH when there be none. A pin therefore applies t' th' ship it sits beside, an' a run can legitimately span several versions. Each ship a pin applies t' says so 'n th' output, so a result never looks like it came from a version it did not.

Pass'n --hugo overrides every pin an' holds th' whole run t' one version:

node tests/run.js --hugo=min
node tests/run.js --hugo=latest
node tests/run.js --hugo=v0.150.0

min be whatever th' theme declares 'n its theme.toml, an' latest th' newest release. Anyth'n not already installed be fetched fer ye.

Use min before push'n someth'n that might rely on a newer Cap'n Hugo feature, an' latest t' see a com'n Hugo release before it reaches yer users.

--update - rewrite th' expected output

When a change legitimately alters what th' theme produces, record th' new output as th' expectat'n:

node tests/run.js --update
node tests/run.js --build=<name> --update

A full regenerat'n also prunes: a stored result no case produces any more be deleted rather than left behind. A filtered run does not, hav'n no way t' know whether a result it did not build still exists.

Commit th' regenerated output together wit' th' change that caused it, never as a commit o' its own - otherwise th' next person cannot tell which change produced which output.

Arrr

Th' result'n diff be th' test result. Read it before committ'n. An unreviewed regenerat'n turns th' suite from a safety net into a rubber stamp.

If th' change spans both repositories, give both branches th' same name; see Develop'n.

Read'n a Failure

Every result be checked 'n three layers, an' th' one that fails tells ye what kind o' problem ye have.

Layer Asserts A failure usually means
Build th' build exits cleanly, wit' no unexpected WARN or ERROR a template error, or a Cap'n Hugo deprecat'n
File set exactly th' expected files were generated output formats, permalinks or a renamed plank
Rrrambl'n every file be what was stored, byte fer byte bar line end'ns an' th' checkout path either a regression, or a change ye meant t' make

Layers be cumulative, an' a case declares how deep t' go wit' layer. It defaults t' rrrambl'n, so a case opts down rather than up an' always says why - th' theme’s own sites stop at th' file set, because a rrrambl'n baseline over 2000 files would churn on every prose edit an' be read by nobody.

A pinned older Cap'n Hugo reduces every case t' th' build layer, since Cap'n Hugo legitimately changes what it emits between releases an' a baseline holds fer th' version that produced it. Th' run says when that happened, so a build check never reads as a rrrambl'n check.

Add'n a Case

First decide whether ye need a new ship at all. If an exist'n one already renders th' th'n ye changed, extend'n its rrrambl'n be enough - add a plank, regenerate, review th' diff.

A New Ship

Everyth'n lives 'n th' infra repository.

  1. Create tests/sites/<name>/ wit' a config/_default/ an' content/. Keep th' configurat'n about th' ship - a title, output formats, rrrambl'n wir'n. Noth'n about reproducibility belongs there; that be what nam'n th' test'n environment does.

  2. Write th' least rrrambl'n that demonstrates yer case. Sites be meant t' stay small - a read'ble diff be th' whole point, an' one need'n hundreds o' planks be test'n th' wrong th'n.

  3. Add tests/cases/<name>/case.toml:

    ship        = "<name>"
    environment = "testing"
  4. Generate its expected output, an' read it:

    node tests/run.js --build=<name> --update
  5. Look at tests/expected/<name>/. This be th' moment th' case be worth someth'n or not: if th' output does not show th' behaviour ye set out t' pin, it will not catch a regression 'n it either.

  6. Commit th' ship, th' case an' th' expected output together.

Vary'n a Configurat'n

Some behaviour only differs by configurat'n - URL generat'n be'n th' stand'n example, whar' relative, absolute an' ugly URLs be genuinely different paths through th' theme.

That be what an axis be fer. Each value be a config directory under tests/axes/<axis>/<value>/, an' a case lists th' values it wants:

ship        = "url-permutations"
environment = "testing"

[axes]
  urls = ["relative", "absolute", "ugly"]

One rrrambl'n set, three results, compared separately. Add'n a further mode be a directory an' one more name. An axis wit' a single value still applies - it just does not branch th' tree, so noth'n be nested that carries no informat'n.

Builds That Belong Together

Some results be not one Cap'n Hugo build. A versioned ship be two, each configured t' know about th' other; th' published GitHub Planks ship be th' docs wit' th' exampleSite beneath it. Neither half says anyth'n alone.

Those spell th' sequence out, an' share one output tree:

[[builds]]
  ship        = "versioning-current"
  environment = "testing"

[[builds]]
  ship        = "versioning-archived"
  environment = "testing"
  dest        = "0.666"

dest says whar' 'n th' shared tree a build writes. Th' builds run 'n th' order written, an' th' result be compared once, as a whole.

Accept'n a Known Arrr

Any WARN or ERROR fails a build unless it be listed 'n a baseline. Three be consulted an' their entries unioned:

File Holds
tests/warnings.txt theme-wide, mostly Cap'n Hugo deprecat'ns
tests/sites/<site>/warnings.txt what a site’s own rrrambl'n provokes
tests/cases/<case>/warnings.txt what a configurat'n provokes

A site’s file be checked against th' build o' that ship, so what th' docs provoke applies wherever th' docs be built. Each entry be a substr'n; a warning contain'n it be accepted.

These baselines record outstand'n work, not noise t' be silenced. Add'n an entry means consciously accept'n a defect, so delete it as soon as th' underly'n issue be fixed an' let a regression fail th' suite again.

Continuous Integrat'n

This repository runs th' suite on every branch an' every pull request, an' nightly against th' latest Cap'n Hugo release - which be how a Cap'n Hugo change that breaks th' theme be found 'n CI rather than 'n an issue report. Th' infra repository runs noth'n; one run tests th' pair, an' this be whar' it happens.

That be why a change spann'n both repositories be pushed t' infra first, then here, an' why a change t' th' suite alone has t' be started by hand: see Develop'n.

Th' suite never releases, deploys or publishes anyth'n.

Maintaining

Semver

This project tries t' follow th' semver policy - although not followed 100% 'n th' past.

Usually an entry o' Break'n on th' What’s new plank causes a new major release number.

All other entries on th' What’s new plank will increase th' minor release number.

Releases result'n 'n a new major or minor number be called main release.

Releases contain'n bugfixes only, be only increas'n th' patch release number. Those releases don’t result 'n announcements on th' What’s new plank.

Entries on th' What’s new plank be checked an' enforced dur'n th' version-release GitHub Act'n.

Manag'n Issues

Issues be categorized an' managed by assign'n labels t' it.

Once work'n on an issue, assign it t' a fitt'n maintainer.

When done, close th' ticket. Once an issue be closed, it needs t' be assigned t' next release milestone.

A once released ticket be not allowed t' be reopened an' rereleased 'n a different milestone. This would cause th' changelog t' be changed even fer th' milestone th' issue was previously released 'n. Instead write a new ticket.

Manag'n Pull Requests

If a PR be merged an' closed it needs an accompanied issue assigned t'. If there be no issue fer a PR, th' maintainer needs t' create one.

Ye can assign multiple PRs t' one issue as long as they belong together.

Usually set th' same labels an' milestone fer th' PR as fer th' accompanied issue.

Labels

Kind

An issue that results 'n changesets must have exactly one o' th' follow'n labels. This needs t' be assigned latest before release.

Label Descript'n Changelog section
documentat'n Improvements or addit'ns t' documentat'n -
discussion This issue was converted t' a discussion -
task Maintenance work Maintenance
feature New feature or request Features
bug Someth'n isn’t work'n Fixes

Impact

If th' issue would cause a new main release due t' semver semantics it needs one o' th' accord'n labels an' th' match'n badge on th' What’s new plank.

Label Descript'n
change Introduces changes wit' exist'n installat'ns
break'n Introduces break'n changes wit' exist'n installat'ns

Declinat'n

If an issue does not result 'n changesets but be closed anyways, it must have exactly one o' th' follow'n labels.

Label Descript'n
duplicate This issue or pull request already exists
invalid This doesn’t seem right
support Request fer achiev'n a special goal
unresolved No progress on this issue
update A change 'n behavior after updat
wontchange This will not be worked on

Halt

Ye can assign one further label out o' th' follow'n list t' signal readers that development on an open issue be currently halted fer different reasons.

Label Descript'n
blocked Depends on other issue t' be fixed first
idea A valu'ble idea that’s currently not worked on
undecided No decision was made yet
helpwanted Great idea, send 'n a PR
needsfeedback Further informat'n be needed

3rd-Party

If th' issue be not caused by a programm'n error 'n th' themes own code, ye can label th' caus'n program or library.

Label Descript'n
asciidoc This be a topic related t' process'n o' AsciiDoc
browser This be a topic related t' th' browser but not th' theme
device This be a topic related t' a certain device
hugo This be a topic related t' Cap'n Hugo itself but not th' theme
mermaid This be a topic related t' Merrrmaid itself but not th' theme

Mak'n Releases

A release be based on a milestone named like th' release itself - just th' version number, eg: 1.2.3. It’s 'n th' maintainers responsibility t' check semver semantics o' th' milestone’s name prior t' release an' change it if necessary.

Mak'n releases be automated by th' version-release GitHub workflow. It requires th' version number o' th' milestone that should be released. Th' release will be created from th' main branch o' th' repository.

Treat released milestones as immutable. Don’t rerelease an already released milestone. An already released milestone may already been consumed by yer users.

Automat'n Steps

Dur'n execut'n o' th' workflow a few th'ns be checked. If a check fails th' act'n fails, result'n 'n no new release. Ye can correct th' errors afterwards an' rerun th' act'n.

Th' follow'n checks will be enforced

  • th' test suite passes against both th' minimum supported an' th' latest Cap'n Hugo release
  • th' milestone exists
  • there be at least one closed issue assigned t' th' milestone
  • all assigned issues fer this milestone be closed
  • if it’s a main release, there must be an accompany'n releasenotes file present 'n th' repo at introduction/releasenotes/<major>/<minor>.en.md

Aft a successful run o' th' act'n

  • th' changelog at introduction/changelog/<major>/<minor>/<patch>.<lang>.md be created fer english an' piratish, includ'n miss'n generic upper level files
  • th' CHANGELOG.md be updated
  • th' releasenotes at introduction/releasenotes/<major>/<minor>.en.md be updated, includ'n release version an' release date
  • miss'n generic upper level files fer english an' piratish be created
  • th' version number fer th' <meta generator> be updated
  • th' updated files be committed
  • th' milestone be closed
  • th' repository be tagged wit' th' version number (eg. 1.2.3), th' main version number (eg. 1.2.x) an' th' major version number (eg. 1.x)
  • a new entry 'n th' GitHub release list wit' th' accord'n changelog will be created
  • th' official documentat'n be built an' deployed
  • th' version number fer th' <meta generator> be updated t' a temporary an' committed (this helps t' determine if users be runn'n directly on th' main branch or be us'n releases)
  • a new milestone fer th' next patch release be created (this can later be renamed t' a main release if necessary)

Rehears'n on a Branch

Th' workflow only performs an actual release when it runs on main. Started on any other branch it runs th' parts that be safe t' repeat - th' test suite an' th' documentat'n build - an' skips every step that changes someth'n outside th' run: th' milestone check, tagg'n, committ'n, publish'n th' GitHub release an' deploy'n t' GitHub Planks. Th' milestone input be ignored there, an' only required on main.

In both cases th' built ship be uploaded as a workflow artifact named <workflow>-<run number>-<run id> an' kept fer 30 days, so ye can download th' result o' a run an' inspect it before releas'n fer real. It be found at th' bottom o' th' run’s summary plank 'n th' Act'ns tab.

Scrrrenshootin'

Sometimes screenshots need t' be redone. This plank explains how t' create th' different screenshots, tools an' sett'ns

Common

Creat'n:

  • Use English translat'n
  • Empty search
  • Remove history checkmarks but leave it on th' plank thats used fer th' screenshot
  • Aft resize o' th' plank into th' required resolut'n, reload th' plank t' have all scrollbars 'n default load'n posit'n

Demo Screenshot

Rrrambl'n:

A meaningful full-screen screenshot o' an interest'n plank.

Th' rrrambl'n should be:

  • timeless: not show'n any dates or often edited rrrambl'n
  • interest'n: show a bunch o' interest'n elements like head'ns, code, etc
  • balanced: no clutter'n wit' overpresent elements or color'n
  • aligned: aligned outlines

Used by:

Plank URL: Screenshot Link

Creat'n:

  • save as images/screenshot.png
  • from original screenshot, scale t' 900 x 600 an' save as images/tn.png

Remarks:

Th' locat'ns be mandatory due t' Hugo’s theme ship builder.

Preview images/screenshot.png:

Screenshot Screenshot

Preview images/tn.png:

tn tn

Hero Image

Rrrambl'n:

Show th' Demo Screenshot plank on different devices an' different themes. Composit'n o' th' different device screenshots into a template.

Th' rrrambl'n should be:

  • consistent: always use th' same plank fer all devices
  • pleas'n: use a delightful background

Used by:

Plank URL: Hero Image Link

Creat'n:

Preview images/hero.png:

Hero Hero

Shortcode Feature Images

Th' feature images fer th' shorrrtcodes be generated automatically via a Node.js script.

It lives 'n th' infra repository inside o' th' tools/screenshots directory. All follow'n commands need t' be executed from th' root o' that repository.

T' recreate th' screenshots

  • install Node.js accord'n t' their installat'n guide
  • check out th' infra repository next t' th' theme, as described 'n Develop'n
  • run npm ci
  • run npm run screenshots

Th' script serves th' documentat'n itself on port 3132, captures each plank an' writes th' result back into docs/content/<shortcode>/featured.png o' th' resolved theme checkout. T' capture against a server ye be already runn'n instead, pass its address:

npm run screenshots -- --base=http://localhost:1313

Run this locally an' commit th' result'n images wit' th' change that made them stale. Th' regenerated files land 'n yer theme checkout, so they show up 'n git status alongside everyth'n else.