Blog writing process
Methodology, tools and procedures
Okay I have 9 blog posts in progress and 220 pages total on my site. It’s starting to look like an organized and productive system. Here is how I organize my writing workflow using Spacemacs and Firefox.
Site metadocumentation
This post is more about the workflow for writing the site content.
You will find documentation about the site’s tech stack and hosting in metadocumentation. In summary, the site is built with Hugo (one of the most popular open-source static site generators) and the Wowchemy theme and templates, self-hosted on a TrueNAS box with a pfSense reverse proxy.
Editing software
I do all the editing in Spacemacs, with heavy use of the plugins Projectile and Magit (Spacemacs layers git and project).
Here is a screenshot of my écriture browser session.
Under blogue, I have my blog site with a distinct favicon for dev, staging and production. Research tabs for each post drafts are organized under authoring. Under docs, I have reference documentation for Hugo and Wowchemy.
See also how I manage my Firefox tabs and sessions with Tree Style Tabs and Tab Session Manager.
Reference documentation
For getting started guides, go here:
https://wowchemy.com/docs/getting-started/
When picking a starter template, “Start with […]” means SaaS hosting and CMS with Fastly, and “Star on GitHub” is where you can clone the Git repo for self-managed sites.
Here are the reference documentation pages I have open in my “écriture” Firefox session. You always need reference documentation at these four levels: your CMS engine, your site template, your site, your content authoring.
- Wowchemy page elements (my site template).
- The Emoji cheat sheet based on Hugo’s enableEmoji setting.
- https://emojipedia.org/ for copy-pasting emoji characters.
- Hugo Documenation (my CMS engine).
- My site’s Metadocumentation.
- Style guide.
- My content tags and categories.
Markup style
I write in Markdown (Hugo uses Goldmark by default) with semantic line breaks. I document custom styles and frequently used markup in the Styles metadocumentation page.
Icons for staging and dev
To help differentiate dev and staging tabs in Firefox,
I use a variant favicon that I serve as static files.
A custom template in Wowchemy
replaces the icon using Javascript depending on the HUGO_ENV
parameter set with the --environment
flag in the publish
script.
{{ if (in (slice (getenv "HUGO_ENV") hugo.Environment) "staging") }}
<script type="text/javascript">
var link = document.querySelector("link[rel~='icon']");
link.href = '/images/icon-staging.png';
</script>
{{ end }}
{{ if (in (slice (getenv "HUGO_ENV") hugo.Environment) "development") }}
<script type="text/javascript">
var link = document.querySelector("link[rel~='icon']");
link.href = '/images/icon-dev.png';
</script>
{{ end }}
Development site icon:
Staging site icon:
Production site icon:
Scripts
I have a few scripts to assist me with authoring tasks.
I launch them from a terminal windows in Spacemacs (SPC p '
).
dev
script
Starts a hugo server with a few command line parameter.
I typically launch this as a compile command (SPC p c
).
It runs in the background, I hide/show the compile window with SPC c d
.
hugo --i18n-warnings server --buildDrafts --buildFuture
new
script
I use this script to create a new post.
It automatically creates a new branch on master
and a new post in content.
git checkout master
git checkout -b "post/$1"
hugo new --kind post "post/$1"
publish
script
This script has three functions, depending on how I call it.
./publish
- Check that the
master
branch is checked out. - Publish to prod with rsync.
- Check that the
./publish staging
- Check that the
staging
branch is checked out. - Publish to staging site with rsync.
- Check that the
./publish static
rsync
my blog files to the static files site.
if [ "$1" = "static" ]; then
rsync --verbose --recursive --chmod=ug=rwX,o=rX --delete "/home/alex/Documents/Blog files/" <web server>:static
elif [ "$1" = "staging" ]; then
hugo \
--environment staging \
--buildDrafts \
--buildFuture \
--baseURL "https://staging.alexandre.deverteuil.net/" \
&& rsync --verbose --recursive --chmod=ug=rwX,o=rX --delete public/ <web server>:staging
else
if [ "$(git branch --show-current = \"master\")" ]; then
hugo \
&& rsync --verbose --recursive --chmod=ug=rwX,o=rX --delete public/ <web server>:public
else
echo "Not on the master branch. Aborting!"
exit 1
fi
fi
retitle
script
When I use the new
script to create a new post,
I have to think about the title before writing.
Sometimes I want to rename a post while editing.
The retitle
script renames the branch and the post slug.
old=$(git branch --show-current) # Branch name should start with "/post/"
new="$1" # No "/post/" prefix required in the command parameter.
if [ ! -d "content/$old" ]; then
echo "You are on branch \"$old\" but the post \"$old\" does not exist."
echo "Please retitle manually."
exit 1
fi
echo "Rename \"$old\" to \"post/$new\"? [y/n]"
read r
if [ "$r" = "y" ]; then
mv "content/$old" "content/post/$new"
git branch --move "$old" "post/$new"
fi
Git branching model
master
branch
Always in sync with production.
The publish
script checks if the current branch is master
before executing rsync
.
post/*
branches
I have several posts in progress at any given time.
Right now I have 9.
Each post draft has its own branch, which I start from master
.
I rebase post/*
branches on master
whenever they get out of sync.
The initial commit message is “Create post/something-something”. Every subsequent post is “Edit”. I don’t really specify what is edited in each commit in a granular way. Sometimes, a commit fixes a typo. Sometimes it rearranges paragraphs. After a while, I get something like this.
Rebase action | Commit message |
---|---|
pick | Create post/my-post |
squash | Edit |
squash | Edit |
squash | Add feature image |
squash | Change feature image |
squash | Edit and retitle |
When I’m ready to publish, I’ll rebase and squash my edit commits, keeping only the create commit.
staging
branch
I have a staging site at https://staging.alexandre.deverteuil.net. It’s password protected. This is to allow friends to review my drafts.
I merge posts on staging
to share them, and git reset --hard master
to reset, discarding the merge commits.
Media files
Large static files are hosted on a separate static HTTP server. It’s just another FreeBSD jail with apache24 serving a filesystem directory. This is to avoid commiting large binary files to Git, like videos or large archives I host for sharing.
I put those files in ~/Documents/Blog files
.
They get synchronized to Nextcloud for backups (see this post about my Nextcloud setup).
I commit blog post images optimized for web in my site’s repository, because they’re optimized and I can take advantage of Hugo Page Bundles.
Writing project media and supporting material
If I need to organize files related to a blog post, I can just create a directory under ~/Documents/Projets
.
See How I organize my ~/Documents directoy.
Such project directories may contain pictures, drawings, scans, etc.
The web optimized version is exported to my blog content and I keep the originals on my personal computer.
Editing helper style
I created a custom CSS style and a Hugo shortcode to help annotate my writings.
See my edit
shortcode documentation.
I can use it by appending the {.edit}
custom attribute after any paragraph in my Markdown content like this:
A paragraph I want to mark as draft,
or an edit annotation I want visually standing out.
{.edit}
And it will be rendered like this:
A paragraph I want to mark as draft, or an edit annotation I want visually standing out.
Before publishing an article, I make sure to remove all edit annotations.
Improvements ideas
Metrics and logs for the site build and publish jobs
Hugo spouts out metrics:
| EN
-------------------+------
Pages | 220
Paginator pages | 1
Non-page files | 436
Static files | 5
Processed images | 304
Aliases | 34
Sitemaps | 1
Cleaned | 0
I could parse them (can hugo output JSON?) and send them to Pushgateway, and log a line and get it picked up by Promtail’s systemd collector.
In the publish
script, I would use curl
and logger
.
Takeaways
- Site metadocumentation in /docs/meta/
- Software I use for authoring and development:
- Git branches:
master
is what is shown on my public/production site.- Each post draft has a
post/*
branch. staging
is for sharing drafts with friends.
- Git workflow:
- I frequently rebase my
post/*
branches onmaster
. - Squash commits before merging branches.
- Don’t worry about committing unoptimized images temporarily. They will be garbage collected after commit squashing, keeping only the web optimized version.
- I frequently rebase my
Writing process inspiration
- Julia Evans, Blog about what you’ve struggled with, blog post.
- Ben Halpern’s website, sick website dude! My personal interpretation of this lesson here is to be bold, courageous, and to shine with the true colours of my core self.
- Essay Writing Guide By Jordan B. Peterson. It’s a good template to get started in the writing process.