logo

drewdevault.com

[mirror] blog and personal website of Drew DeVault git clone https://hacktivis.me/git/mirror/drewdevault.com.git

Gemini-and-Hugo.gmi (4597B)


  1. This is my first Gemini-exclusive blog post. Enjoy!
  2. My blog on the WWW is managed by Hugo, a static site generator written in Go.
  3. => https://drewdevault.com My home page on the WWW
  4. => https://gohugo.io Hugo
  5. I want to have something similar set up to allow me to more easily share content between my WWW site and my Gemini site, and so today I set out to teach Hugo about Gemini. At first I expected to be patching Hugo, but I was able to get something with a reasonable level of workitude with the OOTB tools for custom output formats.
  6. => https://gohugo.io/templates/output-formats/ Hugo's custom output formats
  7. I had these goals from the outset:
  8. 1. I wanted to opt-in to mixing content between the Gemini site and the WWW site. Not all WWW content is appropriate to Gemini.
  9. 2. By no means was I going to attempt an automated translation of Markdown (the original source for my WWW articles) to Gemini. The Gemini experience should be first-class, so a manual translation was called for.
  10. 3. Some means of having Gemini-exclusive content is desirable. Not just blog posts like this, but also pages like information about my Gemini software.
  11. => /gmni.gmi gmni: a gemini client
  12. => /gmnisrv.gmi gmnisrv: a gemini server
  13. In order to accomplish these goals, I needed to set aside some kind of Gemini-specific output directory for Hugo, convince it to read Gemtext alternate versions of my pages, and figure out how to designate some pages as Gemini-only. Turns out Hugo already supports custom output formats, which can have their own templates and needn't be HTML. The relevant config.toml additions, for me, were:
  14. ```
  15. [mediaTypes]
  16. [mediaTypes."text/gemini"]
  17. suffixes = ["gmi"]
  18. [outputFormats]
  19. [outputFormats.Gemini]
  20. name = "GEMTEXT"
  21. isPlainText = true
  22. isHTML = false
  23. mediaType = "text/gemini"
  24. protocol = "gemini://"
  25. permalinkable = true
  26. path = "gemini/"
  27. ```
  28. This also accomplishes another goal: by adding `path = "gemini/"`, I can cordon the Gemini content off into a subdirectory, and avoid polluting the Gemini site with WWW content or vice versa.
  29. However, after a few minutes trying to figure out how this worked, it dawned upon me that Hugo does not support custom *input* formats as well. This made goal #2 a challenge. Ultimately I came up with the following hack for layouts/blog/single.gmi:
  30. ```
  31. # {{$.Title}}
  32. {{ trim (readFile (replace $.File.Path ".md" ".gmi")) "\n" | safeHTML }}
  33. (further templating code trimmed)
  34. ```
  35. This just swaps .md for .gmi in the file extension of the input file, then reads it and runs it through safeHTML to get rid of the typical HTML garbage (e.g. &). Gemtext is whitespace-sensitive, so I also trim off any leading or trailing newlines so that I can make it flow more nicely into the templated content.
  36. In order to write a Gemini version of an article, I add `outputs: [html, gemtext]` to the frontmatter of the WWW version, then write a gemtext version to the same file path s/.html/.gmi/. Easy!
  37. I was also able to write a layout for the Gemini index page which enumerates all of the articles with Gemini versions:
  38. ```
  39. ## Blog posts
  40. {{ range (where .Site.RegularPages "Section" "blog") }}
  41. {{- if .OutputFormats.Get "gemtext" }}
  42. => {{replace .Permalink "/gemini" "" 1}} {{.Date.Format "January 2, 2006"}}: {{.Title}}{{ end }}{{ end }}
  43. ```
  44. Gemini's sensitivity to whitespace is again the reason why this is a bit ugly. A similar change to the WWW index page omits articles which have no HTML version. Also note the replacing of "/gemini" with "" in the permalinks - this was necessary to un-do the path = "gemini/" from config.toml so that once the gemini subdirectory was rehomed as the root of a Gemini site, the links lined up right.
  45. I also wanted to generate a Gemini-specific RSS feed. I updated config.toml with another custom format:
  46. ```
  47. [outputFormats.GEMRSS]
  48. name = "GEMRSS"
  49. isHTML = false
  50. mediaType = "application/rss+xml"
  51. protocol = "gemini://"
  52. path = "gemini/"
  53. ```
  54. Then I updated the default output formats for "section"-class pages, i.e. blog posts.
  55. ```
  56. [outputs]
  57. section = ["HTML", "RSS", "GEMRSS"]
  58. ```
  59. layouts/_default/section.gemrss.xml renders the feed, but I'll let you read that on your own time rather than paste that mess into this article. An oddity that I decided not to care about is that the rendered feed is *not* output to the gemini directory - I'll just update my build script to move it to the right location after Hugo finishes its work.
  60. And that's it! A few minor tweaks & updates to my deploy script and this is ready to ship. Tada! Thanks for having me here in Geminispace - I'm enjoying my stay.