Moving my posts to orgmode

Thursday, September 8, 2022

orgmodebloggemlog

Intro

As I shared in a previous blog post1, I’ve made the switch to orgmode. Not really to emacs but at least to orgmode :). I’ll talk about my general orgmode workflow in another post. In this one I want to highlight how it changed my blog writing and deployment workflow.

While moving to orgmode to all my digital writings, it quickjy occured to me that I should switch to orgmode for my blog and gemlog writing too. It meant two options: either by creating new posts only in orgmode and exporting them to my current setup or moving all my posts to orgmode and reworking my deployment workflow.

I decided to go “all in” and have everything in orgmode. I looked at options to export orgmode content for my blog. Turns out not only there is a built-in markdown export existing in doomemacs, there is also an existing plugin for emacs to export orgmode to hugo, called ox-hugo2! Don’t you love the opensource community? <3

Ox-hugo is built upon the emacs export capabilities and allow me to configure in my orgfile the path to my hugo directory, and by adding a simple properties to indicate the “content type” (the directory within “content”) to then export posts in the right format (with frontmatter, images, links, source code, …) to your hugo directory, then you just need to run the hugo command to update the public directory (and sync wherever).

One org file to rule all blog/gemlog writings

There are two ways of managing your posts with ox-hugo, either one files per post, or one file for all posts, which is the recommended approach. I decided to follow the recommended approach, and put all my gemlog and blog posts within 1 file. It made emacs slow, but removing line number and spell checker on such a huge file helped a lot!

The structure of this orgfile is:

#+TITLE: <title of the file> # eg: "My writings"
#+html_head: <link rel="stylesheet" href="../templates/org.css">
#+STARTUP: show1levels
#+TAGS: Blog(b) Gemlog(g)
#+HUGO_BASE_DIR: ~/path/to/hugo/

* Drafts
** TODO A post in draft
** TODO Another Draft
* Blog
:PROPERTIES:
:EXPORT_HUGO_SECTION: posts
:END:
** <Year> # eg: "2022"
*** <Month number> - <month name> # eg: "09 - September"
**** <Post Title> # eg: "Moving my blog and gemlog posts to orgmode"
     :PROPERTIES:
     :EXPORT_FILE_NAME: learning-orgmode-and-a-bit-of-emacs
     :EXPORT_DATE: 2022-09-06
     :END:
     <content of this post>
**** <Post Title>
     […]
* Gemlog
:PROPERTIES:
:EXPORT_HUGO_SECTION: gemlog
:END:
** <Year> # eg: 2022
*** <Month number> - <month name> # eg: 09 - September
**** <Gemlog Entry Title> # eg: "Software anthropomorphism… weird brain…"
     :PROPERTIES:
     :EXPORT_FILE_NAME: software-antropomorphism-weird-brain
     :EXPORT_DATE: 2022-09-02
     :END:
      <content of this post>
**** <Gemlog Entry Title> # eg: "Another gemlog entry"
*** <Month number> - <month name>
**** <Gemlog Entry Title>

The header is self explanatory, the main thing for ox-hugo is the HUGO_BASE_DIR to point to my hugo directory. The CSS linked is used for when I export org files in html (mainly for sharing a read only version to others - I used it more for work related notes than personal ones). This and the other element in the header are part of all my org files and not specific for the topic of the day.

First level 1 header is for drafts. I write them here so I don’t care at first if it will be a blog or gemlog post. All draft entry starts with TODO keyword so that ox-hugo don’t even try to export them. That also allows me to add properties to it like any todos like scheduled date or anything; and see them in my agenda view (more on this in another post).

Then for each “content type” (blog or gemlog), I have a level 1 entry for all the blog posts and 1 for all the gemlog entries. Then below each entry I can add the EXPORT_HUGO_SECTION to indicate that all the entries bellow the blog header are blog posts and all the entries bellow gemlog are gemlog posts. With that, ox-hugo will export each entries in the right content subfolder in my hugo directory :).

The date is coming from properties within the entry itself. I’ve only shown one example here but all entries (blog and gemlog) have the same properties definition at the start of the entry. EXPORT_FILE_NAME indicates the name of the file that will be created for this entry and EXPORT_DATE indicate the date.

EXPORT_HUGO_SECTION and EXPORT_DATE are used in combination to export the file at the right place /path/to/hugo/content/<export_hugo_section>/<year>/<month>/<day>/<export_file_name>

That’s it for the structure of the writings.org file, which is simple as I like. A little screenshot:

