From 6d5271fbe3d30413da51c243c89fd0df1f2c33c8 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sat, 12 Oct 2019 15:34:51 -0400 Subject: Add how to fuck up releases --- _posts/2019-10-12-how-to-fuck-up-releases.md | 72 ++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 _posts/2019-10-12-how-to-fuck-up-releases.md diff --git a/_posts/2019-10-12-how-to-fuck-up-releases.md b/_posts/2019-10-12-how-to-fuck-up-releases.md new file mode 100644 index 0000000..5bbcf67 --- /dev/null +++ b/_posts/2019-10-12-how-to-fuck-up-releases.md @@ -0,0 +1,72 @@ +--- +title: How to fuck up software releases +layout: post +tags: [practices] +--- + +I manage releases for a bunch of free & open-source software. Just about every +time I ship a release, I find a novel way to fuck it up. Enough of these +fuck-ups have accumulated now that I wanted to share some of my mistakes and how +I (try to) prevent them from happening twice. + +At first, I did everything manually. This is fine enough for stuff with simple +release processes - stuff that basically amounts to tagging a commit, pushing +it, and calling it a day. But even this gets tedious, and I'd often make a +mistake when picking the correct version number. So, I wrote a small script: +[semver](https://git.sr.ht/~sircmpwn/dotfiles/tree/master/bin/semver). `semver +patch` bumps the patch version, `semver minor` bumps the minor version, and +`semver major` bumps the major version, based on semantic versioning. I got into +the habit of using this script instead of making the tags manually. The next +fuckup soon presented itself: when preparing the +[shortlog](https://git-scm.com/docs/git-shortlog), I would often feed it the +wrong commits, and the changelog would be messed up. So, I updated the script to +run the appropriate shortlog command and pre-populate the annotated tag with it, +launching the editor to adjust the changelog as necessary. + +Soon I wanted to apply this script to other projects, but not all of them used +semantic versioning. I updated it to work for projects which just use +`major.minor` versions as well. However, another problem arose: some projects +have the version number specified in the Makefile or meson.build. I would +frequently fuck this up in many creative ways: forgetting it entirely; updating +it but not committing it; updating it and committing it, but tagging the wrong +commit; etc. [wlroots](https://github.com/swaywm/wlroots) in particular was +difficult because I also had to update the soversion, which had special +requirements. To address these issues, I added a custom `.git/_incr_version` +script which can add additional logic on a per-repo basis, and updated semver to +call this script if present.[^1] + +Eventually, I went on vacation and shipped a release while I was there. The +`_incr_version` script I had put into `.git` on my home workstation wasn't +checked into version control and didn't come with me on vacation, leading to yet +another fucked up release. I moved it from `.git/_incr_version` to +`contrib/_incr_version`. I made the mistake, however, of leaving the old path in +as a fallback, which meant that I never noticed that *another* project's script +was still in `.git` until I went on another vacation and fucked up another +release. Add a warning which detects if the script is at the old path... + +Some of my projects don't use semantic versioning at all, but still have all of +these other gotchas, so I added an option to just override the automatic version +increment with a user-specified override. For a while, this worked well. But, +inevitably, no matter how much I scripted away my mistakes I would always find a +new and novel way of screwing up. The next one came when I shipped a release +while on an Alpine Linux machine, which ships Busybox instead of GNU tools. +Turns out Busybox gzip produces output which does not match the GNU output, +which means the tarballs I signed locally differed from the ones generated by +Github. Update the signing script to save the tarball to disk (previously, +it lived in a pipe) and upload these alongside the releases...[^2] + +Surely, there are no additional ways to fuck it up at this point. I must have +every base covered, right? Wrong. Dead wrong. On the very next release I +shipped, I mistakenly did everything from a feature branch, and shipped +experimental, incomplete code in a stable release. Update the script to warn if +the master branch isn't checked out... Then, of course, another fuckup: I tagged +a release without pulling first, and when I pushed, git happily rejected my +branch and accepted the tag - shipping an outdated commit as the release. Update +the script to `git pull` first... + +I am doomed to creatively outsmart my tools in releases. If you'd like to save +yourself from some of the mistakes I've made, you can [find my semver script +here](https://git.sr.ht/~sircmpwn/dotfiles/tree/master/bin/semver). + +[^1]: Each of these `_incr_version` scripts proved to have many bugs of their own, of course. +[^2]: Eli Schwartz of Arch Linux also sent a patch to Busybox which made their gzip implementation consistent with GNU's. -- cgit v1.2.3