3.7 KiB
draft | title | aliases | series | date | author | cover | keywords | description | showFullContent | tags | ||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
false | The Joy of Versioning |
|
|
2023-09-02 | Nick Dumas | Investing in tooling makes adhering to good practices almost easy enough to be joyful. | false |
|
What am I Doing?
Too many times this year I've found myself struggling to improve my blog pipeline because I couldn't keep track of when code stopped and started doing what it was supposed to do. This was entirely my own fault, I was not observing best-practices:
- I wasn't using semantic versioning
- I wasn't tagging
- all development happened on main
- etc etc
All of this worked well enough for private use monoliths, one-offs and skunkworks projects but these Drone pipelines presented a new challenge.
Drone pipelines tend to be structured as a series of docker images operating on a mount that gets injected into all of them so they can share their work. This is fine, docker images are an easy fire-and-forget solution for deploying tools.
As things grew more complex, my sloppy coding practices put me in a lot of unnecessary tight spots.
- Some parts of the pipeline were idempotent, others weren't.
- Some parts of the pipeline were affected by each other's work. For example, one step scans files for attachments and copies them into Hugo-appropriate directories, and the next transforms links from Obsidian to Hugo layouts.
- I frequently wanted to implement multiple features/fixes simultaneously but when this took longer than planned, rolling back to a known-good version was impossible because my docker images are only tagged with
latest
.
All of this added up to things breaking for far longer than they needed to, more often than they needed to. Eventually, enough was enough. I drew a line in the sand and decided that I wasn't going to live like this anymore.
After some digging I found resources that helped me build a Makefile to take care of things. That first Makefile added a lot but I'm only going to cover the tooling for semantic versioning and git tagging; the rest of that Makefile was go cross-compilation and docker image stuff that I'm replacing with bazel.
To handle automatically incrementing semver values, I landed on bump
. Because it's written in Go, I was able to fork it and patch a few minor issues and make sure that it keeps working for the foreseeable future.
Why does it work?
My current solution relies on a few pieces: bump
and my Makefile invoking some git commands.
VERSION ?= $(shell git -C "$(MD)" describe --tags --dirty=-dev)
COMMIT_ID := $(shell git -C "$(MD)" rev-parse HEAD | head -c8)
setup-bump:
go install github.com/therealfakemoot/bump@latest
bump-major: setup-bump
bump major
bump-minor: setup-bump
bump minor
bump-patch: setup-bump
bump patch
bump is a golang utility that'll read a git repository's tags and apply a semantic versioning compliant version increment. bump patch
bumps v0.0.1
to v0.0.2
. bump major
goes from v2.24.5
to v3.0.0
. You get the idea.
All together, this suite works perfectly for handling tagging. I don't have a super rigorous policy on what constitutes a major, minor, or patch version but being able to make bump-patch
to tag a specific known-good commit made a world of difference. My drone pipelines became drastically more reliable thanks to version pinning.
But what about Bazel?
Bazel isn't directly involved in manipulating tags yet. To do that, I'll need to add bazel build files to the bump
repo. I'll cover that in the next post, where I cover how to use bazel's stamping funtionality.