Skip to content

Latest commit

 

History

History
94 lines (78 loc) · 4.24 KB

File metadata and controls

94 lines (78 loc) · 4.24 KB

workman.tech

Ryan Workman's personal blog at https://workman.tech, served by GitHub Pages from the repo root. Posts are authored in markdown under _posts/<theme>/ and a small Node script (build.js) renders them to HTML alongside per-theme landing pages and regenerates the homepage post list.

How to add a post

  1. Create _posts/<theme>/YYYY-MM-DD-slug.md. The folder name is the theme (e.g. engineering), and it becomes part of the URL.

  2. Add frontmatter at the top of the file:

    ---
    title: rails perf wins from a 3-month audit
    date: 2026-06-01
    description: short blurb used for the meta description and OG tags
    tags: [ruby, rails, performance]
    ---

    title and date are required. description is optional but recommended. tags is optional and defaults to [].

  3. Run npm install once, then npm run build whenever you change a post.

  4. Review the diff (it will include both the source .md and the generated HTML) and commit everything. CI fails the PR if the committed HTML isn't a fresh build.

The published URL will be https://workman.tech/<theme>/YYYY-MM-DD-slug.html.

Architecture

  • _posts/<theme>/*.md — markdown sources. Theme = parent folder.
  • _posts/drafts/ — local, gitignored staging area for work-in-progress drafts. build.js skips it (see EXCLUDED_THEME_DIRS), so a draft sitting here never renders, never lands on the homepage, and never generates a public drafts/ directory. Move a draft into a real _posts/<theme>/ folder to publish it.
  • _template.html — per-post template. Carries the full <head> (viewport, favicons, OG/Twitter meta, canonical link) plus the article body wrapper.
  • _theme_template.html — per-theme landing page template.
  • build.js — single-file Node script. Walks _posts/, renders posts and theme landing pages, regenerates the post-list region inside index.html between <!-- posts:start --> and <!-- posts:end --> markers. Every file it writes is run through Prettier first, so the committed HTML is deterministically formatted.
  • <theme>/ — generated output directories (committed; GH Pages needs them). Each generated HTML file starts with a <!-- generated by build.js --> marker so npm run clean can safely identify and remove them.
  • index.html — hand-edited, but the region between the two marker comments is rewritten on every build.

Dependencies: markdown-it and gray-matter at runtime, plus dev tools prettier (which build.js runs over every rendered file so the generated HTML is consistently formatted) and html-validate (HTML linting, config in .htmlvalidate.json).

Commands

  • npm run dev — local preview at http://localhost:3000 (wraps python3 -m http.server 3000; needs Python 3 on PATH).
  • npm run build — render everything (each file formatted with Prettier).
  • npm run lint:html — run html-validate over index.html and the generated theme HTML.
  • npm run lint:fix — format source files with Prettier (prettier --write .; scope controlled by .prettierignore). html-validate has no autofix, so it is not part of this.
  • npm run clean — remove generated theme directories (only directories whose HTML files all start with the generated marker are touched) and reset the homepage post-list region to the empty-state placeholder.

Continuous integration

.github/workflows/build-check.yml runs on every pull request and on pushes to main. It runs npm ci && npm run build and fails if the working tree changed afterward (git status non-empty). That guards the one invariant that matters here: the committed HTML must be a fresh render of the source. It catches stale generated HTML, generated files that were never committed, and build.js errors (bad frontmatter or filenames make the build exit non-zero). If it fails, run npm run build locally and commit the result. The workflow then runs npm run lint:html (html-validate) over the output.

Follow-up ideas

  • Code-block syntax highlighting (markdown-it emits <pre><code class="language-foo"> already; needs a stylesheet or a highlighter pass).
  • Per-tag landing pages.
  • RSS feed.
  • sitemap.xml.
  • Per-post Open Graph images.
  • Formal tests for build.js (currently validated manually).