Private go modules with multiple git identities

because every tutorial on the internet misses that one percent…

If you’re working with go on a regular basis, chances are you have come across the problem of working with private modules. Let’s quickly recap:

  • Your organization hosts go modules at github.com/your-org.
  • There’s some private project at github.com/your-org/awesome-stuff.
  • This private project depends on other private modules, you have this in your go.mod:
module github.com/your-org/awesome-stuff

go 1.17

require (
    github.com/your-org/awesome-auth-lib v0.1.3
    github.com/your-org/awesome-logging-lib v0.2.6
)

You already have your export GOPRIVATE=github.com/your-org in ~/.bash_profile so that go toolchain doesn’t try downloading private modules from the public go proxy. You have also already found out that for a good measure you need this in your ~/.gitconfig:

[url "git@github.com:"]
    insteadOf = https://github.com

This is what every article about using private go modules explains. And this works. This works for 99% of all cases.

What’s the final percent, you may ask. Let’s assume that your organization has a partner who happens to also use go and has their own private go modules hosted on GitHub. Their code is hosted in the github.com/awesome-partner organization. One day they come to you and ask to contribute to their code. For whatever reason, you decide that you’re going to use a different GitHub identity with a different key pair - it’s a different GitHub account whatsoever.

So you follow the instructions for configuring multiple identities. You add the default username and email address to your ~/.gitconfig:

1
2
3
4
[user]
# Please adapt and uncomment the following lines:
	name = John Doe
	email = john.doe@your-org.com

Which you probably already have anyway. You also configure your SSH so you can use your partner’s repositories:

1
2
3
4
Host awesome-partner
	HostName github.com
	User git
	IdentityFile ~/.ssh/github-partner

And updated repository local configuration: origin, username and email address:

1
2
3
4
5
[remote "origin"]
	url = git@awesome-partner:awesome-partner/cool-stuff.git
	fetch = +refs/heads/*:refs/remotes/origin/*
[user]
	email = john.doe.partners@your-org.com

With this, you can now clone and pull partner’s go project:

1
2
3
mkdir -p $GOPATH/src/github.com/awesome-partner
cd $GOPATH/src/github.com/awesome-partner
git clone git@awesome-partner:awesome-partner/cool-stuff.git

As it happens, the partner also uses plenty of their own secret go sauce. You find something like this:

module github.com/awesome-partner/cool-stuff

go 1.16

require (
    github.com/awesome-partner/secret-sauce v1.10.17
    # more secret sauce...
)

You run go mod tidy and things go wrong. Things go wrong because of the insteadOf rule in the ~/.gitconfig file.

§what you really want

Instead of:

[url "git@github.com:"]
    insteadOf = https://github.com

do this:

[url "git@github.com:your-org"]
    insteadOf = https://github.com/your-org

[url "git@awesome-partner:awesome-partner"]
    insteadOf = https://github.com/awesome-partner

The rule for your organization uses the default github.com hostname so this saves you from having to modify origins for every repository belonging to your organization. This is your bread and butter so you don’t want to do this often. The default rule makes the daily work ergonomic.

All other rules exist for those odd cases when you need to work with your partner’s code. This is where you’ll have to update origins in the repository-local .git/config.

Consider always scoping those rules to the organization.