Fri May 14 2021

Making a release toolkit - Part 4: Publishing

This is the last in a series of posts about creating my very own release toolkit for automated package publishing. The other posts are here:


Finally, publishing! This has been a fun process, it's nice to be almost finished. It wasn't a terribly large project, but big enough that I kept putting it off. Breaking it down into small chunks helped me wrap my head around it. So did writing about it.

Much of the work since the last post was plugging away at small tasks, building out the version, verify, and publish commands. Each one had small, clear goals so it was easy to find time here and there to work on it.

I did end up using commander to pass in arguments from the command line so I didn't need to answer the prompts by hand every time. I also created a release script, just a wrapper around version and changelog so I could create a release in one line:

yarn release --minor --type Feature --message "- Sweet feature"

As always, writing tests along the way made it painless when I needed to make changes, and helped me quickly discover when I had broken things.

Publish

This step actually took me longer than I expected. It's simple, basically just npm publish, but creating a GitHub release caused some friction. I initially started with tags, but releases are nice becuase you can see right from the repo homepage what the latest version is.

latest release on GitHub
Can tags do this? Dream on, tags.

One thing: once you've published, npm and GitHub make you jump through some hoops to discourage un-publishing a version of a package, since it would cause things to break for anyone using that version as a dependency. After the left-pad incident 1 a couple years ago npm put even stricter protections in place.

Even if you can un-publish, I try to be very careful about making sure something is ready before releasing a version. You can publish a patch of course, it's just a little embarassing putting something into the public and then realizing you missed something. I gave publish a --dry-run option to see the output of the publishing step without actually publishing.

This is also why I included a verify step: with GitHub branch protection you can make sure nothing gets merged to the main branch without first verifying that the version and changelog have been updated. If you run this on all your branches (alongside commands like test and lint) and require pipelines to pass before merging to main, you've taken an extra step to make sure you're really ready to publish. At that point you can set it up to automatically publish whenever branches merge to main. That's CI/CD (continuous integration / continuous delivery) in a nutshell!


The most annoying part of this was publishing a GitHub release. Turns out it was mosly my own dang fault. Their documentation is usually so good, but for some reason it was near impossible to find documentation for this specifically: automatically creating a release from a GitHub workflow.

Doing this from the command line using curl? Easy. Locally in Node with node-fetch or axios? No problem. Directly in a GitHub workflow run command using the GITHUB_TOKEN environment variable? Piece of cake.

But making an authenticated request in a workflow from Node to /release eluded me for hours.

I tried every combination of authentication I thought would work, stirring a cauldron of Bearers, tokens, and usernames while muttering incantations, but my attempts did not bear fruit. I know there are third-party actions for this, but where' the fun in that? These 7 year old forum posts aren't going to read themselves at 1:00am!

Pipeline debugging is my least favorite kind, due to the long feedback cycle. Make a change, push it, watch the pipeline fail after 45 seconds, rinse and repeat for three hours. Sometimes there are even more steps in there if I'm testing changes that only happen on a protected main branch. There are a few tricks to shorten the loop—similar to all other debugging the key is isolation—in this case we can comment out pipeline steps to move things along faster.

Engineers spend a lot more time debugging than we'd like to admit, so my #1 debugging rule is enjoy it as much as you can. I have a friendly relationship with debugging, I always try to think of it as a game, a puzzle, or a detective story. It really does make it more satisfying when you say "Forget it, Jake. It's Chinatown" when you finally squash that bug.

So, what was the issue? I was getting the repository name with git remote get-url origin and pulling the name out of the url. Turns out that command returns a different url format locally than it does in the CI pipeline, so my /release request was failing not due to authentication but due to an invalid url (the response is 404 in either case, so it wasn't obvious). GitHub actually provides a GITHUB_REPOSITORY environment variable, so... time well spent. 🙄

Got everything working quickly after that, and then spent some time cleaning up and documenting in the README.

This has been a fun little journey! I learned a bit more than I thought I would, and got to set up my custom release toolkit exactly how I want it.


1: For more information see this article, or literally just google "left pad incident." As long as you're googling incidents, look up "The Spaghetti Incident?" 2 Really an underrated album, some of Duff McKagan's best work.

2: As long as you're googling pasta music, look up "The Tortellini Accident." Really an underrated album, some of Buster Poindexter's best work.