2021
The MiniFlux Blogroll in Hugo on this site is dynamic, pulling in my RSS feeds from Miniflux. I use this partial below to render a list of public feeds by category. It’s important to change $allowed_categories here in the script, and to set miniflux.url and miniflux.apiKey in your site config. {{/* GetMinifluxFeeds Get a list of MiniFlux feeds by category @author @qcasey @context Page (.) @access public @example - Go Template {{ partialCached "func/GetMinifluxFeeds" . }} */}} {{ $mf_url := .Site.Params.miniflux.url }} {{ $mf_apiKey := .Site.Params.miniflux.apiKey }} {{ $allowed_categories := (slice "Digital Gardens and Blogs" "News" "Releases") }} {{ if (and $mf_url $mf_apiKey) }} {{ $categories := getJSON (printf "%s/v1/categories" $mf_url) (dict "X-Auth-Token" $mf_apiKey) }} {{ with $categories }} {{ $feeds := getJSON (printf "%s/v1/feeds" $mf_url) (dict "X-Auth-Token" $mf_apiKey) }} {{ if $feeds }} {{ range $categories }} {{ if in $allowed_categories .title }} {{ $c := . }} <h2 id="{{ $c.title | lower }}">{{ $c.title }}</h2> <ul class="blogroll"> {{ range $feeds }} {{ if eq .category.id $c.id }} <li> <a href="{{ .site_url }}"> {{ with .icon.feed_id }} {{ $icon := getJSON (printf "%s/v1/feeds/%.0f/icon" $mf_url .) (dict "X-Auth-Token" $mf_apiKey) }} {{ with $icon }} <img width="20" height="20" src="data:{{ .data }}" /> {{ end }} {{ end }} {{ .title }} </a> </li> {{ end }} {{ end }} </ul> {{ end }} {{ end }} {{ end }} {{ end }} {{ end }}
The static site generator Hugo doesn’t yet support Wikilinks. This is being considered on this github issue, but in the meantime we need to parse each page’s content to replace wikilinks with a relref internal page link. I use the following code snippet to support wikilinks in Hugo. It can be invoked when you’d normally use {{ .Content }} by calling this instead: {{- partial "content-with-wikilinks" . -}}. {{/* Prints page content with two types of wikilinks rendered (with and without text). Based loosely on https://github.com/milafrerichs/hugo-wikilinks with these improvements: - Renders shortcodes correctly - Handles Links with text - Uses safeHTML instead of markdownify (renders <code></code> blocks correctly) This is redundant once a solution is developed for https://github.com/gohugoio/hugo/issues/3606 @author @qcasey @context Type Page (.) @access public */}} {{ $wikiregexWithText := "\\[\\[([^\\]\\|\\r\\n]+?)\\|([^\\]\\|\\r\\n]+?)\\]\\]" }} {{ $wikiregex := "\\[\\[([^\\]\\|\\r\\n]+?)\\]\\]" }} {{ $page := .Page }} {{ $pageContent := .Content }} {{ range ($wikilinks := .Content | findRE $wikiregex) }} {{ $link := . | replaceRE $wikiregex "$1" }} {{ $wikilink := printf "\\[\\[%s\\]\\]" $link }} {{ with relref $page $link }} {{ $link := printf "%s%s%s%s%s" "<a href=\"" . "\">" ($.Site.GetPage $link).Title "</a>" }} {{ $pageContent = $pageContent | replaceRE $wikilink $link }} {{ end }} {{ end }} {{ range ($pageContent | findRE $wikiregexWithText) }} {{ $link := . | replaceRE $wikiregexWithText "$1" }} {{ $text := . | replaceRE $wikiregexWithText "$2" }} {{ $wikilink := printf "\\[\\[%s\\|%s\\]\\]" $link $text }} {{ with relref $page $link }} {{ $link := printf "%s%s%s%s%s" "<a href=\"" . "\">" $text "</a>" }} {{ $pageContent = $pageContent | replaceRE $wikilink $link }} {{ end }} {{ end }} {{ $pageContent | safeHTML }}
Public Notes Each note is private by default. I set the public: true flag in frontmatter to designate publishable content. This is different from the Dendron publish config because it can be placed anywhere in the hierarchy, and will still propagate to child notes. For example: Note Frontmatter Flag Is Published? garden true yes garden.quantified-self – yes garden.quantified-self.musicbrainz – yes garden.someprivatenote false no garden.someprivatenote.childnote – no I considered using cascading frontmatter, but Dendron exports any unknown frontmatter into a custom: heading.
As of Dec 2021, this is now outdated. I am no longer using Dendron, nor nomad to deploy the site. I’ll have a follow up shortly. This expands on the DroneCI -> Nomad pipeline I examined earlier I explained why I use Dendron, today I will show how. The glue that binds Dendron, Quantified Self reports, and Hugo is DroneCI. The CI build runs every night and immediately when a commit is made to the Hugo site. I wrote a custom Pod for exporting notes. It’s similar to the built-in markdown pod, but includes frontmatter and renders a hierarchical structure which makes Hugo happy. The server completes these build steps: Clone the required build repos from Github / Gitea: Hugo site workspace Hugo site theme Dendron workspace and notes Dendron <-> Hugo export pod Nomad config files Once the files are in order, we install dendron-cli and link the hugo-pod in an node container Apply the included patch that skips validation of the pod Run the export pod, creating a well formed /garden/ subdirectory in Hugo’s content folder Build the Hugo site Build the final Docker image with the new public directory Publish the final Docker image Send a redeploy command to Nomad using the config cloned in step 1. Prune old Docker images from our registry Done! Simple, no? 😆 The complexity stems from the sources being so scattered (Dendron, Hugo, Nomad…). Obviously this isn’t practical for casual note writing. But in my head, if it’s worth doing it’s also worth overdoing. This is the full .drone.yml spec: kind: pipeline type: docker name: default clone: git: image: plugins/git recursive: true submodule_override: "themes/novela": https://git.quinncasey.com/qcasey/hugo-theme-novela steps: - name: "Clone hugo theme" image: alpine/git commands: - sed -ir 's$:$/$' .gitmodules - sed -ir 's$git@ssh.$https://$g' .gitmodules - git submodule update --recursive --init - name: "Clone Dendron notes and export pod" image: alpine/git commands: - git clone https://git.quinncasey.com/qcasey/hugo-pod.git - git clone https://git.quinncasey.com/qcasey/notes-framework.git notes - cd notes - git clone https://git.quinncasey.com/qcasey/notes.git garden - name: "Export Dendron notes" image: node:current-alpine3.11 commands: - npm install -g @dendronhq/dendron-cli - cd hugo-podand - npm install # Skip validation error "data must NOT have additional properties" - patch node_modules/@dendronhq/pods-core/lib/utils.js < utils.js.patch - npm link - cd ../notes/ - npm link hugo-pod - dendron-cli exportPod --wsRoot ./ --podId hugo --podPkg hugo-pod --podSource custom --config "fname=dendron,vaultName=vault,dest=../content,includeStubs=true" - name: "Build site with hugo" image: klakegg/hugo:ext-alpine commands: - hugo - name: publish image: plugins/docker settings: registry: registry.quinncasey.com repo: registry.quinncasey.com/garden-quinncasey tags: - latest - ${DRONE_BUILD_NUMBER} username: casey password: from_secret: registry_password - name: clone deployment dotfiles image: alpine/git commands: - git clone -b abathur https://git.quinncasey.com/qcasey/dotfiles - name: redeploy nomad image: multani/nomad commands: - nomad run -address=http://192.168.1.196:4646 -var=image_id=${DRONE_BUILD_NUMBER} dotfiles/nomad/webserver/quinncasey.com.nomad - name: prune images image: anoxis/registry-cli:latest commands: - /registry.py -l casey:$PASSWORD -r https://registry.quinncasey.com -i garden-quinncasey --delete --num 10 --keep-tags "stable" "latest" environment: PASSWORD: from_secret: registry_password
Salient Startpage A startpage with light/dark mode, automatic favicons per site, and retained search bar focus. For the latter Chromium new tab focus / custom new tab page, you’ll want these chromium patches. A little advanced, but worth it! This is compatible with Homer, you can copy/paste your sites/urls/icons from there to config.yaml. The startpage itself can be rendered to an .html file with a directory of images. Run with Docker Clone, build, and run the container: git clone https://github.com/qcasey/SalientStartpage && cd SalientStartpage docker build -t salient-startpage . docker run -p 8080:80 --name salient-startpage salient-startpage Just want the HTML/CSS? Keep the container running like above, and copy the files out: docker cp $(docker ps -aqf "name=salient-startpage"):/usr/share/nginx/html ./public You should be able to point a browser to ./public Configuring Rebuild the container above after making changes to config.yml. The docker build step will fetch favicons to the bookmarks you define, and use Hugo to render the final version of your startpage.
Hugo 💓 Dendron I use Hugo to render a static site. This includes the theme, markdown parser, and structure. All of the content is separate from the site, and is built from Dendron notes. Quantified Self reports and assets are pushed to the notes from CI/CD. DroneCI pulls the Hugo structure and my content together and publishes a docker image. I explain these build steps more here. Why? I love Dendron. I love Hugo. I wish they were mildly more interoperable, but I’m rarely afraid of some DIY. Also, DroneCI happens to pair nicely with the gitea instance I already run in my Homelab. Some negatives to this complex approach include: At the moment, only my public notes have a frontend. Private notes don’t show up anywhere beyond VSCode. For now. I lose quick preview functions like Dendron: Preview or hugo serve -D. I rely entirely on VSCode to render my notes properly and have to do theme changes in a different workspace. I must follow Dendron’s updates and keep my export pod compliant. (Otherwise I run into unexpected errors) However, the pros heavily outweigh the cons. Keep stellar tools I get all the benefits of a static site generator, while continuing to use and incredible open source note taking app. Privacy These measures let me write all my learning, thoughts, and projects in a single place. I can then strike my own balance on privacy and keep from oversharing with public flags in the frontmatter. Quantified Self Part of the appeal of a dynamic build system is the mixing of data sources, journals, and reports in the quest for Quantified Self. This dynamic content is generated at build time or on a schedule. Ultimately the price I pay for complexity is just more complexity.