Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for pinning build flags and environment variables. #82

Merged
merged 1 commit into from
May 13, 2021
Merged
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
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,23 @@ We use *breaking* word for marking changes that are not backward compatible (rel

## (Under development)

## [v0.4.1](https://github.com/bwplotka/bingo/releases/tag/v0.4.1) - 2021.05.12

### Added

* Added support for build flags and envs via go.mod file.

### Fixed

* Generated files have limited permission.
* Support for Go pre-released versions.


## [v0.4.0](https://github.com/bwplotka/bingo/releases/tag/v0.4.0) - 2021.03.24

* Added support for Go 1.16, following the changes it introduces in the module system: https://blog.golang.org/go116-module-changes
### Added

* Added support for Go 1.16, following the changes it introduces in the module system: https://blog.golang.org/go116-module-changes.

## [v0.3.1](https://github.com/bwplotka/bingo/releases/tag/v0.3.1) - 2021.02.02

Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,25 @@ Let's show a few, real, sometimes novel examples showcasing `bingo` capabilities
${GOBIN}/thanos-v0.17.2 --help
```

## Advanced Techniques

* Using advanced go build flags and environment variables.

To tell bingo to use certain env vars and tags during build time, just add them as a comment to the go.mod file manually and do
`bingo get`. Done!

NOTE: Order of comment matters. First bingo expects relative package name (optional), then environment variables, then flags. All space delimited.

Real example from production project that relies on extended Hugo.

```
module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT

go 1.16

require github.com/gohugoio/hugo v0.83.1 // CGO_ENABLED=1 -tags=extended
```

## Production Usage

To see production example see:
Expand Down
19 changes: 12 additions & 7 deletions get.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ func get(ctx context.Context, logger *log.Logger, c getConfig, rawTarget string)
target.Module.Version = mf.DirectPackage().Module.Version
}
target.RelPath = mf.DirectPackage().RelPath
target.BuildFlags = mf.DirectPackage().BuildFlags
target.BuildEnvs = mf.DirectPackage().BuildEnvs

// Save for future versions without potentially existing files.
pkgPath = target.Path()
Expand Down Expand Up @@ -530,6 +532,7 @@ func getPackage(ctx context.Context, logger *log.Logger, c installPackageConfig,
if c.verbose {
logger.Println("getting target", target.String(), "(module", target.Module.Path, ")")
}

// The out module file we generate/maintain keep in modDir.
outModFile := filepath.Join(c.modDir, name+".mod")
tmpEmptyModFilePath := filepath.Join(c.modDir, name+"-e.tmp.mod")
Expand All @@ -551,7 +554,7 @@ func getPackage(ctx context.Context, logger *log.Logger, c installPackageConfig,
}
defer errcapture.Do(&err, tmpEmptyModFile.Close, "close")

runnable := c.runner.With(ctx, tmpEmptyModFile.FileName(), c.modDir)
runnable := c.runner.With(ctx, tmpEmptyModFile.FileName(), c.modDir, nil)
if err := resolvePackage(logger, c.verbose, tmpEmptyModFile.FileName(), runnable, c.update, &target); err != nil {
return err
}
Expand Down Expand Up @@ -588,8 +591,7 @@ func getPackage(ctx context.Context, logger *log.Logger, c installPackageConfig,
return err
}

runnable := c.runner.With(ctx, tmpModFile.FileName(), c.modDir)
if err := install(runnable, name, c.link, tmpModFile.DirectPackage()); err != nil {
if err := install(ctx, c.runner, c.modDir, name, c.link, tmpModFile); err != nil {
return errors.Wrap(err, "install")
}

Expand Down Expand Up @@ -642,7 +644,6 @@ func autoFetchReplaceStatements(runnable runner.Runnable, target bingo.Package)
return nil, errors.Wrapf(err, "parse target mod file %v", targetModFile)
}
return targetModParsed.Replace, nil

}

// gobin mimics the way go install finds where to install go tool.
Expand All @@ -654,15 +655,19 @@ func gobin() string {
return binPath
}

func install(runnable runner.Runnable, name string, link bool, pkg *bingo.Package) (err error) {
func install(ctx context.Context, r *runner.Runner, modDir string, name string, link bool, modFile *bingo.ModFile) (err error) {
pkg := modFile.DirectPackage()
if err := validateTargetName(name); err != nil {
return errors.Wrap(err, pkg.String())
}

// Two purposes of doing list with mod=mod:
// * Check if path is pointing to non-buildable package.
// * Rebuild go.sum and go.mod (tidy) which is required to build with -mod=readonly (default) to work.
if listOutput, err := runnable.List(runner.NoUpdatePolicy, "-mod=mod", "-f={{.Name}}", pkg.Path()); err != nil {
var listArgs []string
listArgs = append(listArgs, modFile.DirectPackage().BuildFlags...)
listArgs = append(listArgs, "-mod=mod", "-f={{.Name}}", pkg.Path())
if listOutput, err := r.With(ctx, modFile.FileName(), modDir, nil).List(runner.NoUpdatePolicy, listArgs...); err != nil {
return errors.Wrap(err, "list")
} else if !strings.HasSuffix(listOutput, "main") {
return errors.Errorf("package %s is non-main (go list output %q), nothing to get and build", pkg.Path(), listOutput)
Expand All @@ -672,7 +677,7 @@ func install(runnable runner.Runnable, name string, link bool, pkg *bingo.Packag

// go install does not define -modfile flag so so we mimic go install with go build -o instead.
binPath := filepath.Join(gobin, fmt.Sprintf("%s-%s", name, pkg.Module.Version))
if err := runnable.Build(pkg.Path(), binPath); err != nil {
if err := r.With(ctx, modFile.FileName(), modDir, pkg.BuildEnvs).Build(pkg.Path(), binPath, pkg.BuildFlags...); err != nil {
return errors.Wrap(err, "build versioned")
}

Expand Down
6 changes: 3 additions & 3 deletions get_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,7 +702,7 @@ func TestGet(t *testing.T) {
},
expectRows: []row{
// TODO(bwplotka) This will be painful to maintain, but well... improve it
{name: "thanos", binName: "thanos-v0.19.0", pkgVersion: "github.com/thanos-io/thanos/cmd/thanos@v0.19.0"},
{name: "thanos", binName: "thanos-v0.20.1", pkgVersion: "github.com/thanos-io/thanos/cmd/thanos@v0.20.1"},
},
expectBinaries: []string{
"buildable",
Expand All @@ -712,7 +712,7 @@ func TestGet(t *testing.T) {
"f2-v1.0.0", "f2-v1.1.0", "f2-v1.2.0", "f2-v1.3.0", "f2-v1.4.0", "f2-v1.5.0", "f3-v1.1.0", "f3-v1.3.0", "f3-v1.4.0",
"faillint-v1.0.0", "faillint-v1.1.0", "faillint-v1.3.0", "faillint-v1.4.0", "faillint-v1.5.0",
"go-bindata-v3.1.1+incompatible",
"thanos-v0.13.1-0.20210108102609-f85e4003ba51", "thanos-v0.19.0",
"thanos-v0.13.1-0.20210108102609-f85e4003ba51", "thanos-v0.20.1",
"wr_buildable-v0.0.0-20210109165512-ccbd4039b94a", "wr_buildable-v0.0.0-20210110214650-ab990d1be30b",
},
},
Expand All @@ -727,7 +727,7 @@ func TestGet(t *testing.T) {
},
expectRows: []row{
// TODO(bwplotka) This will be painful to maintain, but well... improve it
{name: "thanos", binName: "thanos-v0.19.0", pkgVersion: "github.com/thanos-io/thanos/cmd/thanos@v0.19.0"},
{name: "thanos", binName: "thanos-v0.20.1", pkgVersion: "github.com/thanos-io/thanos/cmd/thanos@v0.20.1"},
},
expectSameBinariesAsBefore: true,
},
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func main() {
goCmd := getFlags.String("go", "go", "Path to the go command.")
getUpdate := getFlags.Bool("u", false, "The -u flag instructs get to update modules providing dependencies of packages named on the command line to use newer minor or patch releases when available.")
getUpdatePatch := getFlags.Bool("upatch", false, "The -upatch flag (not -u patch) also instructs get to update dependencies, but changes the default to select patch releases.")

getInsecure := getFlags.Bool("insecure", false, "Use -insecure flag when using 'go get'")
getLink := getFlags.Bool("l", false, "If enabled, bingo will also create soft link called <tool> that links to the current"+
"<tool>-<version> binary. Use Variables.mk and variables.env if you want to be sure that what you are invoking is what is pinned.")
Expand Down
47 changes: 44 additions & 3 deletions pkg/bingo/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"sort"
"strings"

"github.com/bwplotka/bingo/pkg/envars"
"github.com/bwplotka/bingo/pkg/runner"
"github.com/efficientgo/tools/core/pkg/errcapture"
"github.com/efficientgo/tools/core/pkg/merrors"
Expand Down Expand Up @@ -46,6 +47,11 @@ type Package struct {
// Empty if the module is a full package path.
// If Module.Path is empty and RelPath specified, it means that we don't know what is a module what is the package path.
RelPath string

// BuildEnvs are environment variables to be used during go build process.
BuildEnvs envars.EnvSlice
// BuildFlags are flags to be used during go build process.
BuildFlags []string
}

// String returns a representation of the Package suitable for `go` tools and logging.
Expand Down Expand Up @@ -223,7 +229,7 @@ func (mf *ModFile) Reload() (err error) {

mf.directPackage = &Package{Module: r.Mod}
if len(r.Syntax.Suffix) > 0 {
mf.directPackage.RelPath = strings.Trim(r.Syntax.Suffix[0].Token[3:], "\n")
mf.directPackage.RelPath, mf.directPackage.BuildEnvs, mf.directPackage.BuildFlags = parseDirectPackageMeta(strings.Trim(r.Syntax.Suffix[0].Token[3:], "\n"))
}
break
}
Expand All @@ -235,6 +241,27 @@ func (mf *ModFile) Reload() (err error) {
return nil
}

func parseDirectPackageMeta(line string) (relPath string, buildEnv []string, buildFlags []string) {
elem := strings.Split(line, " ")
for i, l := range elem {
if l == "" {
continue
}

if l[0] == '-' {
buildFlags = elem[i:]
break
}

if !strings.Contains(l, "=") {
relPath = l
continue
}
buildEnv = append(buildEnv, l)
}
return relPath, buildEnv, buildFlags
}

func (mf *ModFile) DirectPackage() *Package {
return mf.directPackage
}
Expand All @@ -260,11 +287,19 @@ func (mf *ModFile) SetDirectRequire(target Package) (err error) {
mf.dropAllRequire()
mf.m.AddNewRequire(target.Module.Path, target.Module.Version, false)

var meta []string
// Add sub package info if needed.
if target.RelPath != "" && target.RelPath != "." {
meta = append(meta, target.RelPath)
}
meta = append(meta, target.BuildEnvs...)
meta = append(meta, target.BuildFlags...)

if len(meta) > 0 {
r := mf.m.Require[0]
r.Syntax.Suffix = append(r.Syntax.Suffix[:0], modfile.Comment{Suffix: true, Token: "// " + target.RelPath})
r.Syntax.Suffix = append(r.Syntax.Suffix[:0], modfile.Comment{Suffix: true, Token: "// " + strings.Join(meta, " ")})
}

mf.m.Cleanup()
mf.directPackage = &target
return nil
Expand Down Expand Up @@ -333,7 +368,7 @@ func ModDirectPackage(modFile string) (pkg Package, err error) {
return *mf.directPackage, nil
}

// ModIndirectPackage return the all indirect mod from any module file.
// ModIndirectModules return the all indirect mod from any module file.
func ModIndirectModules(modFile string) (mods []module.Version, err error) {
m, err := ParseModFileOrReader(modFile, nil)
if err != nil {
Expand Down Expand Up @@ -390,6 +425,9 @@ type PackageRenderable struct {
PackagePath string
EnvVarName string
Versions []PackageVersionRenderable

BuildFlags []string
BuildEnvVars []string
}

func (p PackageRenderable) ToPackages() []Package {
Expand Down Expand Up @@ -457,6 +495,9 @@ ModLoop:
Versions: []PackageVersionRenderable{
{Version: pkg.Module.Version, ModFile: filepath.Base(f)},
},
BuildFlags: pkg.BuildFlags,
BuildEnvVars: pkg.BuildEnvs,

EnvVarName: varName,
PackagePath: pkg.Path(),
ModPath: pkg.Module.Path,
Expand Down
Loading