Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions get.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,10 +622,19 @@ func getPackage(ctx context.Context, logger *log.Logger, c installPackageConfig,
}

if !strings.HasSuffix(target.Module.Version, "+incompatible") {
fetchedDirectives, err = autoFetchDirectives(runnable, logger, target)
var declaredModulePath string
fetchedDirectives, declaredModulePath, err = autoFetchDirectives(runnable, logger, target)
if err != nil {
return err
}
if declaredModulePath != "" && declaredModulePath != target.Module.Path {
forkModule := target.Module
target.Module.Path = declaredModulePath
fetchedDirectives.replace = append(fetchedDirectives.replace, mod.ReplaceDirective{
Old: module.Version{Path: declaredModulePath},
New: forkModule,
})
}
}
}

Expand Down Expand Up @@ -722,10 +731,10 @@ func (d nonRequireDirectives) isEmpty() bool {
// as the target module we want to install.
// It's a very common case where modules mitigate faulty modules or conflicts with replace directives.
// Since we always download single tool dependency module per tool module, we can copy its non-require statements if exists to fix this common case.
func autoFetchDirectives(runnable runner.Runnable, logger *log.Logger, target bingo.Package) (d nonRequireDirectives, _ error) {
func autoFetchDirectives(runnable runner.Runnable, logger *log.Logger, target bingo.Package) (d nonRequireDirectives, declaredModulePath string, err error) {
gopath, err := runnable.GoEnv("GOPATH")
if err != nil {
return d, errors.Wrap(err, "go env")
return d, "", errors.Wrap(err, "go env")
}

// We leverage fact that when go get runs if downloads the version we find as relevant locally
Expand All @@ -734,16 +743,19 @@ func autoFetchDirectives(runnable runner.Runnable, logger *log.Logger, target bi
if _, err := os.Stat(targetModFile); err != nil {
if os.IsNotExist(err) {
// Pre module package.
return d, nil
return d, "", nil
}
return d, errors.Wrapf(err, "stat target mod directory %v", targetModFile)
return d, "", errors.Wrapf(err, "stat target mod directory %v", targetModFile)
}

// Mod directory has only read permissions.
targetModParsed, err := mod.OpenFileForRead(targetModFile)
if err != nil {
return d, errors.Wrapf(err, "parse target mod file %v", targetModFile)
return d, "", errors.Wrapf(err, "parse target mod file %v", targetModFile)
}
defer errcapture.Do(&err, targetModParsed.Close, "close target mod file")

declaredModulePath, _ = targetModParsed.Module()

if semver.MustParse(targetModParsed.GoVersion()).GreaterThan(runnable.GoVersion()) {
logger.Printf("WARNING: Go module you are trying to install requires higher Go version (%v) than you are using (%v). Use newer Go version to install it if you encounter build errors (e.g when generics were used).\n", targetModParsed.GoVersion(), runnable.GoVersion().String())
Expand All @@ -754,9 +766,9 @@ func autoFetchDirectives(runnable runner.Runnable, logger *log.Logger, target bi
d.retract = targetModParsed.RetractDirectives()

if len(d.retract) > 0 && runnable.GoVersion().LessThan(version.Go116) {
return d, errors.Newf("target Go module is using new 'retract' directive. Use Go1.16+ to build it")
return d, "", errors.Newf("target Go module is using new 'retract' directive. Use Go1.16+ to build it")
}
return d, nil
return d, declaredModulePath, nil
}

// gobin mimics the way go install finds where to install go tool.
Expand Down
32 changes: 32 additions & 0 deletions get_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,10 @@ func TestGet_ModuleCases(t *testing.T) {
}

t.Run("benchstat: latest in case where no major version is found", func(t *testing.T) {
if goVersion.LessThan(semver.MustParse("v1.25")) {
t.Skip("latest golang.org/x/perf requires Go 1.25")
}

g.Clear(t)

testutil.Ok(t, os.MkdirAll(filepath.Join(g.tmpDir, "newproject"), os.ModePerm))
Expand Down Expand Up @@ -538,6 +542,34 @@ func TestGet_ModuleCases(t *testing.T) {
}
})

t.Run("fork with a different declared module path", func(t *testing.T) {
g.Clear(t)

testutil.Ok(t, os.MkdirAll(filepath.Join(g.tmpDir, "newproject"), os.ModePerm))
p := newTestProject(t, filepath.Join(g.tmpDir, "newproject"), filepath.Join(g.tmpDir, "testproject"), false)
p.assertNotChanged(t)

bingoPath := filepath.Join(g.tmpDir, bingoBin)
buildInitialGobin(t, bingoPath)

const forkVersion = "v1.3.1-0.20220406082449-39bc1352dc62"
fmt.Println(g.ExecOutput(t, p.root, bingoPath, "get", "github.com/ypresto/CompileDaemon@"+forkVersion))

expectBingoListRows(t, []row{{
name: "compiledaemon",
binName: "compiledaemon-" + forkVersion,
pkgVersion: "github.com/githubnemo/CompileDaemon@" + forkVersion,
}}, g.ExecOutput(t, p.root, bingoPath, "list"))
testutil.Equals(t, []string{"compiledaemon-" + forkVersion}, g.existingBinaries(t))

modFile, err := os.ReadFile(filepath.Join(p.root, defaultModDir, "compiledaemon.mod"))
testutil.Ok(t, err)
testutil.Equals(t, true, strings.Contains(string(modFile),
"replace github.com/githubnemo/CompileDaemon => github.com/ypresto/CompileDaemon "+forkVersion))
testutil.Equals(t, true, strings.Contains(string(modFile),
"require github.com/githubnemo/CompileDaemon "+forkVersion))
})

// Tricky cases TODO.

// Regression against https://github.com/bwplotka/bingo/issues/125
Expand Down
Loading