Figure 1: My org mode structure for the writings.org file

Figure 1: My org mode structure for the writings.org file

Deployment

Thanks to ox-hugo, deploying my blog from markdown is very easy but contains a new step to export either all files to my hugo directory or just the subtree I’m working on.

The s p m shortcut open the following export screen:

Figure 2: Doom Emacs export screen with ox-hugo

Figure 2: Doom Emacs export screen with ox-hugo

So continuing the keybind by adding either H H to export just the subtree or H A to re-export everything. So after finishing a post, I remove the TODO keyword, make sure the properties are correct (data and filename) and move the entry at the right place in the tree.

And orgmode is awesome to move trees around by simple keybind and autocompletion.

Figure 3: Org refile to another header of the page

Figure 3: Org refile to another header of the page

(Where you can see the first title I gave to this post.)

Once I run this command, I can reuse my deploy script that generate the public directory and “rsync it” to my server. Easy!

From md to org

Once this was done, the tough part was migrating all posts in markdown format to orgmode format! That was a bit of a challenge that took a bit of manual cleaning and changes.

I used pandoc3 to move all files to the orgmode format and then concat them in one file. That part was easy:

find . -name '*.md' -type f -exec pandoc  -f markdown -t org -o {}.org {} \;
find . -name '*.org' -type f -exec cat > all_in_one.org {} +

Pandoc created level 1 for title of entries and then subtrees depending on the content, but in the structure I highlighted above, published posts are basically level 4 headings. So I opened the file with emacs to move all subtrees with simple keybinds.

All that was easy, automated and painless… But then entered the “cleaning” process, which was hard, (half) manual and painful.

First I tried to review the different links, but maybe went a bit quickly on this. I found out I had markdown files where link where empty, so it made me find them in the orgmode export easy and could fix them. I went quickly on this because I’m planing to run a simple crawler to check for empty/bad links on my blog soon.

Then the images. I used ox-hugo capabilities to link to files in a directory having “static” in the path, meaning everything below that path will be copied as is in the static folder of hugo. You can read their documentation4 for more information on managing files.

It means for me that in the same folder as the writings.org file, there is a folder named static that contains the structure of my images:

tree -L 4 ./static
static
└── images
    └── posts
        ├── 2020
        │   └── 04
        ├── 2021
        │   ├── 02
        │   └── 03
        └── 2022
            └── 09

And of course the different images in their related “month directory”. I used this opportunity to add a caption to all my images. I also realised some images where broken so I (hopefully) fixed them too, except one I couldn’t find anymore for some reason.

The last and most painful thing to clean were footnotes… This was also due to me not being consistant in the way I managed footnotes until now, so automation was harder. I’m trying now to avoid links directly in my writing content and pushed them to footnotes. It may make less sense on a web page, but does on a gemini page or even when reading org files (IMO)… And I don’t think it is such an issue on an html page anyway, you can click on the footnote number to go back and force between content and notes.

Conclusion

I like having everything at the same place for my blog/gemlog posts. Makes it easy to have more draft at the same time and work on multiple article that may be linked at the same time. Ox-hugo makes it so nice to export it to hugo at the right place in the right format that it is awesome. Regenerating every posts takes a long time but once you’ve done it, you can just generate new or modified files only.

Also, having my gemlog in the same file with ox-hugo simplified managing the export of gemini content to my blog. I don’t have to think about creating an almost empty file in hugo each time I write on my capsule, it is automated :).

Which brings to the next big thing I need to tackle and might be a lot more complex… Managing my gemini capsule from the same orgmode file. Unfortunately there isn’t any equivalent of ox-hugo. There is a couple of ox-gemini that export file or subtree to gemtext format and seems to work well, but doesn’t seem to be able to configure as much as ox-hugo, but I’ll have to look into because I’m sure I’m missing some cool things emacs can do for me here. I still need to deep dive into the export mechanism of emacs (outside of ox-hugo I mean) that seems powerful, so maybe I can automate most of it without coding anything :) Or maybe I’ll have to learn elisp -_-".

I’ll do a dedicated post about my gemlog workflow when it will be… Well, actually working :). Right now it added a bit of manual work before starting my existing deploy script. But that’s another cool nerd thing to work on :).


Footnotes:


Contact

If you find any issue or have any question about this article, feel free to reach out to me via email, mastodon, matrix or even IRC, see the About Me page for details.

See Also

Learning orgmode… and a bit of emacs