mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-02-03 13:11:48 +02:00
feat: initial proxy build support (#2129)
* feat: allow to use ModulePath on templates Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * feat: initial proxy build support Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: build Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: main check Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: make it more flexible Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: small improvements Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: copy go.sum Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: root mod proxy Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: test Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: snapshots Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: lint Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: proxy main pkg Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: environment variables Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * test: added some tests to go mod proxy feature Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: improve test Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: linte Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: goreleaser.yml Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: simplify tests Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * test: test build Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: revert unwanted changes Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: allow to run when no mod.suym Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * docs: example Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * fix: not a go module on go 1.15 Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com> * docs: improve docs as per comments Signed-off-by: Carlos Alexandro Becker <caarlos0@gmail.com>
This commit is contained in:
parent
90f2ba6925
commit
8306b946d3
@ -4,6 +4,8 @@ before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
- ./scripts/completions.sh
|
||||
gomod:
|
||||
proxy: true
|
||||
builds:
|
||||
- env:
|
||||
- CGO_ENABLED=0
|
||||
|
@ -71,8 +71,10 @@ func (*Builder) WithDefaults(build config.Build) (config.Build, error) {
|
||||
|
||||
// Build builds a golang build.
|
||||
func (*Builder) Build(ctx *context.Context, build config.Build, options api.Options) error {
|
||||
if err := checkMain(build); err != nil {
|
||||
return err
|
||||
if !ctx.Config.GoMod.Proxy {
|
||||
if err := checkMain(build); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
target, err := newBuildTarget(options.Target)
|
||||
if err != nil {
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
@ -548,6 +549,47 @@ func TestRunPipeWithoutMainFunc(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunPipeWithProxiedRepo(t *testing.T) {
|
||||
folder := testlib.Mktmp(t)
|
||||
proxied := filepath.Join(folder, "dist/proxy/default")
|
||||
require.NoError(t, os.MkdirAll(proxied, 0o750))
|
||||
require.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(proxied, "main.go"),
|
||||
[]byte("// +build: main\npackage main\nimport github.com/goreleaser/goreleaser"),
|
||||
0o666,
|
||||
))
|
||||
require.NoError(t, ioutil.WriteFile(
|
||||
filepath.Join(proxied, "go.mod"),
|
||||
[]byte("module foo\nrequire github.com/goreleaser/goreleaser v0.161.1"),
|
||||
0o666,
|
||||
))
|
||||
cmd := exec.Command("go", "mod", "download")
|
||||
cmd.Dir = proxied
|
||||
require.NoError(t, cmd.Run())
|
||||
config := config.Project{
|
||||
GoMod: config.GoMod{
|
||||
Proxy: true,
|
||||
},
|
||||
Builds: []config.Build{
|
||||
{
|
||||
Binary: "foo",
|
||||
Hooks: config.HookConfig{},
|
||||
Main: "github.com/goreleaser/goreleaser",
|
||||
Dir: proxied,
|
||||
Targets: []string{
|
||||
runtimeTarget,
|
||||
},
|
||||
GoBinary: "go",
|
||||
},
|
||||
},
|
||||
}
|
||||
ctx := context.New(config)
|
||||
|
||||
require.NoError(t, Default.Build(ctx, ctx.Config.Builds[0], api.Options{
|
||||
Target: runtimeTarget,
|
||||
}))
|
||||
}
|
||||
|
||||
func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) {
|
||||
folder := testlib.Mktmp(t)
|
||||
require.NoError(t, ioutil.WriteFile(
|
||||
|
@ -1,14 +1,29 @@
|
||||
// Package gomod provides go modules utilities, such as template variables and the ability to proxy the module from
|
||||
// proxy.golang.org.
|
||||
package gomod
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe"
|
||||
"github.com/goreleaser/goreleaser/internal/tmpl"
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
)
|
||||
|
||||
const (
|
||||
go115NotAGoModuleError = "go list -m: not using modules"
|
||||
go116NotAGoModuleError = "command-line-arguments"
|
||||
)
|
||||
|
||||
// Pipe for env.
|
||||
type Pipe struct{}
|
||||
|
||||
@ -16,14 +31,17 @@ func (Pipe) String() string {
|
||||
return "loading go mod information"
|
||||
}
|
||||
|
||||
const (
|
||||
go115NotAGoModuleError = "go list -m: not using modules"
|
||||
go116NotAGoModuleError = "command-line-arguments"
|
||||
)
|
||||
// Default sets the pipe defaults.
|
||||
func (Pipe) Default(ctx *context.Context) error {
|
||||
if ctx.Config.GoMod.GoBinary == "" {
|
||||
ctx.Config.GoMod.GoBinary = "go"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run the pipe.
|
||||
func (Pipe) Run(ctx *context.Context) error {
|
||||
out, err := exec.CommandContext(ctx, "go", "list", "-m").CombinedOutput()
|
||||
out, err := exec.CommandContext(ctx, ctx.Config.GoMod.GoBinary, "list", "-m").CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get module path: %w: %s", err, string(out))
|
||||
}
|
||||
@ -35,5 +53,139 @@ func (Pipe) Run(ctx *context.Context) error {
|
||||
|
||||
ctx.ModulePath = result
|
||||
|
||||
if !ctx.Config.GoMod.Proxy {
|
||||
return pipe.Skip("gomod.proxy is disabled")
|
||||
}
|
||||
|
||||
if ctx.Snapshot {
|
||||
return pipe.ErrSnapshotEnabled
|
||||
}
|
||||
|
||||
return setupProxy(ctx)
|
||||
}
|
||||
|
||||
func setupProxy(ctx *context.Context) error {
|
||||
for i := range ctx.Config.Builds {
|
||||
build := &ctx.Config.Builds[i]
|
||||
if err := proxyBuild(ctx, build); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const goModTpl = `
|
||||
module {{ .BuildID }}
|
||||
|
||||
require {{ .ModulePath }} {{ .Tag }}
|
||||
`
|
||||
|
||||
const mainGoTpl = `
|
||||
// +build main
|
||||
package main
|
||||
|
||||
import _ "{{ .Main }}"
|
||||
`
|
||||
|
||||
// ErrProxy happens when something goes wrong while proxying the current go module.
|
||||
type ErrProxy struct {
|
||||
err error
|
||||
details string
|
||||
}
|
||||
|
||||
func newErrProxy(err error) error {
|
||||
return ErrProxy{
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func newDetailedErrProxy(err error, details string) error {
|
||||
return ErrProxy{
|
||||
err: err,
|
||||
details: details,
|
||||
}
|
||||
}
|
||||
|
||||
func (e ErrProxy) Error() string {
|
||||
out := fmt.Sprintf("failed to proxy module: %v", e.err)
|
||||
if e.details != "" {
|
||||
return fmt.Sprintf("%s: %s", out, e.details)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (e ErrProxy) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func proxyBuild(ctx *context.Context, build *config.Build) error {
|
||||
mainPackage := path.Join(ctx.ModulePath, build.Main)
|
||||
template := tmpl.New(ctx).WithExtraFields(tmpl.Fields{
|
||||
"Main": mainPackage,
|
||||
"BuildID": build.ID,
|
||||
})
|
||||
|
||||
log.Infof("proxying %s@%s to build %s", ctx.ModulePath, ctx.Git.CurrentTag, mainPackage)
|
||||
|
||||
mod, err := template.Apply(goModTpl)
|
||||
if err != nil {
|
||||
return newErrProxy(err)
|
||||
}
|
||||
|
||||
main, err := template.Apply(mainGoTpl)
|
||||
if err != nil {
|
||||
return newErrProxy(err)
|
||||
}
|
||||
|
||||
dir := filepath.Join(ctx.Config.Dist, "proxy", build.ID)
|
||||
|
||||
log.Debugf("creating needed files")
|
||||
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return newErrProxy(err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filepath.Join(dir, "main.go"), []byte(main), 0o666); err != nil {
|
||||
return newErrProxy(err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte(mod), 0o666); err != nil {
|
||||
return newErrProxy(err)
|
||||
}
|
||||
|
||||
if err := copyGoSum("go.sum", filepath.Join(dir, "go.sum")); err != nil {
|
||||
return newErrProxy(err)
|
||||
}
|
||||
|
||||
log.Debugf("tidying")
|
||||
cmd := exec.CommandContext(ctx, ctx.Config.GoMod.GoBinary, "mod", "tidy")
|
||||
cmd.Dir = dir
|
||||
cmd.Env = append(ctx.Config.GoMod.Env, os.Environ()...)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return newDetailedErrProxy(err, string(out))
|
||||
}
|
||||
|
||||
build.Main = mainPackage
|
||||
build.Dir = dir
|
||||
return nil
|
||||
}
|
||||
|
||||
func copyGoSum(src, dst string) error {
|
||||
r, err := os.OpenFile(src, os.O_RDONLY, 0o666)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
w, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
_, err = io.Copy(w, r)
|
||||
return err
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
package gomod
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/goreleaser/goreleaser/internal/testlib"
|
||||
@ -12,25 +15,211 @@ import (
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
ctx := context.New(config.Project{})
|
||||
require.NoError(t, Pipe{}.Run(ctx))
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
testlib.AssertSkipped(t, Pipe{}.Run(ctx))
|
||||
require.Equal(t, "github.com/goreleaser/goreleaser", ctx.ModulePath)
|
||||
}
|
||||
|
||||
func TestRunSnapshot(t *testing.T) {
|
||||
ctx := context.New(config.Project{
|
||||
GoMod: config.GoMod{
|
||||
Proxy: true,
|
||||
},
|
||||
})
|
||||
ctx.Snapshot = true
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
testlib.AssertSkipped(t, Pipe{}.Run(ctx))
|
||||
require.Equal(t, "github.com/goreleaser/goreleaser", ctx.ModulePath)
|
||||
}
|
||||
|
||||
func TestRunOutsideGoModule(t *testing.T) {
|
||||
require.NoError(t, os.Chdir(t.TempDir()))
|
||||
dir := testlib.Mktmp(t)
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\nfunc main() {println(0)}"), 0o666))
|
||||
ctx := context.New(config.Project{})
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
testlib.AssertSkipped(t, Pipe{}.Run(ctx))
|
||||
require.Empty(t, ctx.ModulePath)
|
||||
}
|
||||
|
||||
func TestRunCommandError(t *testing.T) {
|
||||
os.Unsetenv("PATH")
|
||||
require.NoError(t, os.Chdir(t.TempDir()))
|
||||
ctx := context.New(config.Project{})
|
||||
require.EqualError(t, Pipe{}.Run(ctx), "failed to get module path: exec: \"go\": executable file not found in $PATH: ")
|
||||
ctx := context.New(config.Project{
|
||||
GoMod: config.GoMod{
|
||||
GoBinary: "not-a-valid-binary",
|
||||
},
|
||||
})
|
||||
require.EqualError(t, Pipe{}.Run(ctx), "failed to get module path: exec: \"not-a-valid-binary\": executable file not found in $PATH: ")
|
||||
require.Empty(t, ctx.ModulePath)
|
||||
}
|
||||
|
||||
func TestDescription(t *testing.T) {
|
||||
require.NotEmpty(t, Pipe{}.String())
|
||||
}
|
||||
|
||||
func TestGoModProxy(t *testing.T) {
|
||||
t.Run("goreleaser", func(t *testing.T) {
|
||||
dir := testlib.Mktmp(t)
|
||||
dist := filepath.Join(dir, "dist")
|
||||
ctx := context.New(config.Project{
|
||||
Dist: dist,
|
||||
GoMod: config.GoMod{
|
||||
Proxy: true,
|
||||
},
|
||||
Builds: []config.Build{
|
||||
{
|
||||
ID: "foo",
|
||||
Goos: []string{runtime.GOOS},
|
||||
Goarch: []string{runtime.GOARCH},
|
||||
Main: ".",
|
||||
},
|
||||
},
|
||||
})
|
||||
ctx.Git.CurrentTag = "v0.161.1"
|
||||
|
||||
mod := "github.com/goreleaser/goreleaser"
|
||||
|
||||
fakeGoModAndSum(t, mod)
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.NoError(t, Pipe{}.Run(ctx))
|
||||
requireGoMod(t, mod, ctx.Git.CurrentTag)
|
||||
requireMainGo(t, mod)
|
||||
require.Equal(t, mod, ctx.Config.Builds[0].Main)
|
||||
require.Equal(t, filepath.Join(dist, "proxy", "foo"), ctx.Config.Builds[0].Dir)
|
||||
require.Equal(t, mod, ctx.ModulePath)
|
||||
})
|
||||
|
||||
t.Run("nfpm", func(t *testing.T) {
|
||||
dir := testlib.Mktmp(t)
|
||||
dist := filepath.Join(dir, "dist")
|
||||
ctx := context.New(config.Project{
|
||||
Dist: dist,
|
||||
GoMod: config.GoMod{
|
||||
Proxy: true,
|
||||
},
|
||||
Builds: []config.Build{
|
||||
{
|
||||
ID: "foo",
|
||||
Goos: []string{runtime.GOOS},
|
||||
Goarch: []string{runtime.GOARCH},
|
||||
Main: "./cmd/nfpm",
|
||||
},
|
||||
},
|
||||
})
|
||||
ctx.Git.CurrentTag = "v2.3.1"
|
||||
|
||||
mod := "github.com/goreleaser/nfpm/v2"
|
||||
fakeGoModAndSum(t, mod)
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.NoError(t, Pipe{}.Run(ctx))
|
||||
requireGoMod(t, mod, ctx.Git.CurrentTag)
|
||||
requireMainGo(t, mod+"/cmd/nfpm")
|
||||
require.Equal(t, mod+"/cmd/nfpm", ctx.Config.Builds[0].Main)
|
||||
require.Equal(t, filepath.Join(dist, "proxy", "foo"), ctx.Config.Builds[0].Dir)
|
||||
require.Equal(t, mod, ctx.ModulePath)
|
||||
})
|
||||
|
||||
// this repo does not have a go.sum file, which is ok, a project might not have any dependencies
|
||||
t.Run("no go.sum", func(t *testing.T) {
|
||||
dir := testlib.Mktmp(t)
|
||||
dist := filepath.Join(dir, "dist")
|
||||
ctx := context.New(config.Project{
|
||||
Dist: dist,
|
||||
GoMod: config.GoMod{
|
||||
Proxy: true,
|
||||
},
|
||||
Builds: []config.Build{
|
||||
{
|
||||
ID: "foo",
|
||||
Goos: []string{runtime.GOOS},
|
||||
Goarch: []string{runtime.GOARCH},
|
||||
},
|
||||
},
|
||||
})
|
||||
ctx.Git.CurrentTag = "v0.0.1"
|
||||
|
||||
mod := "github.com/goreleaser/example-mod-proxy"
|
||||
fakeGoMod(t, mod)
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.NoError(t, Pipe{}.Run(ctx))
|
||||
requireGoMod(t, mod, ctx.Git.CurrentTag)
|
||||
requireMainGo(t, mod)
|
||||
require.Equal(t, mod, ctx.Config.Builds[0].Main)
|
||||
require.Equal(t, filepath.Join(dist, "proxy", "foo"), ctx.Config.Builds[0].Dir)
|
||||
require.Equal(t, mod, ctx.ModulePath)
|
||||
})
|
||||
|
||||
t.Run("no perms", func(t *testing.T) {
|
||||
for file, mode := range map[string]os.FileMode{
|
||||
"go.mod": 0o500,
|
||||
"go.sum": 0o500,
|
||||
"main.go": 0o500,
|
||||
"../../../go.sum": 0o300,
|
||||
} {
|
||||
t.Run(file, func(t *testing.T) {
|
||||
dir := testlib.Mktmp(t)
|
||||
dist := filepath.Join(dir, "dist")
|
||||
ctx := context.New(config.Project{
|
||||
Dist: dist,
|
||||
GoMod: config.GoMod{
|
||||
Proxy: true,
|
||||
},
|
||||
Builds: []config.Build{
|
||||
{
|
||||
ID: "foo",
|
||||
Goos: []string{runtime.GOOS},
|
||||
Goarch: []string{runtime.GOARCH},
|
||||
},
|
||||
},
|
||||
})
|
||||
ctx.Git.CurrentTag = "v0.161.1"
|
||||
|
||||
mod := "github.com/goreleaser/goreleaser"
|
||||
|
||||
fakeGoModAndSum(t, mod)
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.NoError(t, Pipe{}.Run(ctx)) // should succeed at first
|
||||
|
||||
// change perms of a file and run again, which should now fail on that file.
|
||||
require.NoError(t, os.Chmod(filepath.Join(dist, "proxy", "foo", file), mode))
|
||||
require.ErrorAs(t, Pipe{}.Run(ctx), &ErrProxy{})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func requireGoMod(tb testing.TB, module, version string) {
|
||||
tb.Helper()
|
||||
|
||||
mod, err := os.ReadFile("dist/proxy/foo/go.mod")
|
||||
require.NoError(tb, err)
|
||||
require.Equal(tb, fmt.Sprintf(`module foo
|
||||
|
||||
go 1.16
|
||||
|
||||
require %s %s
|
||||
`, module, version), string(mod))
|
||||
}
|
||||
|
||||
func requireMainGo(tb testing.TB, module string) {
|
||||
tb.Helper()
|
||||
|
||||
main, err := os.ReadFile("dist/proxy/foo/main.go")
|
||||
require.NoError(tb, err)
|
||||
require.Equal(tb, fmt.Sprintf(`
|
||||
// +build main
|
||||
package main
|
||||
|
||||
import _ "%s"
|
||||
`, module), string(main))
|
||||
}
|
||||
|
||||
func fakeGoModAndSum(tb testing.TB, module string) {
|
||||
tb.Helper()
|
||||
|
||||
fakeGoMod(tb, module)
|
||||
require.NoError(tb, os.WriteFile("go.sum", []byte("\n"), 0o666))
|
||||
}
|
||||
|
||||
func fakeGoMod(tb testing.TB, module string) {
|
||||
tb.Helper()
|
||||
require.NoError(tb, os.WriteFile("go.mod", []byte(fmt.Sprintf("module %s\n", module)), 0o666))
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ type Piper interface {
|
||||
// BuildPipeline contains all build-related pipe implementations in order.
|
||||
// nolint:gochecknoglobals
|
||||
var BuildPipeline = []Piper{
|
||||
gomod.Pipe{}, // setup gomod-related stuff
|
||||
env.Pipe{}, // load and validate environment variables
|
||||
git.Pipe{}, // get and validate git repo state
|
||||
semver.Pipe{}, // parse current tag to a semver
|
||||
@ -46,6 +45,7 @@ var BuildPipeline = []Piper{
|
||||
defaults.Pipe{}, // load default configs
|
||||
snapshot.Pipe{}, // snapshot version handling
|
||||
dist.Pipe{}, // ensure ./dist is clean
|
||||
gomod.Pipe{}, // setup gomod-related stuff
|
||||
effectiveconfig.Pipe{}, // writes the actual config (with defaults et al set) to dist
|
||||
changelog.Pipe{}, // builds the release changelog
|
||||
build.Pipe{}, // build
|
||||
|
4
main.go
4
main.go
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/goreleaser/goreleaser/cmd"
|
||||
)
|
||||
@ -34,5 +35,8 @@ func buildVersion(version, commit, date, builtBy string) string {
|
||||
if builtBy != "" {
|
||||
result = fmt.Sprintf("%s\nbuilt by: %s", result, builtBy)
|
||||
}
|
||||
if info, ok := debug.ReadBuildInfo(); ok && info.Main.Sum != "" {
|
||||
result = fmt.Sprintf("%s\nmodule version: %s, checksum: %s", result, info.Main.Version, info.Main.Sum)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -610,6 +610,7 @@ type Project struct {
|
||||
EnvFiles EnvFiles `yaml:"env_files,omitempty"`
|
||||
Before Before `yaml:",omitempty"`
|
||||
Source Source `yaml:",omitempty"`
|
||||
GoMod GoMod `yaml:"gomod,omitempty"`
|
||||
|
||||
// this is a hack ¯\_(ツ)_/¯
|
||||
SingleBuild Build `yaml:"build,omitempty"`
|
||||
@ -624,6 +625,12 @@ type Project struct {
|
||||
GiteaURLs GiteaURLs `yaml:"gitea_urls,omitempty"`
|
||||
}
|
||||
|
||||
type GoMod struct {
|
||||
Proxy bool `yaml:",omitempty"`
|
||||
Env []string `yaml:",omitempty"`
|
||||
GoBinary string `yaml:",omitempty"`
|
||||
}
|
||||
|
||||
// Load config file.
|
||||
func Load(file string) (config Project, err error) {
|
||||
f, err := os.Open(file) // #nosec
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/build"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/checksums"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/docker"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/gomod"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/milestone"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/nfpm"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/project"
|
||||
@ -39,6 +40,7 @@ var Defaulters = []Defaulter{
|
||||
snapshot.Pipe{},
|
||||
release.Pipe{},
|
||||
project.Pipe{},
|
||||
gomod.Pipe{},
|
||||
build.Pipe{},
|
||||
sourcearchive.Pipe{},
|
||||
archive.Pipe{},
|
||||
|
85
www/docs/cookbooks/build-go-modules.md
Normal file
85
www/docs/cookbooks/build-go-modules.md
Normal file
@ -0,0 +1,85 @@
|
||||
# Building Go modules
|
||||
|
||||
With the default configs, you can already build a Go module without issues.
|
||||
|
||||
But, if you want to access module information in runtime (e.g. `debug.BuildInfo` or `go version -m $binary`), you'll
|
||||
need to setup GoReleaser to "proxy" that module before building it.
|
||||
|
||||
To do that, you can simply add this to your config:
|
||||
|
||||
```yaml
|
||||
# goreleaser.yml
|
||||
gomod:
|
||||
proxy: true
|
||||
```
|
||||
|
||||
In practice, what this does is:
|
||||
|
||||
- for each of your builds, create a `dist/proxy/{{ build.id }}`;
|
||||
- creates a `go.mod` that requires your __main module__ at the __current tag__;
|
||||
- creates a `main.go` that imports your __main package__;
|
||||
- copy the project's `go.sum` to that folder.
|
||||
|
||||
In which:
|
||||
|
||||
- __build.id__: the `id` property in your `build` definition;
|
||||
- __main module__: is the output of `go list -m`;
|
||||
- __main package__: is the __main module__ + your build's `main`;
|
||||
- __current tag__: is the tag that is being built.
|
||||
|
||||
So, let's say:
|
||||
|
||||
- __main module__: `github.com/goreleaser/nfpm/v2`;
|
||||
- build's `main`: `./cmd/nfpm/`;
|
||||
- __current tag__: `v2.5.0`.
|
||||
|
||||
GoReleaser will create a `main.go` like:
|
||||
|
||||
```go
|
||||
// +build: main
|
||||
package main
|
||||
|
||||
import _ "github.com/goreleaser/nfpm/v2/cmd/nfpm"
|
||||
```
|
||||
|
||||
a `go.mod` like:
|
||||
|
||||
```
|
||||
module nfpm
|
||||
|
||||
require github.com/goreleaser/nfpm/v2 v2.5.0
|
||||
```
|
||||
|
||||
Then, it'll run:
|
||||
|
||||
```sh
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
And, to build, it will use something like:
|
||||
|
||||
```shell
|
||||
go build -o nfpm github.com/goreleaser/nfpm/v2/cmd/nfpm
|
||||
```
|
||||
|
||||
This will resolve the source code from the defined module proxy using `proxy.golang.org`.
|
||||
Your project's `go.sum` will be used to verify any modules that are downloaded, with `sum.golang.org` "filling in" any gaps.
|
||||
|
||||
## Limitations
|
||||
|
||||
1. Extra files will still be copied from the current project's root folder and not from the proxy cache;
|
||||
1. You can't build packages that are not contained in the main module.
|
||||
|
||||
## More information
|
||||
|
||||
You can find more information about it on the [issue][issue] that originated it and its subsequent [pull request][pr].
|
||||
|
||||
Make sure to also read the [relevant documentation][docs] for more options.
|
||||
|
||||
[issue]: https://github.com/goreleaser/goreleaser/issues/1354
|
||||
[pr]: https://github.com/goreleaser/goreleaser/pull/2129
|
||||
[docs]: /customization/gomod/
|
||||
|
||||
## Real example
|
||||
|
||||
Source code of a working example can be found at [goreleaser/example-mod-proxy](https://github.com/goreleaser/example-mod-proxy).
|
37
www/docs/customization/gomod.md
Normal file
37
www/docs/customization/gomod.md
Normal file
@ -0,0 +1,37 @@
|
||||
---
|
||||
title: Go Modules
|
||||
---
|
||||
|
||||
GoReleaser has support for creating verifiable builds.
|
||||
A [verifiable build][vgo] is one that records enough information to be precise about exactly how to repeat it.
|
||||
All dependencies are loaded via `proxy.golang.org`, and verified against the checksum database `sum.golang.org`.
|
||||
A GoReleaser-created verifiable build will include module information in the resulting binary, which can be printed using `go version -m mybinary`.
|
||||
|
||||
Configuration options available are described bellow.
|
||||
|
||||
```yaml
|
||||
# goreleaser.yml
|
||||
|
||||
gomod:
|
||||
# Proxy a module from proxy.golang.org, making the builds verifiable.
|
||||
# This will only be effective if running against a tag. Snapshots will ignore this setting.
|
||||
#
|
||||
# Default is false.
|
||||
proxy: true
|
||||
|
||||
# If proxy is true, use these environment variables when running `go mod` commands (namely, `go mod tidy`).
|
||||
# Defaults to `os.Environ()`.
|
||||
env:
|
||||
- GOPROXY=https://proxy.golang.org,direct
|
||||
- GOSUMDB=sum.golang.org
|
||||
- GOPRIVATE=example.com/blah
|
||||
|
||||
# Which Go binary to use.
|
||||
# Defaults to `go`.
|
||||
gobinary: go1.15
|
||||
```
|
||||
|
||||
!!! tip
|
||||
You can use `debug.ReadBuildInfo()` to get the version/checksum/dependencies of the module.
|
||||
|
||||
[vgo]: https://research.swtch.com/vgo-repro
|
@ -62,6 +62,7 @@ nav:
|
||||
- customization/artifactory.md
|
||||
- customization/bintray.md
|
||||
- customization/blob.md
|
||||
- customization/gomod.md
|
||||
- customization/build.md
|
||||
- customization/checksum.md
|
||||
- customization/publishers.md
|
||||
@ -87,6 +88,7 @@ nav:
|
||||
- Cookbooks:
|
||||
- About: cookbooks/index.md
|
||||
- Blog Posts: cookbooks/blog-posts.md
|
||||
- cookbooks//build-go-modules.md
|
||||
- cookbooks/semantic-release.md
|
||||
- cookbooks/release-a-library.md
|
||||
- cookbooks/publish-to-nexus.md
|
||||
|
Loading…
x
Reference in New Issue
Block a user