updating

how this site is kept in sync with upstream al-folio

How to pull in al-folio changes without breaking the blog. Companion to SETUP_LOCAL.md, which covers first-time setup.

The one thing to remember: since the v1 migration (Sep 2026) the theme is no longer in this repo. Layouts, includes, Sass, Liquid tags and feature JS all live in versioned al-* gems. So the normal update is a gem version bump, not a git merge.


1. The two update channels

What changed How it reaches you How often
Layouts, includes, Sass, JS, Liquid tags, feature behaviour Gem pin bump (§2) Most releases
CI workflows, _config.yml keys, docs, test scripts git merge upstream (§3) Occasionally
Example content (_posts, _pages, demo data) Ignore it — you replaced it Never wanted

For scale: the v1 merge touched 300 files, 178 of which were theme runtime. Those files no longer exist here, so that whole category of conflict is gone.

Current version: v1.2 (al_folio_core 1.0.15).


2. Gem updates — the normal case

bundle update on its own does nothing. The Gemfile pins every plugin to an exact version (gem 'al_folio_core', '= 1.0.15'), and Bundler honours the pin already written there. It exits 0 and changes nothing. You must edit the pins by hand first.

Step 1 — read the release notes. docs/releases/ holds the upstream notes; each one lists the new pins in a table and explains what changed. Newer releases may only exist on GitHub until the next starter sync: https://github.com/alshedivat/al-folio/releases

Step 2 — edit the pins. In Gemfile, under group :al_folio_plugins:

gem 'al_folio_core', '= 1.0.16'   # whatever the notes say

Step 3 — if a release adds a new plugin, edit _config.yml too. The Gemfile and the plugins: list are two separate lists that must agree. A gem in only one of them is inert — no error, no warning, the feature just never renders:

plugins:
  - al_new_plugin

Step 4 — install and verify:

bundle install
bundle exec al-folio upgrade audit
bundle exec al-folio upgrade overrides audit   # ← the important one, see §4
bundle list | grep al_folio_core               # confirm you got what you asked for

Step 5 — rebuild and look at it (§6).


3. Syncing the starter from upstream

Still just:

git fetch upstream
git merge upstream/main

Merge — not rebase. This fork has plenty of pushed history plus a gh-pages branch; rebasing would rewrite commits that are already published.

What resolves itself now

Three files are marked merge=ours in .gitattributes, because they are generated by our own workflows and upstream’s copies are never wanted:

File Written by
_data/citations.yml update-citations.yml
assets/img/star-history-light.svg star-history.yml (weekly)
assets/img/star-history-dark.svg star-history.yml (weekly)

⚠️ This needs a one-time local config that a clone does not carry:

git config merge.ours.driver true

Without it the rules silently fall back to a normal merge. No error — you just find yourself hand-resolving citations.yml again.

What to expect otherwise

Mostly .github/workflows/, new _config.yml keys, and docs/. When _config.yml conflicts, the rule of thumb is: take upstream’s structure, keep your values (your GA ID, your favicon, your verification token).


4. The silent failure mode that matters most

In v1, git cannot warn you when a gem update changes a file you shadow. The gem’s copy was never in this repo, so there is nothing to conflict with. You keep running your stale copy, silently.

This has already bitten this blog once: the MathJax scrollbar fix was reverted by an upstream merge and had to be redone months later in ea3cabf8.

al-folio upgrade overrides audit is what restores that signal. It compares each local override against the gem file it shadows and flags any whose upstream side moved since you last reviewed it. The reviewed checksums live in .al-folio-overrides.ymlcommit that file.

The four overrides this site carries

All shadow al_folio_core. Each is the gem’s own content plus only the change noted:

File Local change
_includes/figure.liquid Numbered captions (Figure N:) with inline Markdown
_sass/_components.scss width: auto so non-square social icons keep their aspect ratio
_sass/_utilities.scss .imgcenter (used in ~23 posts); display-math overflow-x off
assets/js/common.js ../../css/jupyter.css for nested notebooks; notebook height sync

When the audit reports one as stale after a gem bump:

bundle exec al-folio upgrade overrides diff _sass/_utilities.scss

Re-apply your change on top of the gem’s new version — do not just accept the old file, or you freeze whatever upstream fixed. Then:

bundle exec al-folio upgrade overrides accept _sass/_utilities.scss

If a fix would help everyone, port it to the gem upstream instead of carrying it here forever.


5. Two more things that fail without an error message

  1. Gemfile and _config.yml must agree. Covered in §2 step 3. Repo dirs use hyphens (al-folio-core); gem and plugin ids use underscores (al_folio_core).
  2. Features render only when the gem is loaded and its flag is on and the page opts in. Otherwise the Liquid tag emits an empty string. If a feature “does nothing”, check all three before debugging anything else.

6. Verifying after any update

bundle exec jekyll build      # ~90s; baseurl is blank, this is correct here
bundle exec jekyll serve      # http://localhost:4000/

A clean build proves very little — the failures that matter here build fine and just look wrong. Actually open:

  • the blog index — featured (“pinned”) posts should be a grid, not a stack
  • a post with a Jupyter notebook — no vertical scrollbar inside the iframe
  • a post with display maths — no horizontal scrollbar on equations
  • a post using {% include figure.liquid %} — captions still numbered
  • dark mode toggle

To compare against what is actually live, serve the last gh-pages deploy beside the new build and diff computed styles:

git archive $(git rev-parse gh-pages) | tar -x -C /tmp/oldsite
(cd /tmp/oldsite && python3 -m http.server 4124) &
(cd _site && python3 -m http.server 4123) &

npm run test:visual does this properly via Playwright, if snapshots are current.


7. Deployment

deploy.yml runs on every push to master, builds, runs purgecss, and force-pushes _site to gh-pages. Two things to know:

  • It is not gated on unit-tests.yml. A failing test suite does not stop a deploy.
  • If the Jekyll build fails, the deploy step never runs and the live site is untouched. That is the only safety net — and it does not catch a site that builds fine but renders wrong.

npm run lint:style-contract fails here by design: it forbids _sass/, _includes/ and _layouts/ in the upstream starter, but local overrides are explicitly legal in a site built from the template. See docs/ARCHITECTURE.md.


8. Toolchain

   
Ruby 3.3.5 (matches CI)
Bundler 4.0.6
Node 20 in CI

Ruby ≥ 3.2 is a hard floor. The prebuilt sass-embedded binaries and Bundler 4 both require it. On Ruby 3.1 Bundler falls back to the source gem, tries to compile it, and dies with NameError: uninitialized constant JSON::Fragment.

Building Ruby also needs libyaml-dev — without it psych fails to configure and the whole build aborts, which is fatal since psych is Jekyll’s YAML parser:

sudo apt-get install -y libyaml-dev libreadline-dev libgmp-dev libgdbm-dev libdb-dev uuid-dev

Fresh clone checklist

git clone --recurse-submodules git@github.com:MarkusThill/MarkusThill.github.io.git
cd MarkusThill.github.io
rbenv local 3.3.5
git config merge.ours.driver true     # ← easy to forget, fails silently (§3)
bundle install
npm ci

9. Open items

  • jQuery is no longer loaded site-wide. _includes/scripts/ (17 files) and assets/js/mdb.min.js are orphaned — nothing includes them. Inline $(...) in several posts is dead code that fails silently.
  • Pagination looks different since v1 — numbers now have a border. This is upstream’s native style; the old borderless look came from Material Design Bootstrap, which v1 dropped. Deliberate, left as shipped.