TL;DR Given a Go module, assuming that I have already done `go mod download`: Is it possible to prevent network access if I delete the entire `$GOPATH/pkg/mod/cache`?
> [...]Manlio Perillo
TL;DR Given a Go module, assuming that I have already done `go mod download`: Is it possible to prevent network access if I delete the entire `$GOPATH/pkg/mod/cache`?
On Monday, March 11, 2019 at 12:30:02 AM UTC+1, Wael Nasreddine wrote:TL;DR Given a Go module, assuming that I have already done `go mod download`: Is it possible to prevent network access if I delete the entire `$GOPATH/pkg/mod/cache`?
Another solution is to delete only $GOPATH/pkg/mod/cache/vcs. This is the directory that takes more disk space since it contains the full history of the vcs.
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index 6d6c037af2..77765023be 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -968,11 +968,44 @@ func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
base.Fatalf("go: internal error: %s@%s: unexpected invalid semantic version", mod.Path, mod.Version)
}
- data, err := modfetch.GoMod(mod.Path, mod.Version)
- if err != nil {
- base.Errorf("go: %s@%s: %v\n", mod.Path, mod.Version, err)
- return nil, ErrRequire
+ // ~HACK~: Support for buildGoModule
+ //
+ // compute the content of go.mod for this module either by using
+ // modfetch.GoMod() in regular day by day use or by reading the version
+ // already on disk.
+ var (
+ data []byte
+ )
+ if _, ok := os.LookupEnv("__NIX_GO_SKIP_MOD_DOWNLOAD"); ok {
+ // XXX: Must not depend on hardcoded GOPATH
+ gomodPath := fmt.Sprintf("%s/pkg/mod/%s@%s/go.mod", os.Getenv("GOPATH"), mod.Path, mod.Version)
+
+ // does gomodPath exist? If so set data to the contents of this module. If
+ // it does not exist then we must set it to `module mod.Path`.
+ _, err := os.Stat(gomodPath)
+ if err == nil {
+ data, err = ioutil.ReadFile(gomodPath)
+ if err != nil {
+ base.Errorf("go [NIX-REQUIRED]: %s@%s: %v\n", mod.Path, mod.Version, err)
+ return nil, ErrRequire
+ }
+ } else {
+ data = []byte("module " + mod.Path)
+ }
+ } else {
+ // the following call has been taken as it existed before the patch. The
+ // `:=` had to be converted to an `=` as the data and error are now both
+ // defined at the top of the if block.
+ var err error
+ data, err = modfetch.GoMod(mod.Path, mod.Version)
+ if err != nil {
+ base.Errorf("go: %s@%s: %v\n", mod.Path, mod.Version, err)
+ return nil, ErrRequire
+ }
}
+ //
+ // ~HACK~: Support for buildGoModule
+
f, err := modfile.ParseLax("go.mod", data, nil)
if err != nil {
base.Errorf("go: %s@%s: parsing go.mod: %v", mod.Path, mod.Version, err)You can try with the following flow (not tested).1. download modules with `go mod download`2. *copy* the content of $GOPATH/pkg/mod/cache/download to the output directory3. set GOPROXY=file:///$out/... when you `go build`
On Sunday, March 10, 2019 at 6:59:07 PM UTC-7, Manlio Perillo wrote:On Monday, March 11, 2019 at 12:30:02 AM UTC+1, Wael Nasreddine wrote:TL;DR Given a Go module, assuming that I have already done `go mod download`: Is it possible to prevent network access if I delete the entire `$GOPATH/pkg/mod/cache`?Another solution is to delete only $GOPATH/pkg/mod/cache/vcs. This is the directory that takes more disk space since it contains the full history of the vcs.That was my first attempt, however it did not work as expect because the contents of $GOPATH/pkg/mod/cache/download does change anytime a new version is created upstream. My hash failed to match multiple times a day!It may be caused by the missing pkg/mod/cache/vcs, but it seems unusual.I was assuming GOPATH was on a temporary directory.And indeed you wrote that GOPATH is set to $NIX_BUILD_TOP/go, but later you wrote that it is set to a temporary directory.I am removing the entire cache folder, as the content changes when the git repository of any of the dependencies change. So if I would hash the cache folder to A and one of the dependencies get a pull request merged upstream, even though it does not technically change the version that Go is building with you will still get B when you hash it. This break the concept of packaging :(
Do you perhaps have the same requirements as in the threadas reported by Nicolas Mailhot?
That is, you need to patch the upstream source but keep the same version, because you can't (or don't want to) update all the versions of the required modules.
In this case vendoring is, IMHO, currently the only solution, because the go tool ignores the go.sum files in this case.You can try to open a bug report about the incorrect behavior of `go mod vendor` when using cgo.Or you can patch cmd/go. However instead of writing a patch specific to Nix, I suggest to write a more generic patch.You can add a new module download mode, e.g. `-mod trust` to instruct cmd/go to not check go.sum.The future notary can be disabled with GONOVERIFY, so its not a problem.Maybe you can use GONOVERIFY to decide if the checksum should be ignored.Finally, you can try to coordinate with other package managers, since this seems to be a shared problem.Here is a patch. It seems to work but one the Go test fails, and it should probably be updated:An alternative is to check for trust mode in the initGoSum function:Finally the last alternative is to check for trust mode in the checkGoMod function, but this will also disable the notary. Maybe it is the correct thing to do:
Hi Wael,Sorry, I am not quite following what you have and have not tried yet, and which issues you have hit with which techniques.
Is one of the issues that the 'vcs' cache directory changes, even if the actual code you need for your build has not changed?
If so, I wonder if you might be able use a filesystem-based module cache via 'GOPROXY=file:///file/path', which could avoid using the 'vcs' directory at build time?It sounds like you have looked into multiple options at this point, so this might be well known to you at this point, but:* The module download cache location is controlled by GOPATH. In particular, 'go mod download', 'go build', etc. populate the module cache in GOPATH/pkg/mod.* In addition, when you want to use a particular module cache, you can tell the 'go' command to use a local module cache by setting 'GOPROXY=file:///file/path' environment variable.* You can put those two things together:# Populate a module download cache in /tmp/gopath-for-cache$ GOPATH=/tmp/gopath-for-cache go mod download# Build using the contents of the module download cache in /tmp/gopath-for-cache$ GOPROXY=file:///tmp/gopath-for-cache/pkg/mod/cache/download go buildNote that /tmp/gopath-for-cache/pkg/mod/cache/download would not contain the 'vcs' directory.
On Monday, March 11, 2019 at 10:01:04 AM UTC-7, Manlio Perillo wrote:Do you perhaps have the same requirements as in the threadas reported by Nicolas Mailhot?That is, you need to patch the upstream source but keep the same version, because you can't (or don't want to) update all the versions of the required modules.Not precisely. In my case, I'm doing the build in two stages a) fetch dependencies and make sure they pass the hash
and b) use (a) to build the module. I can add patches to the stage (a) to patch dependencies, but obviously, it does need some patching work due to the path of the dependency itself. I'm not too worried about patching at this time as I'm more worried about packaging instead.In this case vendoring is, IMHO, currently the only solution, because the go tool ignores the go.sum files in this case.You can try to open a bug report about the incorrect behavior of `go mod vendor` when using cgo.Or you can patch cmd/go. However instead of writing a patch specific to Nix, I suggest to write a more generic patch.
None of these patches seems to work if I remove the entire $GOPATH/pkg/mod/cache directory.
Again, I'm removing this entire directory because of both $GOPATH/pkg/mod/cache/vcs and $GOPATH/pkg/mod/cache/download seem to change whenever upstream has changed even though go.mod/go.sum are still on the same commit.
I do agree with you, however, that the correct solution is to rely on vendoring instead of patching the module support. I will report a bug in vendoring with regards to cgo.
On Monday, March 11, 2019 at 10:01:04 AM UTC-7, Manlio Perillo wrote:Do you perhaps have the same requirements as in the threadas reported by Nicolas Mailhot?That is, you need to patch the upstream source but keep the same version, because you can't (or don't want to) update all the versions of the required modules.Not precisely. In my case, I'm doing the build in two stages a) fetch dependencies and make sure they pass the hash and b) use (a) to build the module. I can add patches to the stage (a) to patch dependencies, but obviously, it does need some patching work due to the path of the dependency itself. I'm not too worried about patching at this time as I'm more worried about packaging instead.
Not precisely. In my case, I'm doing the build in two stages a) fetch dependencies and make sure they pass the hash and b) use (a) to build the module. I can add patches to the stage (a) to patch dependencies, but obviously, it does need some patching work due to the path of the dependency itself. I'm not too worried about patching at this time as I'm more worried about packaging instead.This is how I would do things in order to have a consistent snapshot of Go modules for an OS distribution:1) Clone each repository of the Go modules you want to include in thesnapshot, and all the indirect dependencies2) Patch all the go.mod files to ensure that *only* one versionof each module is used. Do not rely on cmd/go dependencyresolution algorithm3) Synthesize a Go module data for each repository, and makeit accessible from GOPROXY4) BuildNote that 2) will cause hash checks to fail; this is where -mod=trust came to help.
> [...]Manlio Perillo
I'm going to describe how I ended up packaging the go modules (and so far it seems to work correctly). I have also replied inline belowI'm using a two-phase approach to package Go modules for Nix:
- During the first phase, a package named after the module with the suffix -go-modules is built by running go mod download and saving only $GOPATH/pkg/mod/cache/download. The contents of this package are then hashed and compared against a fixed known hash. The build fails if the hash does not match. My only concern is with regards to the stability of $GOPATH/pkg/mod/cache/download, does it ever change given the exact same go.mod?
- The Go module is then built with $GOPROXY set to file://${go-modules} and allows Go to download the dependencies locally. No concerns during this step.
On Wednesday, March 13, 2019 at 2:31:27 PM UTC-7, Manlio Perillo wrote:Not precisely. In my case, I'm doing the build in two stages a) fetch dependencies and make sure they pass the hash and b) use (a) to build the module. I can add patches to the stage (a) to patch dependencies, but obviously, it does need some patching work due to the path of the dependency itself. I'm not too worried about patching at this time as I'm more worried about packaging instead.This is how I would do things in order to have a consistent snapshot of Go modules for an OS distribution:1) Clone each repository of the Go modules you want to include in thesnapshot, and all the indirect dependencies2) Patch all the go.mod files to ensure that *only* one versionof each module is used. Do not rely on cmd/go dependencyresolution algorithm3) Synthesize a Go module data for each repository, and makeit accessible from GOPROXY4) BuildNote that 2) will cause hash checks to fail; this is where -mod=trust came to help.What's wrong with using the Go toolchain to grab the dependencies with go mod download?