diff --git a/internal/pipe/gomod/gomod_proxy.go b/internal/pipe/gomod/gomod_proxy.go index c27355727d8..59897ea7dc2 100644 --- a/internal/pipe/gomod/gomod_proxy.go +++ b/internal/pipe/gomod/gomod_proxy.go @@ -10,14 +10,64 @@ import ( "os/exec" "path" "path/filepath" + "regexp" "strings" "github.com/caarlos0/log" + "github.com/goreleaser/goreleaser/internal/logext" "github.com/goreleaser/goreleaser/internal/tmpl" "github.com/goreleaser/goreleaser/pkg/config" "github.com/goreleaser/goreleaser/pkg/context" ) +// ErrReplaceWithProxy happens when the configuration has gomod.proxy enabled, +// and the go.mod file contains replace directives. +// +// Replaces does not work with proxying, nor with go installs, +// and are made for development only. +var ErrReplaceWithProxy = errors.New("cannot use the go.mod replace directive with go mod proxy enabled") + +type CheckGoModPipe struct{} + +func (CheckGoModPipe) String() string { return "checking go.mod" } +func (CheckGoModPipe) Skip(ctx *context.Context) bool { + return ctx.ModulePath == "" || !ctx.Config.GoMod.Proxy +} + +var replaceRe = regexp.MustCompile("^replace .* => .*$") + +// Run the ReplaceCheckPipe. +func (CheckGoModPipe) Run(ctx *context.Context) error { + for i := range ctx.Config.Builds { + build := &ctx.Config.Builds[i] + path := filepath.Join(build.UnproxiedDir, "go.mod") + mod, err := os.ReadFile(path) + if err != nil { + log.Errorf("could not check %q", path) + return nil + } + for _, line := range strings.Split(string(mod), "\n") { + if !replaceRe.MatchString(line) { + continue + } + log.Warnf( + "your %[2]s file has %[1]s directive in it, and go mod proxying is enabled - "+ + "this does not work, and you need to either disable it or remove the %[1]s directive", + logext.Keyword("replace"), + logext.Keyword("go.mod"), + ) + log.Warnf("the offending line is %s", logext.Keyword(strings.TrimSpace(line))) + if ctx.Snapshot { + // only warn on snapshots + break + } + return ErrReplaceWithProxy + } + } + + return nil +} + // ProxyPipe for gomod proxy. type ProxyPipe struct{} diff --git a/internal/pipe/gomod/gomod_proxy_test.go b/internal/pipe/gomod/gomod_proxy_test.go index 0b93df607b8..0417562bfd3 100644 --- a/internal/pipe/gomod/gomod_proxy_test.go +++ b/internal/pipe/gomod/gomod_proxy_test.go @@ -3,6 +3,7 @@ package gomod import ( "fmt" "os" + "os/exec" "path/filepath" "runtime" "syscall" @@ -15,6 +16,62 @@ import ( "github.com/stretchr/testify/require" ) +func TestString(t *testing.T) { + require.NotEmpty(t, CheckGoModPipe{}.String()) + require.NotEmpty(t, ProxyPipe{}.String()) +} + +func TestCheckGoMod(t *testing.T) { + t.Run("replace on snapshot", func(t *testing.T) { + dir := testlib.Mktmp(t) + dist := filepath.Join(dir, "dist") + ctx := testctx.NewWithCfg(config.Project{ + Dist: dist, + GoMod: config.GoMod{ + Proxy: true, + GoBinary: "go", + }, + Builds: []config.Build{ + { + ID: "foo", + Goos: []string{runtime.GOOS}, + Goarch: []string{runtime.GOARCH}, + Main: ".", + Dir: ".", + }, + }, + }, testctx.Snapshot, withGoReleaserModulePath) + + fakeGoModAndSum(t, ctx.ModulePath) + require.NoError(t, exec.Command("go", "mod", "edit", "-replace", "foo=../bar").Run()) + require.NoError(t, CheckGoModPipe{}.Run(ctx)) + }) + t.Run("replace", func(t *testing.T) { + dir := testlib.Mktmp(t) + dist := filepath.Join(dir, "dist") + ctx := testctx.NewWithCfg(config.Project{ + Dist: dist, + GoMod: config.GoMod{ + Proxy: true, + GoBinary: "go", + }, + Builds: []config.Build{ + { + ID: "foo", + Goos: []string{runtime.GOOS}, + Goarch: []string{runtime.GOARCH}, + Main: ".", + Dir: ".", + }, + }, + }, withGoReleaserModulePath) + + fakeGoModAndSum(t, ctx.ModulePath) + require.NoError(t, exec.Command("go", "mod", "edit", "-replace", "foo=../bar").Run()) + require.ErrorIs(t, CheckGoModPipe{}.Run(ctx), ErrReplaceWithProxy) + }) +} + func TestGoModProxy(t *testing.T) { t.Run("goreleaser", func(t *testing.T) { dir := testlib.Mktmp(t) @@ -167,7 +224,9 @@ func TestProxyDescription(t *testing.T) { func TestSkip(t *testing.T) { t.Run("skip false gomod.proxy", func(t *testing.T) { - require.True(t, ProxyPipe{}.Skip(testctx.New())) + ctx := testctx.New() + require.True(t, ProxyPipe{}.Skip(ctx)) + require.True(t, CheckGoModPipe{}.Skip(ctx)) }) t.Run("skip snapshot", func(t *testing.T) { @@ -177,6 +236,7 @@ func TestSkip(t *testing.T) { }, }, withGoReleaserModulePath, testctx.Snapshot) require.True(t, ProxyPipe{}.Skip(ctx)) + require.False(t, CheckGoModPipe{}.Skip(ctx)) }) t.Run("skip not a go module", func(t *testing.T) { @@ -186,6 +246,7 @@ func TestSkip(t *testing.T) { }, }, func(ctx *context.Context) { ctx.ModulePath = "" }) require.True(t, ProxyPipe{}.Skip(ctx)) + require.True(t, CheckGoModPipe{}.Skip(ctx)) }) t.Run("dont skip", func(t *testing.T) { @@ -195,6 +256,7 @@ func TestSkip(t *testing.T) { }, }, withGoReleaserModulePath) require.False(t, ProxyPipe{}.Skip(ctx)) + require.False(t, CheckGoModPipe{}.Skip(ctx)) }) } diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go index d81e358aa8e..d8c9d7991b8 100644 --- a/internal/pipeline/pipeline.go +++ b/internal/pipeline/pipeline.go @@ -70,6 +70,8 @@ var BuildPipeline = []Piper{ // run prebuild stuff prebuild.Pipe{}, // proxy gomod if needed + gomod.CheckGoModPipe{}, + // proxy gomod if needed gomod.ProxyPipe{}, // writes the actual config (with defaults et al set) to dist effectiveconfig.Pipe{},