How to unpublish/redact/undo/retract a Go release
I recently rolled out go-semantic-release on dependency-management-data (DMD) to make managing the changelog a little easier, by taking Conventional Commits and converting them to a much nicer changelog.
While dependency-management-data is actively used in production by several organisations, it's still not stable, as I've still got some breaking changes and tweaks to internals that I'd like to do before I call it a stable interface, meaning that the release version is going to be v0.x.y.
As part of rolling out go-semantic-release, one thing I found was that that the default is to push stable release versions (a bad default, in my opinion) and so when I pushed the first commit using go-semantic-release, it detected that there wasn't a v1.0.0 release yet, and so seeing that there were changes to release, it pushed v1.0.0, instead of v0.64.x.
This was problematic for a few reasons - the first was that as I'd mentioned, I didn't want to call the current interface of dependency-management-data stable, but if we were now on the v1.x.x release train, I couldn't start pushing arbitrary changes as folks wouldn't expect there to be a breaking change in a stable version. Secondly, this was the Friday before Christmas, and just hours later I needed to start packing to go to my family's for the next few days, meaning I either needed to fix it now, or risk folks relying on the stability π¬
I also wasn't sure if I even would be able to redact the v1.0.0 release and continue pushing v0.x.y releases, or if Go would say that @latest
always pointed to v1.x.y releases.
After some discussion on the Gopher Slack, it was clear that it wasn't clear to folks what should happen, so I set about testing it. The short answer is that yes, if you use the retract
directive, that version can still be downloaded, but won't be discovered when i.e. using @latest
.
It turns out that the official Go module documentation for the retract
directive explains how to retract the version, and even says:
When a user runs
go get example.com/m@latest
, thego
command reads retractions fromv1.0.1
, which is now the highest version. Bothv1.0.0
andv1.0.1
are retracted, so thego
command will upgrade (or downgrade!) to the next highest version, perhapsv0.9.5
.
In my slight panic, I'd not read that at the time, but it's good to know that it's there and super clear.
For example, let's say that I was on v0.1.0
, and had accidentally published v1.0.0
. To retract this version, we could perform these steps:
- Create a commit, on my default branch, which added a
retract v1.0.0
statement - Tag that release as
v1.0.1
- Push that tag
- Run
go get $module@v1.0.1
, to make sure that the Go module proxy fetches that particular version with the retraction - Create a commit, on my default branch, which adds a
retract v1.0.0
andretract v1.0.1
statement, to make sure both the original release, and the release with the retraction, are ignored - Tag that release as
v0.1.1
- Push that tag
- Run
go get $module@latest
, which should then resolve tov0.1.1
(some delay may be required for the module proxy to see the latest tag as@latest
)
After this process, we're now able to correctly push to the v0.x.y release train.
In my case, I also deleted the tags in the repo, as otherwise go-semantic-release will see that there's a previous v1.x.y release, and continue versioning after that.
However, because we've go get
'd the versions, the Go Module Proxy has the version cached.