Conventional Commits; Release Please; My Substack Workflow
When coding outside of work, I have a bad habit of being a bit lax when it comes to communicating via
git. One (not so great) excuse for this is that I've got quite a few slow-changing, solo-ish repos that very likely nobody cares about but me (part of the blessings of creating fringe/esoteric R packages).
When I wrote my Rust-based Apple WeatherKit REST API command line utility during GN's mandatory company sabbatical, I used Conventional Commits (ConCom) primarily for a reason you'll see in the middle section of this edition.
The ConCom (which is my unapproved shorthand, not theirs) specification is a "lightweight convention on top of commit messages. It provides an easy set of rules for creating an explicit commit history; which makes it easier to write automated tools on top of." It is designed (like a fine wine) to pair perfectly with Semantic Versioning (SemVer), by describing the features, fixes, and breaking changes made in commit messages.
<type>[optional scope]: <description> [optional body] [optional footer(s)]
ConComs contains the following structural elements communicating intent:
fix:patches a bug and correlates with SemVer's
feat:introduces a new feature and correlates with SemVer's
BREAKING CHANGE: if
BREAKING CHANGE:is in the commit footer, other or ConCom' type/scopes have a trailing
!then you're indicating you've introduces a breaking API change which correlates with SemVer's
types other than
feat:are allowed; some examples are
A commit type scope may be provided via parens, e.g.
feat(sort): add ability to sort results.
The ConCom site has lots of examples and provides the following reasons for using ConComs if you're asking "Why?":
Automatically generating CHANGELOGs.
Automatically determining a semantic version bump (based on the types of commits landed).
Communicating the nature of changes to teammates, the public, and other stakeholders.
Triggering build and publish processes. (This is why I used it for the weatherkit project.)
Making it easier for people to contribute to your projects, by allowing them to explore a more structured commit history.
The ConCom GH organization has a few repositories, including a reference parser.
If you're not already familiar with or convinced about ConCom, then the next section might be right for you.
In the previous section, I mentioned my Rust-based Apple WeatherKit REST API command line utility. While I still do a sizable majority of coding in the highly expressive-yet-succinct R language (which also treats coders like responsible adults, unlike some other language🐍), Rust and Swift are two other go(heh)-to languages, of late.
One thing Rust is especially good at is generating cross-platform project artifacts, such as command line binary applications. I knew I wanted that feature, plus I had a rough plan for the daily features I'd be adding to the weatherkit project during the second of the two aforementioned sabbatical weeks.
As such, I decided to experiment with Release Please, a GitHub project and Action that automates CHANGELOG generation, the creation of GitHub releases, and version bumps for GH projects.
It does this by — you guessed it — parsing your git history, looking for Conventional Commit (see ☝🏽) messages, and creating release PRs. When paired with something like the Rust Release GHA, one can get a serious automation uplift when publishing a new release.
Release Please gathers up your ConCom decorated commits and:
Updates your changelog file (for example
CHANGELOG.md) and other lang/repo-specific files
SemVer tags the commit
Creates a GH Release based on the tag (this puts you, the human, back into the loop)
The Release Please GH Action updates an Action status as it does its work, and keeps you informed via state messages:
autorelease: pendingis the initial state of the Release PR before it is merged
autorelease: taggedmeans that the Release PR has been merged and the release has been tagged in GitHub
autorelease: snapshotis a special state for snapshot version bumps
As noted, Release Please assumes you are ConComs, and notes you should make heavy use of
refactor: and — if necessary — the
! BREAKING CHANGE modifier, so the proper SemVer rules can be followed for version bumping.
It's scary good, even works if you have a squash workflow, and supports many languages.
My Substack Workflow
I've promised to do this for a few months, so here it finally is: my Substack VS Code + markdown "workflow".
Despite having thousands of creators and readers, Substack's editor and other (lack of) tooling is less than stellar (proving you don't have to make everything perfect out of the gate to gain critical mass). My use of Substack was more out of curiosity than anything else. WordPress — which I use on my blog since I do pro bono WP security++ work for a few non-profits, which makes it necessary to live the daily pain — has supported "newsletters" for ages; but I think Substack likely just made onboarding and first-use way less daunting for most content creators.
There are just a few components to the nearly-Substack-editor-free workflow.
The first is that I use a git-managed directory for the newsletter content, and a small script that creates a new daily edition subdirectory, and sectioned boilerplate markdown file and asset (for any images or content-generating scripts I may have hastily written) directory for a new edition:
├── 2022-08-15 │ ├── index.md │ └── assets ├── 2022-08-12 │ ├── index.md │ └── assets ├── 2022-08-10 │ ├── index.md │ └── assets ├── 2022-08-09 │ └── 2022-08-09.01.md ├── 2022-08-08 │ ├── index.md │ └── assets –
It also fires up Visual Studio Code in the right directory. For super-intense or text/code cleanup, technical exploration, etc. I use Sublime Text, but it's hard to avoid VS Code's add-on ecosystem, especially when you've bought into it for such things as Rust and Quarto projects.
I rely heavily on VS Code's inbuilt markdown support and the following extensions:
Markdown Preview Enhanced — the final step from VS Code to Substack involves copying the preview pane's HTML.
Markdown Image — this works with 👍🏽 and makes it dead simple to add inline images to posts.
LanguageTool (LT API) for spelling/grammar when I choose to obey such constraints.
The section banner image provides a view into said VS Code experience.
As noted, the final step is copy/paste from HTML preview into Substack's editor (which I get to via an Alfred Shortcut — that opens the
/publish/post?type=newsletter URL for my newsletter's custom domain).
That final step is a "gotcha", at times. Substack's focus on minimalism is admirable, but it could really use more complex text formatting capability.
Lately, I rely more on Substack's built-in "add an Unsplash image" feature. I'd opine about how it is due to the awesomeness of the photos (especially the Maine ones), but it's more "boB is in a hurry" than artistic selection acumen.
I drop in some annoying Substack buttons and then proceed to grab the seekrit post URL and drop that in Twitter post automation (or, forget to do so), then schedule the post for a later-in-the-AM drop (or, forget to do so).
The above enables much faster content creation, keeps all my content local, and also lets me add any scripts (e.g. ones that scrape a list of links to embed here).
This workflow saves tons of time, puts the power of the VS Code text processing extension universe at my disposal, and hopefully helps make these editions better final products.
I had some other hacky-fancy things setup to try to automate Twitter posting, but — as Substack has kind of proved — simpler is better.
I'm also open to how to improve this workflow if you've solved some things more elegantly.
Two closing items:
take some time to check out Eric Topol's recent post on long COVID
James Deaton shared a great real-world Marp presentation from the share-request in the 2022-08-11 edition ☮