Skip to content

fix(plugin): merge case-variant tags into a single archive page#88

Merged
nguquen merged 1 commit into
botbie:masterfrom
botbeech:fix/tag-case-collision
Apr 21, 2026
Merged

fix(plugin): merge case-variant tags into a single archive page#88
nguquen merged 1 commit into
botbie:masterfrom
botbeech:fix/tag-case-collision

Conversation

@botbeech
Copy link
Copy Markdown
Contributor

Summary

  • _plugins/jekyll-paginate-tags.rb slugifies tag names via tag.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, ''), so distinct tag strings that share a slug (e.g. CTF + ctf, WebAssembly + webassembly) target the same /tag/<slug>/ destination.
  • The old generator iterated site.tags.keys and emitted one page per tag name, so two tag names that slugified identically produced two pages at the same path. Jekyll logged Conflict: The following destination is shared by multiple files and the second page silently overwrote the first — dropping the posts of the losing tag from the archive.
  • This PR groups tag names by slug before generation, unions + dedups their posts, and renders one page per slug.

Concrete example from the current post set

Three posts carry either ctf or CTF:

Post Tag(s)
_posts/2012-12-31-29c3-ctf-misc-300-funchive.md ctf
_posts/2014-07-01-binary-analysis-next-chapter-of-the-game.md ctf
_posts/2018-10-14--flareon2018-chal5-webassembly-writeup-side-channel-attack.md CTF

Before: /tag/ctf/ listed either two posts or one, depending on hash iteration order, and the build logged a conflict warning. After: /tag/ctf/ lists all three in reverse-chronological order.

Implementation notes

  • Grouping happens on site.site_payload['site']['tags'] (the same Liquid-drop array the old code passed to Pager.new), so pagination, templating and paginator.posts keep their existing shape.
  • Dedup by post['url'] protects against a post tagged with multiple case variants appearing twice on the merged page. Order is re-established via sort_by { |p| p['date'] }.reverse.
  • Display name (page.tag in the layout) picks the first all-lowercase variant among the grouped names, falling back to the first seen. This matches the URL slug visually and keeps the rendered page headers readable.
  • Empty slugs skipped: a tag whose characters are entirely stripped by the slug function would otherwise emit a page at /tag//. next if slug.empty? removes that edge case.
  • _plugins/jekyll-paginate-authors.rb is not affected — authors are keyed by unique authors.yml usernames, not by free-form tag strings, so the collision pattern doesn't arise there.

Validation

Local bundle exec jekyll build (Jekyll 4.4.1) with the fix:

  • Build completes in ~3s with no Conflict warnings.
  • 56 _site/tag/<slug>/ directories produced.
  • _site/tag/ctf/index.html contains links to all three CTF posts (verified with grep).
  • _site/tag/bitcoin/page/{2,3}/ pagination still works for high-volume tags.
  • All non-colliding tags (bitcoin, blockchain, cryptocurrency, etc.) render identically to before.

Relation to #87

This is the follow-up the Ruby/Jekyll upgrade PR (#87) flagged. Logically independent and applies cleanly on top of current master. Either order of review/merge works; the upgrade PR does not modify this plugin.

The tag slug function downcases and strips non-word characters, so
distinct tag strings like "CTF" / "ctf" or "WebAssembly" / "webassembly"
both target the same /tag/<slug>/ destination. Previous code generated
one page per tag name, which produced a Jekyll build-time
"destination is shared by multiple files" conflict and silently
dropped the posts of the losing tag from the archive.

Group tags by their slug before generating, union and dedup their
post lists, and render one paginated page per slug. Picks a canonical
display name (prefers the all-lowercase variant) so the `page.tag`
Liquid variable stays readable.

Verified against the current post set: the /tag/ctf/ archive now lists
all three posts (two tagged "ctf", one tagged "CTF") in reverse-
chronological order, with no build-time conflict warning.
@nguquen nguquen merged commit 1d1ce8c into botbie:master Apr 21, 2026
@botbeech botbeech deleted the fix/tag-case-collision branch April 21, 2026 00:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants