Migrating from Jekyll @ stwoo.net

Gohyde reads the same _config.yml, front matter, layouts, includes, and collections as Jekyll, so most sites build with little or no change. This guide covers the friction points that show up on real sites.

Steps

  1. Point Gohyde at the site and build:
    cd my-jekyll-site
    gohyde build
    
  2. Read the warnings. Missing assets, Sass fallbacks, and unknown tags all warn to stderr without failing the build. Work through them.
  3. Clean the output dir if you see stray files: rm -rf _site _builds.

What works unchanged

Common adjustments

url and baseurl

Asset URLs and page URLs include baseurl. Make sure absolute links prepend {{ site.url }} explicitly:

<link rel="canonical" href="{{ site.url }}{{ site.baseurl }}{{ page.url }}" />

If site.url is blank, links render relative — uncomment url: in _config.yml for absolute URLs.

Sass

Install Dart Sass (npm i -g sass or your package manager). The legacy Ruby sass gem is detected and skipped. Without a real compiler, SCSS passes through unprocessed with a warning.

jekyll-assets-style tags

{% asset %}, {% javascript %}, {% stylesheet %}, {% image %} and the @path/@url/@uri URL-only flags are supported. See Assets.

Plugins

Ruby/Python Jekyll plugins don’t run as-is — port them to Gohyde’s plugin SDK, in whichever of the three runtimes fits best (see Writing Plugins): Ruby or Python if you’re porting existing Ruby/Python logic largely as-is, Go if you want the fastest option (in-process, no subprocess/JSON-RPC per call) and don’t mind compiling a .so — the same hook/filter/tag/block-tag/converter/generator surface is identical across all three, so nothing is Go-only or Python/Ruby-only.

gohyde migrate --fix will attempt to auto-port Ruby Liquid filters (a plugin that reopens Jekyll::Filters, or passes a module to Liquid::Template.register_filter) into a <name>.gohyde.rb file next to the original — this is the one Jekyll plugin idiom mechanical enough to translate automatically. This is a best-effort convenience, not a guarantee — always review the generated file before trusting it in a real build; a method body copied verbatim from Jekyll may reference helpers or gems that don’t exist in Gohyde’s plugin process. Everything else (Jekyll::Generator subclasses, Jekyll::Hooks.register callbacks, custom Liquid::Tag classes, anything touching Jekyll internals) has no automatic equivalent and is reported, not guessed at — port those by hand using the idioms below.

Three Ruby idioms that differ:

jekyll-feed, jekyll-seo-tag, etc.

Gem-based tag plugins ({% feed_meta %}, {% seo %}) aren’t bundled. Inline the markup or port the plugin:

<link type="application/atom+xml" rel="alternate"
      href="{{ site.url }}{{ site.baseurl }}/feed.xml"
      title="{{ site.title }}" />

Custom Markdown converters (Jekyll::Converters::Markdown::*, e.g. kramdown_pygments.rb)

A widely-copied Jekyll snippet swaps the site’s default Markdown processor for kramdown+Pygments instead of kramdown+coderay/Rouge, selected via _config.yml’s markdown: key. Gohyde’s own markdown: key is parsed but ignored — Gohyde always renders through goldmark (see Configuration) — so there’s nothing to plug a custom converter class into at all. This isn’t a porting gap: delete the plugin file. What it’s usually for (real, per-token syntax highlighting instead of a bare unstyled <pre>) is already covered natively — see Liquid & Jekyll compatibility on Chroma. Dropping the Ruby file in as-is (rather than deleting it) won’t crash the build either way: it never calls the Gohyde SDK’s Gohyde.run, so the Ruby bridge just reports “plugin closed stdout” as a warning and moves on.

Custom “local include” tags that reimplement {% include %}

A common technique: copy Jekyll’s own lib/jekyll/tags/include.rb (IncludeContentTag/IncludeContentRelativeTag) and point it at a different directory (e.g. _localincludes/ instead of _includes/) via a renamed tag like {% localinclude %}. Don’t port this — Gohyde’s own {% include %} already supports subdirectories under _includes/: move the files into e.g. _includes/local/ and change your templates from {% localinclude thing.html %} to {% include local/thing.html %}. The underlying Jekyll::Site internals this kind of plugin depends on (includes_load_paths, liquid_renderer, regenerator, registers[: cached_partials]) have no Gohyde plugin-context equivalent anyway, so a manual port isn’t a realistic fallback even if you wanted one.

Verifying parity

Run the same site through both generators and diff the output:

jekyll build -d _jekyll_out
gohyde build -d _gohyde_out
diff -r _jekyll_out _gohyde_out

Whitespace and attribute ordering may differ; semantic differences are bugs — Jekyll’s output is the spec.