-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
417 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
// package golang provides util functions for [Magefile]. | ||
// For usage please refer to [documentation] provided by Magefile. | ||
// For autocompletions see [completions]. | ||
// | ||
// [Magefile]: https://magefile.org/ | ||
// [documentation]: https://magefile.org/importing/ | ||
// [completions]: https://github.com/elisasre/mageutil/tree/main/completions | ||
package golang | ||
|
||
import ( | ||
"context" | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path" | ||
"strings" | ||
|
||
"github.com/magefile/mage/sh" | ||
) | ||
|
||
// BinDir is base directory for build outputs. | ||
const BinDir = "./target/bin/" | ||
|
||
// BuildInfo contains relevant information about produced binary. | ||
type BuildInfo struct { | ||
BinPath string | ||
GOOS string | ||
GOARCH string | ||
} | ||
|
||
type ( | ||
BuildPlatform struct{ OS, Arch string } | ||
BuildMatrix []BuildPlatform | ||
) | ||
|
||
// DefaultBuildMatrix defines subset of cross-compile targets supported by Go. | ||
var DefaultBuildMatrix = BuildMatrix{ | ||
{OS: "linux", Arch: "amd64"}, | ||
{OS: "linux", Arch: "arm64"}, | ||
{OS: "darwin", Arch: "amd64"}, | ||
{OS: "darwin", Arch: "arm64"}, | ||
{OS: "windows", Arch: "amd64"}, | ||
} | ||
|
||
// Build builds binary which is created under ./target/bin/{GOOS}/{GOARCH}/. | ||
func Build(ctx context.Context, name string, buildArgs ...string) (BuildInfo, error) { | ||
return BuildWith(ctx, nil, name, buildArgs...) | ||
} | ||
|
||
// BuildWith injects given env and builds binary which is created under ./target/bin/{GOOS}/{GOARCH}/. | ||
func BuildWith(ctx context.Context, env map[string]string, name string, buildArgs ...string) (BuildInfo, error) { | ||
return build(ctx, env, BinDir, name, buildArgs...) | ||
} | ||
|
||
// BuildForPlatform builds binary for wanted architecture and os. | ||
func BuildForPlatform(ctx context.Context, goos, goarch, name string, buildArgs ...string) (BuildInfo, error) { | ||
return BuildForPlatformWith(ctx, nil, goos, goarch, name, buildArgs...) | ||
} | ||
|
||
// BuildForPlatform injects env, builds binary for wanted architecture and os. | ||
func BuildForPlatformWith(ctx context.Context, env map[string]string, goos, goarch, name string, buildArgs ...string) (BuildInfo, error) { | ||
if env == nil { | ||
env = map[string]string{} | ||
} | ||
env["GOOS"] = goos | ||
env["GOARCH"] = goarch | ||
return BuildWith(ctx, env, name, buildArgs...) | ||
} | ||
|
||
// WithSHA is a wrapper for build functions that adds SHA256Sum calculation. | ||
func WithSHA(info BuildInfo, err error) (BuildInfo, error) { | ||
if err != nil { | ||
return BuildInfo{}, err | ||
} | ||
|
||
return info, SHA256Sum(info.BinPath, info.BinPath+".sha256") | ||
} | ||
|
||
func BuildForTesting(ctx context.Context, name string) (BuildInfo, error) { | ||
env := map[string]string{ | ||
"CGO_ENABLED": "1", | ||
} | ||
|
||
pkgs, err := ListPackages(ctx, "./...") | ||
if err != nil { | ||
return BuildInfo{}, err | ||
} | ||
|
||
args := []string{"-race", "-cover", "-covermode", "atomic", "-coverpkg=" + strings.Join(pkgs, ",")} | ||
return build(ctx, env, TestBinDir, name, args...) | ||
} | ||
|
||
// BuildFromMatrixWithSHA is a higher level build utility function doing cross compilation with sha calculation. | ||
func BuildFromMatrixWithSHA(ctx context.Context, env map[string]string, matrix BuildMatrix, name string, buildArgs ...string) ([]BuildInfo, error) { | ||
info := make([]BuildInfo, 0, len(matrix)) | ||
for _, m := range matrix { | ||
i, err := BuildForPlatformWith(ctx, env, m.OS, m.Arch, name, buildArgs...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
info = append(info, i) | ||
} | ||
|
||
return info, nil | ||
} | ||
|
||
// BuildTargets returns list packages under ./cmd/. | ||
func BuildTargets(ctx context.Context) ([]string, error) { | ||
entries, err := os.ReadDir("./cmd/") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
targets := make([]string, 0, len(entries)) | ||
for _, e := range entries { | ||
if e.IsDir() { | ||
targets = append(targets, e.Name()) | ||
} | ||
} | ||
return targets, nil | ||
} | ||
|
||
// ListPackages lists all packages in given target. | ||
func ListPackages(ctx context.Context, target string) ([]string, error) { | ||
pkgsRaw, err := sh.Output("go", "list", target) | ||
if err != nil { | ||
return nil, err | ||
} | ||
pkgs := strings.Split(strings.ReplaceAll(pkgsRaw, "\r\n", ","), "\n") | ||
return pkgs, nil | ||
} | ||
|
||
// SHA256Sum calculates sum for input file and stores it in output file. | ||
// Output should be compatible with sha256sum program. | ||
func SHA256Sum(input, output string) error { | ||
f, err := os.Open(input) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
|
||
data, err := io.ReadAll(f) | ||
if err != nil { | ||
return err | ||
} | ||
sum := sha256.Sum256(data) | ||
hexSum := hex.EncodeToString(sum[:]) | ||
|
||
sumFile, err := os.Create(output) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
|
||
_, err = fmt.Fprintf(sumFile, "%s *%s\n", hexSum, input) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func build(ctx context.Context, env map[string]string, base, name string, buildArgs ...string) (BuildInfo, error) { | ||
info, err := prepareBuildInfo(env, base, name) | ||
if err != nil { | ||
return BuildInfo{}, err | ||
} | ||
|
||
args := append([]string{"build", "-o", info.BinPath}, append(buildArgs, "./cmd/"+name)...) | ||
if err = GoWith(ctx, env, args...); err != nil { | ||
return BuildInfo{}, err | ||
} | ||
|
||
return info, nil | ||
} | ||
func prepareBuildInfo(env map[string]string, base, name string) (BuildInfo, error) { | ||
goos, err := sh.Output("go", "env", "GOOS") | ||
if err != nil { | ||
return BuildInfo{}, err | ||
} | ||
|
||
goarch, err := sh.Output("go", "env", "GOARCH") | ||
if err != nil { | ||
return BuildInfo{}, err | ||
} | ||
|
||
if envOS, ok := env["GOOS"]; ok { | ||
goos = envOS | ||
} | ||
|
||
if envArch, ok := env["GOARCH"]; ok { | ||
goarch = envArch | ||
} | ||
|
||
binaryPath := path.Join(base, goos, goarch, name) | ||
return BuildInfo{BinPath: binaryPath, GOOS: goos, GOARCH: goarch}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package golang | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/elisasre/mageutil/git" | ||
"github.com/magefile/mage/sh" | ||
) | ||
|
||
// Go is shorthand for go executable provided by system. | ||
func Go(ctx context.Context, args ...string) error { | ||
return GoWith(ctx, nil, args...) | ||
} | ||
|
||
// GoWith is shorthand for go executable provided by system. | ||
func GoWith(ctx context.Context, env map[string]string, args ...string) error { | ||
fmt.Printf("env: %s\n", env) | ||
return sh.RunWithV(env, "go", args...) | ||
} | ||
|
||
// Tidy runs go mod tidy. | ||
func Tidy(ctx context.Context) error { | ||
return Go(ctx, "mod", "tidy") | ||
} | ||
|
||
// TidyAndVerify runs go mod tidy and verifies that there are no changes to go.mod or go.sum. | ||
// This is useful in CI/CD pipelines to validate that dependencies match go.mod. | ||
func TidyAndVerify(ctx context.Context) error { | ||
if err := Tidy(ctx); err != nil { | ||
return err | ||
} | ||
if err := git.Git(ctx, "diff", "--exit-code", "--", "go.mod", "go.sum"); err != nil { | ||
return fmt.Errorf("go.mod and go.sum are not in sync. run `go mod tidy` and commit changes") | ||
} | ||
return nil | ||
} |
Oops, something went wrong.