Private repo access from buildGoModule

I have a derivation using buildGoModule and a go program that depends on a private repository.

There is a similar discussion from 2019 here: git/buildGoModule + private repositories - #7 by tomberek

The setup is that my go project includes a private repo. This can be directly in the source (import (github.com/TomMD/privatething)) or indirectly in the go.mod (replace ( github.com/a/b => github.com/TomMD/b-but-private)).

Ultimately when you build, such as with a default.nix like:

{ pkgs ? import (fetchTarball {url="https://github.com/NixOS/nixpkgs/archive/e9b8b78affb3e881e33680acfd999697493fd38a.tar.gz"; sha256="0vrawckd3khx6rx1sgybppfnrgmsq6d1m7k7jyfw9nakmixxgq7a";}) {}
, gitignoreSourcePure ? pkgs.nix-gitignore.gitignoreSourcePure
}:
 pkgs.buildGoModule rec {
   pname = "hello";
   version = "1";
   src = gitignoreSourcePure [ ] ./.;
   buildInputs = with pkgs; [
     openssh
   ];
   vendorSha256 = pkgs.lib.fakeSha256;
   proxyVendor = true;
   doCheck = false;
   preCheck = ''
     export PATH="${pkgs.lib.makeBinPath [ pkgs.openssh ]}:$PATH"
   '';
   meta = with pkgs.lib; {
     description = "Hello";
     homepage = src.meta.homepage;
     platforms = platforms.linux ++ platforms.darwin;
   };
}

The result is unsurprising because it is using unauthenticated HTTP:

go: github.com/foo/bar@v0.19.1: reading github.com/TomMD/resume/go.mod at revision v0.19.1: git ls-remote -q origin in /private/tmp/nix-build-hello-1-go-modules.drv-0/go/pkg/mod/cache/vcs/f0af8278e5b83502c71826605ec31c65511d4081781b022c6c53a17aec908942: exit status 128:
        fatal: could not read Username for 'https://github.com': terminal prompts disabled

EDIT: Submitted too soon.

The linked discussion has some suggestions. We can try them. First we tell git to use ssh instead of http second we educate the sandbox about the known_hosts:

{ pkgs ? import (fetchTarball {url="https://github.com/NixOS/nixpkgs/archive/e9b8b78affb3e881e33680acfd999697493fd38a.tar.gz"; sha256="0vrawckd3khx6rx1sgybppfnrgmsq6d1m7k7jyfw9nakmixxgq7a";}) {}
, gitignoreSourcePure ? pkgs.nix-gitignore.gitignoreSourcePure
}:
 pkgs.buildGoModule rec {
   pname = "hello";
   version = "1";
   src = gitignoreSourcePure [ ] ./.;
   buildInputs = with pkgs; [
     openssh
   ];
   vendorSha256 = pkgs.lib.fakeSha256;
   proxyVendor = false;
   overrideModAttrs = old: {
         preBuild = ''
         export HOME=$(pwd)
         cat <<EOF > $HOME/.gitconfig
         [url "git@github.com:TomMD"]
             insteadOf = "https://github.com/TomMD"
         EOF
         mkdir ~/.ssh
         chmod 700 ~/.ssh
         cat <<EOF >~/.ssh/known_hosts
         github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl
         EOF
         chmod 644 ~/.ssh/known_hosts
         export GIT_SSH_COMMAND="${pkgs.openssh}/bin/ssh -o UserKnownHostsFile=$HOME/.ssh/known_hosts"
         '';
       impureEnvVars = pkgs.lib.fetchers.proxyImpureEnvVars ++ [ "SSH_AUTH_SOCK" ];
   };
   doCheck = false;

   meta = with pkgs.lib; {
     description = "Hello";
     homepage = src.meta.homepage;
     platforms = platforms.linux ++ platforms.darwin;
   };
}

This gets us much of the way, ssh doesn’t object to the host SSH key and it tries to pull from the correct repo. nix-build fails with:

go: errors parsing go.mod:
/private/tmp/nix-build-hello-1-go-modules.drv-0/go-hello-world/go.mod:6:2: replace github.com/TomMD/go-git: git ls-remote -q origin in /private/tmp/nix-build-hello-1-go-modules.drv-0/go/pkg/mod/cache/vcs/24658a01e857650bb9f5c0bd2b11fcf9feb3612f395da37911296c9feb0445d5: exit status 128:
        git@github.com: Permission denied (publickey).
        fatal: Could not read from remote repository.

Now in the prior (linked) conversation this is where ssh-agent was hinted at with an impure of SSH_AUTH_SOCK. That doesn’t work and certainly shouldn’t since the socket file isn’t available in the build sandbox. What’s the solution here?

Without knowing the intricacies of go’s download process, if it’s available via https, could a netrc file be used instead? The other discussion seems to require ssh, which is more tricky.

I can’t get .netrc to work with nix-build + go. After perhaps 45 minutes I gave up on that avenue, but if someone else has success then a small write up would be appreciated. Till then, the “best” solution is probably still to perform go mod vendor outside of nix.

1 Like

Hey @TomMD .
I’m building a go project with dependencies in private repos and came to the same conclusion - just have to vendor outside of nix.

What are you doing about the vendorHash? As I understand you need to leave it as null in order to force pkgs.buildGoModule to respect the vendor folder and not reach out to the source repos of the dependencies in go.mod.

Are you just leaving vendorHash as null forever (I guess you have to).

Also if you came to a resolution on this project, do you have any other insights about using nix to build go projects with private dependencies?

Yeah, using a null on the vendor hash is necessary. You might also want to hard code a version such as buildGoXXXModule instead of letting the go version float with the nix hash, but that’s not certain. CGO_ENABLED = 0 will have the obvious impact as well, keeping it enabled be needed for some code bases but it impacts reproducibility in some cases.

Related topic with a possible solution to use GOPROXY: Rethink GOPROXY - #10 by ppenguin