X-Git-Url: https://www.kengrimes.com/gitweb/?p=kengrimes.com%2Fcontent.git;a=blobdiff_plain;f=content%2Fox-hugo-tutorial.md;fp=content%2Fox-hugo-tutorial.md;h=464212a26473f5f8936166862c9491b473e4679d;hp=0000000000000000000000000000000000000000;hb=137a062b4047f3eb304f4e397eb385a5f983c16f;hpb=6cb0fef53857e8b87402e13595faa8726bf95d5b diff --git a/content/ox-hugo-tutorial.md b/content/ox-hugo-tutorial.md new file mode 100644 index 0000000..464212a --- /dev/null +++ b/content/ox-hugo-tutorial.md @@ -0,0 +1,656 @@ +--- +title: "Using ox-hugo To Build Websites with Emacs" +author: ["Ken Grimes"] +date: 2018-04-11T21:56:00-07:00 +tags: ["org", "emacs", "hugo"] +categories: ["tutorial"] +draft: false +caption: "Exporting to Hugo's Blackfriday Markdown from Orgmode" +header: "/img/org.png" +--- + +This article explains in detail the process of setting up a bare-bones website +using Hugo and org-mode. My goal in writing this is to provide readers with a +superior understanding of the fundamentals of this workflow. It is by no means +an exhaustive explanation of org-mode or Emacs, but should give readers of any +skill level a strong foundation to apply their own knowledge and techniques to +the Emacs-Hugo toolchain. + +I assume only beginner-level knowledge of Emacs. + + +# Intro & Setup {#intro-and-setup} + +[Kaushal Modi](https://github.com/kaushalmodi) made ox-hugo by extending org's ox-blackfriday package, providing +an impressive amount of features for organizing blog text and linked data with +Hugo. He maintains [great documentation](https://ox-hugo.scripter.co/) and ample [examples](https://github.com/kaushalmodi/ox-hugo/tree/master/test/site/content-org) for using the +package. I will explain my own workflow here, but for an exhaustive (though +terse) reference, I highly recommend Modi's [test site](https://ox-hugo.scripter.co/test/) and [post source](https://raw.githubusercontent.com/kaushalmodi/ox-hugo/master/test/site/content-org/all-posts.org) org file, +which contain demonstrations and tests for all of ox-hugo's features. + +After issuing the Emacs command `M-x package-install RET ox-hugo RET`, you'll +need to `require` it. You can do this by running `M-: (require 'ox-hugo)`, but +you'll want to add it to your configuration as explained [here](https://ox-hugo.scripter.co/doc/usage/). Once this is +done, using ox-hugo is just a matter of making an org file and writing +content. Org's format is very straightforward, and is designed to make sense to +the reader even if they're unfamiliar with the formal syntax. For instance, + +```org +* My food +| Where's My Food? | Fridge | Counter | Mouth | Total | +| Oranges | 1 | 3 | 0 | :=vsum($2..$4) | +| Marshmallows | 0 | 100 | 20 | :=vsum($2..$4) | +| Brussel Sprouts | 32 | 4 | 0 | :=vsum($2..$4) | +``` + +Produces a dynamic spreadsheet table in org-mode that exports to HTML like this: + + +## My food {#my-food} + +| Where's My Food? | Fridge | Counter | Mouth | Total | +|------------------|--------|---------|-------|-------| +| Oranges | 1 | 3 | 0 | 4 | +| Marshmallows | 0 | 100 | 20 | 120 | +| Brussel Sprouts | 32 | 4 | 0 | 36 | + +If you're already familiar with org-mode, the benefits are obvious and creating +content is fairly trivial. Org-mode is, however, a complex and expansive program +with many features, and its learning curve can appear daunting at first glance. +Using ox-hugo is a great way to learn the format, since it gives the author a +command-center view of their entire content hierarchy, much like a traditional +database, but in a flat format that's much easier to read and understand. Org +features present themselves naturally, and the author can easily visualize the +correspondence between the org format and the output on their webpage. + +Just take a look at the [org file](https://www.kengrimes.com/gitweb/?p=kengrimes.com/content.git;a=blob_plain;f=content.org;hb=HEAD) for this webpage. Search for "ox-hugo is super +cool!" and you should find this very paragraph. + +Eventually you'll want to [read the manual](https://orgmode.org/manual/), though. You may access it in Emacs +with `M-x org-info`. + + +# Making a New Blog {#making-a-new-blog} + +Compared to a generic org file, the only important "extra" data that ox-hugo +needs to properly export data is a `:PROPERTIES: ... :END:` block with +definitions used for Hugo's [front matter](https://gohugo.io/content-management/front-matter/) (used for associating a title, header, +or other custom data with the page it generates). `:PROPERTIES:` blocks are +common in org for defining arbitrary metadata about sections, and can be used in +many such ways. Providing an `:EXPORT_FILE_NAME:` definition signals to ox-hugo +that this heading is available for export, and that it should be exported to a +markdown file with the name provided. For example, the `:PROPERTIES:` block of +the page you're currently reading looks like this: + +```org +:PROPERTIES: +:EXPORT_FILE_NAME: ox-hugo +:EXPORT_HUGO_CUSTOM_FRONT_MATTER: :caption "Exporting to Hugo's Blackfriday Markdown from Orgmode" +:EXPORT_HUGO_CUSTOM_FRONT_MATTER+: :header /img/org.png +:END: +``` + +The `:caption` and `:header` variables are optional definitions allowed by the +Speedy theme of this website, but the filename is the only required property for +ox-hugo. So, as a minimal example, here's what a new blog might look like in its +entirety: + +{{< highlight org "linenos=table, linenostart=1" >}} +#+hugo_base_dir: . +* Home +:PROPERTIES: +:EXPORT_HUGO_SECTION: +:EXPORT_FILE_NAME: _index +:END: +This is the home of my blog! +** I have herpes +:PROPERTIES: +:EXPORT_FILE_NAME: herpes +:END: +Someone gave me herpes! Oh no! +{{< /highlight >}} + +The org file can be placed in any directory so long as `HUGO_BASE_DIR` correctly +identifies the Hugo project's root directory. This path definition is required +for any valid ox-hugo file, and in the example above uses `.` as the base +directory, which assumes that the file will be placed in the hugo project's base +directory. If you saved this file as hugotest.org, exported it with org's +exporter `C-c C-e` and selected the Hugo output `H` and the All Subtrees To +Files option `A`, you'd wind up with the following files in your directory: + +```nil +. +├── content +│   ├── _index.md +│   └── herpes.md +└── hugotest.org +``` + +Most sites will be more than a blog, though, and will want multiple sections. In +fact, many sites are made up of nothing but a slew of sections that users +navigate between with some built-in menu. So a more functional minimal example +would be the following: + +{{< highlight org "linenos=table, linenostart=1" >}} +#+hugo_base_dir: . +* My Homepage +:PROPERTIES: +:EXPORT_HUGO_SECTION: +:EXPORT_FILE_NAME: _index +:EXPORT_HUGO_MENU: :menu "main" +:END: +This is the home of my blog! +* My Blog +:PROPERTIES: +:EXPORT_HUGO_SECTION: posts +:END: +** My Blog Homepage +:PROPERTIES: +:EXPORT_HUGO_MENU: :menu "main" +:EXPORT_FILE_NAME: _index +:END: +Man, look at all my blog posts. +** I have herpes +:PROPERTIES: +:EXPORT_FILE_NAME: herpes +:END: +Someone gave me herpes! Oh no! +{{< /highlight >}} + +Which yields the following: + +```nil +. +├── content +│   ├── _index.md +│   └── posts +│   ├── herpes.md +│   └── _index.md +└── hugotest.org +``` + +As you might expect, this structure adheres to the Hugo [content management](https://gohugo.io/content-management/organization/) +scheme. Additionally, the index files have been marked with menu metadata, which +allows Hugo themes to automatically generate navigation menus from the markdown +files. Hereafter, making new blog posts is as simple as adding new sub-headings +under the "My Blog" heading, and exporting. As you can see, this is suitable for +defining the hierarchical structure of any general website, not just +blogs. Org-mode and Hugo just make creating new pages so simple and +well-structured that providing content is all that's required for a new page, +blog entry, or entirely new site section. If you can blog with ox-hugo, you can +deftly deploy any manner of web content, or even develop entire websites as +naturally as you make blog posts. Any tool that can turn blogging and web +development into the same task is quite an achievement! + +Of course, themes to style this content are another can of worms entirely, but +it is sufficient for now to mention that Hugo makes [using themes](https://gohugo.io/themes/installing-and-using-themes/) as easy as +downloading one and specifying it in Hugo's config file. + +One question you may ask is why the blog's homepage is not defined in the **My +Blog** heading. This is a fair question! Property blocks are inherited by +sub-headings, and as of the current version of ox-hugo even `:EXPORT_HUGO_MENU:` +properties are inherited. This might be intended by the content creator, but +most likely you don't want every single post you make to be in the main menu. So +it makes sense to define all your pages, including the index, as a sub-heading +of the section definition (which specifies which sub-directory content will +output to). + +To illustrate, let's assume you want to extend the previous site definition with +a section about fishsticks. + +{{< highlight org "linenos=table, linenostart=24" >}} +* Fishsticks +:PROPERTIES: +:EXPORT_HUGO_MENU: :menu "main" +:EXPORT_HUGO_SECTION: fishsticks +:EXPORT_FILE_NAME: _index +:END: +This section devoted to Orson Wells, R.I.P. +** Van De Camps +:PROPERTIES: +:EXPORT_FILE_NAME: van-de-camps +:END: +If this is fish, I'll be a monkey's uncle. +** Gortons +:PROPERTIES: +:EXPORT_FILE_NAME: gortons +:END: +I think these gave me the herpes. +{{< /highlight >}} + +In this example, we've defined the main homepage of the section inside the +tier-1 heading for Fishsticks. This is valid, and produces the expected file output: + +```nil +. +├── content +│   ├── fishsticks +│   │   ├── gortons.md +│   │   ├── _index.md +│   │   └── van-de-camps.md +│   ├── _index.md +│   └── posts +│   ├── herpes.md +│   └── _index.md +└── hugotest.org +``` + +But on inspection of the gortons.md file, we find the anomoly mentioned above: + +{{< highlight markdown "linenos=table, linenostart=1" >}} +--- +title: "Gortons" +author: ["Ken Grimes"] +draft: false +menu: + main: + weight: 2002 + identifier: "gortons" +--- + +I think these gave me the herpes. +{{< /highlight >}} + +Uh oh! Not only did these fishsticks give us herpes, they are now part of the +main menu. Tisk tisk. So if you use this workflow, be sure to put your index +pages in subheadings so that the tier-1 heading can be used for "global" +definitions that affect all of the pages. + +Another question might be why the index pages are named **\_index**. You can use +**index** instead of **\_index**, the only difference is whether Hugo treats the +index page as a leaf, or a branch, when [bundling resources](https://gohugo.io/content-management/page-bundles/) for Hugo to query +during site generation. This is a relatively new addition to Hugo as of version +0.39, and isn't fully functional or integrated well into ox-hugo, so I simply +don't use it at the moment. I define all indexes as **\_index** to make them +branches because, in future versions, packaging files within bundles like this +will provide a more stable way for Hugo themes to reference page- and +section-specific files that accompany the content. Currently, I store all such +files in the static folder, which is copied verbatim to the output directory by +Hugo when the site is built. + + +# Hugo Setup {#hugo-setup} + +At this point, setting up Hugo and publishing is simple. [Installing](https://gohugo.io/getting-started/installing/) Hugo is +pretty straightforward on any Unix-like system with a package manager; it is +available on most distributions at this point. Windows installation is a bigger +pain in the ass, but you should be used to that if you're still in the +stone-age. + +Using `hugo new site .` on the command-line will create a new hugo site in the +current directory, but `hugo` expects to be creating a new directory with this +command and will complain if it already exists. It also provides the `--force` +option to allow creating a new site in an extant directory, but this too will +fail if the **content** subdirectory already exists (which ox-hugo will create +when you export). + +So you have three choices: + +1. run `hugo new site /path/to/some-new-dir` and move your org file to it +2. simply `rm -Rf content/` to remove the content directory ox-hugo created, + then run `hugo new site --force .` +3. don't even bother with the `hugo new site` command, and make a **config.toml** + file manually. + +It's convenient to do this through the `hugo` command because it will create +Hugo-specific subdirectories like archetypes, layouts, themes, etcetera, in +addition to populating a basic **config.toml** file. The subdirectories it creates +aren't necessary, but help illustrate Hugo's structure. In any case, you'll want +to wind up with a directory structure something like this (created with option 2 +above, extending from previous examples): + +```nil +. +├── archetypes +│   └── default.md +├── config.toml +├── content +├── data +├── hugotest.org +├── layouts +├── static +└── themes +``` + +Exporting with ox-hugo using `C-c C-e H A` again will, as expected, fill the +content directory with our content. + +```nil +. +├── archetypes +│   └── default.md +├── config.toml +├── content +│   ├── fishsticks +│   │   ├── gortons.md +│   │   ├── _index.md +│   │   └── van-de-camps.md +│   ├── _index.md +│   └── posts +│   ├── herpes.md +│   └── _index.md +├── data +├── hugotest.org +├── layouts +├── static +└── themes +``` + +Now, running the command `hugo` with no subcommands will invoke the Hugo +generator on the current directory, and output finalized content in the +**public/** directory. + +```nil +. +├── archetypes +│   └── default.md +├── config.toml +├── content +│   ├── fishsticks +│   │   ├── gortons.md +│   │   ├── _index.md +│   │   └── van-de-camps.md +│   ├── _index.md +│   └── posts +│   ├── herpes.md +│   └── _index.md +├── data +├── hugotest.org +├── layouts +├── public +│   ├── categories +│   │   └── index.xml +│   ├── fishsticks +│   │   └── index.xml +│   ├── index.xml +│   ├── posts +│   │   └── index.xml +│   ├── sitemap.xml +│   └── tags +│   └── index.xml +├── static +└── themes +``` + +Hugo, by default, generates xml files that are suitable for RSS feeds. With a +theme installed, Hugo will produce more suitable web content (usually +HTML). You'll notice from this default output however that Hugo creates a +sitemap, and two directories for [taxonomies](https://gohugo.io/content-management/taxonomies/) that let you "tag" and "categorize" +content. The taxonomy index pages allow users to browse content by category or +tag. These taxonomies correspond to org-mode tags, and ox-hugo will +automatically associated tagged headings with the tags taxonomy, or the +categories taxonomy if prefixed with an @ symbol. You are free to define your +own taxonomies, and even disable the default "tags" and "categories" taxonomies, +but since org-mode tags directly translate to the default Hugo taxonomies, it +makes sense to just use the default taxonomies for now. + + +# Example Hugo Site {#example-hugo-site} + +As an example, let's add some tags and categories to our **hugotest.org** file: + +{{< highlight org "linenos=table, linenostart=1" >}} +#+hugo_base_dir: . +* My Homepage +:PROPERTIES: +:EXPORT_HUGO_SECTION: +:EXPORT_FILE_NAME: _index +:EXPORT_HUGO_MENU: :menu "main" +:END: +This is the home of my blog! +* My Blog +:PROPERTIES: +:EXPORT_HUGO_SECTION: posts +:END: +** My Blog Homepage +:PROPERTIES: +:EXPORT_HUGO_MENU: :menu "main" +:EXPORT_FILE_NAME: _index +:END: +Man, look at all my blog posts. +** I have herpes :@inanity:herpes:fear: +:PROPERTIES: +:EXPORT_FILE_NAME: herpes +:END: +Someone gave me herpes! Oh no! +* Fishsticks +:PROPERTIES: +:EXPORT_HUGO_MENU: :menu "main" +:EXPORT_HUGO_SECTION: fishsticks +:EXPORT_FILE_NAME: _index +:END: +This section devoted to Orson Wells, R.I.P. +** Van De Camps :@inanity: +:PROPERTIES: +:EXPORT_FILE_NAME: van-de-camps +:END: +If this is fish, I'll be a monkey's uncle. +** Gortons :@inanity:herpes: +:PROPERTIES: +:EXPORT_FILE_NAME: gortons +:END: +I think these gave me the herpes. +{{< /highlight >}} + +Exporting **hugotest.org** with `C-c C-e H A` and generate with `hugo` will yield +the same file structure as before, but this time we'll see that the categories +and tags directories have sections for our newly added tags. + +```nil +. +├── archetypes +│   └── default.md +├── config.toml +├── content +│   ├── fishsticks +│   │   ├── gortons.md +│   │   ├── _index.md +│   │   └── van-de-camps.md +│   └── posts +│   └── herpes.md +├── data +├── hugotest.org +├── layouts +├── public +│   ├── categories +│   │   ├── inanity +│   │   │   └── index.xml +│   │   └── index.xml +│   ├── fishsticks +│   │   └── index.xml +│   ├── index.xml +│   ├── posts +│   │   └── index.xml +│   ├── sitemap.xml +│   └── tags +│   ├── fear +│   │   └── index.xml +│   ├── herpes +│   │   └── index.xml +│   └── index.xml +├── static +└── themes +``` + +The index pages of taxonomies provide a list of all available taxonomies of that +type, with links to lists that show content associated with that taxonomy. For +instance, public/tags/index.xml looks like this: + +{{< highlight xml "linenos=table, linenostart=1" >}} + + + + Tags on My New Hugo Site + http://example.org/tags/ + Recent content in Tags on My New Hugo Site + Hugo -- gohugo.io + en-us + + + + + + Fear + http://example.org/tags/fear/ + Mon, 01 Jan 0001 00:00:00 +0000 + + http://example.org/tags/fear/ + + + + + Herpes + http://example.org/tags/herpes/ + Mon, 01 Jan 0001 00:00:00 +0000 + + http://example.org/tags/herpes/ + + + + + +{{< /highlight >}} + +And public/tags/fear/index.xml looks like this: + +{{< highlight xml "linenos=table, linenostart=1" >}} + + + + Fear on My New Hugo Site + http://example.org/tags/fear/ + Recent content in Fear on My New Hugo Site + Hugo -- gohugo.io + en-us + + + + + + I have herpes + http://example.org/posts/herpes/ + Mon, 01 Jan 0001 00:00:00 +0000 + + http://example.org/posts/herpes/ + Someone gave me herpes! Oh no! + + + + Van De Camps + http://example.org/fishsticks/van-de-camps/ + Mon, 01 Jan 0001 00:00:00 +0000 + + http://example.org/fishsticks/van-de-camps/ + If this is fish, I&rsquo;ll be a monkey&rsquo;s uncle. + + + + +{{< /highlight >}} + +This allows themes to easily build navigation pages for browsing or querying +taxonomies. Files like these are often useful to output as JSON (done by the +theme) to allow Javascript-driven dynamic search features, but a simpler scheme +can output HTML pages to browse taxonomies just as you would posts in a section +(i.e. org-mode heading). + + +## Theming {#theming} + +The last thing to do here is to download or create a theme for Hugo. As +mentioned before, installing a theme is very simple. This blog uses a custom +theme named Speedy that I have been developing to help myself learn Hugo's +internals, but for this example I'll be using Kaushal Modi's [bare-min theme](https://github.com/kaushalmodi/hugo-bare-min-theme). The +bare-min theme is the best starting place out there for making new themes, and +outputs basic HTML pages without any need to mess with CSS or JS. It also +provides easy debugging facilities and search features. + +We'll just install it and generate the site again. You can download the theme +from its github page and extract it to the themes folder, or much more easily +use git to clone it to your themes directory. +`git clone https://github.com/kaushalmodi/hugo-bare-min-theme.git themes/bare-min` +Then open up your **config.toml** file, and add the theme. + +{{< highlight toml "linenos=table, linenostart=1" >}} +baseURL = "http://example.org/" +languageCode = "en-us" +title = "My New Hugo Site" +# Adding a theme: +theme = "bare-min" +{{< /highlight >}} + +Be sure that the theme's name matches the theme directory's name in the themes/ +directory of your project base directory. (e.g. themes/bare-min here). + +That's it for installing the theme. Just run `hugo` again, and behold your output: + +```nil +. +└── public + ├── categories + │   ├── inanity + │   │   ├── index.html + │   │   └── index.xml + │   ├── index.html + │   └── index.xml + ├── css + │   └── github_chroma.css + ├── fishsticks + │   ├── gortons + │   │   └── index.html + │   ├── index.html + │   ├── index.xml + │   └── van-de-camps + │   └── index.html + ├── index.html + ├── index.xml + ├── js + │   └── search.js + ├── page + │   └── 1 + │   └── index.html + ├── posts + │   ├── herpes + │   │   └── index.html + │   ├── index.html + │   └── index.xml + ├── sitemap.xml + └── tags + ├── fear + │   ├── index.html + │   └── index.xml + ├── herpes + │   ├── index.html + │   └── index.xml + ├── index.html + └── index.xml +``` + +The bare-min theme outputs HTML, provides CSS for doing chroma-based syntax +highlighting (in case you include code blocks), and inline styles for basic +page formatting. Generated pages also have a lot of useful debugging information. + +You can now serve the **public/** directory over an HTTP server. Hugo is packaged +with an internal [HTTP server](https://gohugo.io/commands/hugo_server/) to help with testing, which is quite convenient +because it can automatically refresh whenever content in its content directory +is updated (so when you export from ox-hugo, you don't have to run `hugo` +again). To use it, simply run `hugo server` and point your browser at + (1313 is the default `--port` argument for `hugo server`). + +Eventually you'll want to move on to other themes, or develop your own, but at +this point you've got a fully functional blog publishing workflow from start to +finish. + + +# Attaching Files, Capturing Information & Automation {#attaching-files-capturing-information-and-automation} + +Once you have a basic site structured in your org file, you're ready to start +throwing information in it. It is of course sufficient to open the org file and +edit it, but most org-mode users prefer to automate _everything_, and being able +to use org's capture feature to instantly populate new blog posts is extremely +convenient. + +The [ox-hugo documentation](https://ox-hugo.scripter.co/) provides succinct explanations on how to do this, +including elisp snippets for [capture setup](https://ox-hugo.scripter.co/doc/org-capture-setup/), [image linking](https://ox-hugo.scripter.co/doc/images-in-content/), and [automating +exports](https://ox-hugo.scripter.co/doc/auto-export-on-saving/) when you save your org file (so no more need to `C-c C-e H A` every +time, just save the file as usual with `C-x C-s`).