logo

drewdevault.com

[mirror] blog and personal website of Drew DeVault git clone https://hacktivis.me/git/mirror/drewdevault.com.git

how-to-fuck-up-releases.md (4543B)


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