The One and the Many

Updating untagged Go dependencies

I recently discovered that GitHub's dependabot does not automatically update Go dependencies referenced by commit. This is unfortunate as when you have multiple GitHub repos it's nice to rely on dependabot to keep your dependencies up to date.

As a result, I was looking for a workaround. What I came up with so far is a small tool and an accompanying GitHub Action that can look for these dependencies being out of date. I've called it check-untagged-go-deps. Note I am not suggesting this tool as a great solution as it is simplistic and likely will not handle many edge cases. However I think it will be adequate for repos I work with.

What are these dependencies?

The dependencies I'm talking about are in go.mod files and look like this:

require (
  github.com/maxmind/mmdbwriter v1.1.1-0.20251212174813-0dd562e0096e
  go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
)

As opposed to those referenced by a version tag:

require (
  github.com/stretchr/testify v1.11.1
)

Go calls the first pseudo-versions.

Happily many modules today do tag releases, so having this kind of dependency is not very common. If you have a dependency that doesn't have a version tag, it is usually an older or less maintained dependency. Or you may need a more bleeding edge version of the module that isn't released yet. Whatever the reason, sometimes these are not easily avoidable.

If you're like me, typically you want your projects to have the latest version of their dependencies. This is nice for both security as well as not falling behind in features and compatibility.

How my tool works

check-untagged-go-deps reads go.mod and looks for these dependencies. Then it uses go list -m -json example.com/sample/module@main to see if there are new commits. If there are, right now it exits with a failure so you can notice and address it. In the future it may make sense to have the GitHub Action create a pull request updating the dependency, but I went with exiting with a failure for simplicity. Note I realize main can vary. Right now the tool is simplistic in its assumptions.

One notable aspect is that the tool parses go.mod. You might wonder whether we could use go list -m -json all rather than parsing this file. I found that the go list command lists transitive dependencies not listed in go.mod, so go.mod seemed preferable.

You might also wonder whether we could run go get -u -t ./... and report if any dependencies referenced by commit were updated. I found that go get will update these dependencies if there is no version for the module at all (e.g. a dependency that never tagged a release), but it won't do so if the module has tagged a release.

For example, consider go4.org/netipx:

$ grep netipx go.mod
        go4.org/netipx v0.0.0-20230824141953-6213f710f925
$ go get -u -t ./...
go: upgraded go4.org/netipx v0.0.0-20230824141953-6213f710f925 => v0.0.0-20231129151722-fdeea329fbba

We were able to update that module. But consider github.com/maxmind/mmdbwriter:

$ grep mmdbwriter go.mod
        github.com/maxmind/mmdbwriter v1.1.1-0.20251212174813-0dd562e0096e
$ go get -u -t ./...
$ grep mmdbwriter go.mod
        github.com/maxmind/mmdbwriter v1.1.1-0.20251212174813-0dd562e0096e

This module wasn't updated even though there are newer commits in its main.

check-untagged-go-deps handles both of these. Hopefully one day dependabot will get updated to handle this and check-untagged-go-deps won't be needed.