Centralize Go validation and build environments
This commit is contained in:
@@ -11,10 +11,10 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/candidate"
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/gotool"
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/versioncontract"
|
||||
)
|
||||
|
||||
@@ -115,15 +115,17 @@ func Build(ctx context.Context, request Request) (Result, error) {
|
||||
return result, fmt.Errorf("create build output directory %q: %w", result.OutputDir, err)
|
||||
}
|
||||
|
||||
compiler, err := runGo(ctx, repositoryDir, nil, "version")
|
||||
goTool, err := gotool.New(ctx, repositoryDir)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("prepare Go toolchain: %w", err)
|
||||
}
|
||||
compiler, err := goTool.Run(ctx, repositoryDir, gotool.Native, gotool.Target{}, nil, "version")
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("identify Go compiler: %w", err)
|
||||
}
|
||||
result.Compiler = strings.TrimSpace(string(compiler))
|
||||
host, err := hostTarget(ctx, repositoryDir)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("identify Go host: %w", err)
|
||||
}
|
||||
goHost := goTool.HostTarget()
|
||||
host := Target{OS: goHost.OS, Arch: goHost.Arch}
|
||||
|
||||
for index, target := range result.Targets {
|
||||
if err := ctx.Err(); err != nil {
|
||||
@@ -134,7 +136,7 @@ func Build(ctx context.Context, request Request) (Result, error) {
|
||||
if request.Output != nil {
|
||||
fmt.Fprintf(request.Output, "building %s/%s as %s\n", target.OS, target.Arch, filename)
|
||||
}
|
||||
if err := buildTarget(ctx, repositoryDir, request.Spec.MainPackage, path, request.Version, target); err != nil {
|
||||
if err := buildTarget(ctx, goTool, repositoryDir, request.Spec.MainPackage, path, request.Version, target); err != nil {
|
||||
return result, fmt.Errorf("build target %s/%s: %w", target.OS, target.Arch, err)
|
||||
}
|
||||
artifact := Artifact{Path: path, Filename: filename, Target: target}
|
||||
@@ -164,7 +166,7 @@ func Build(ctx context.Context, request Request) (Result, error) {
|
||||
if request.Output != nil {
|
||||
fmt.Fprintf(request.Output, "building temporary version check for %s/%s\n", host.OS, host.Arch)
|
||||
}
|
||||
if err := buildTarget(ctx, repositoryDir, request.Spec.MainPackage, smokePath, request.Version, host); err != nil {
|
||||
if err := buildTarget(ctx, goTool, repositoryDir, request.Spec.MainPackage, smokePath, request.Version, host); err != nil {
|
||||
return result, fmt.Errorf("build native version check: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -236,76 +238,13 @@ func gitCommit(ctx context.Context, repositoryDir string) (string, error) {
|
||||
return strings.TrimSpace(string(output)), nil
|
||||
}
|
||||
|
||||
func hostTarget(ctx context.Context, repositoryDir string) (Target, error) {
|
||||
output, err := runGo(ctx, repositoryDir, nil, "env", "GOHOSTOS", "GOHOSTARCH")
|
||||
if err != nil {
|
||||
return Target{}, err
|
||||
}
|
||||
parts := strings.Fields(string(output))
|
||||
if len(parts) != 2 {
|
||||
return Target{}, fmt.Errorf("unexpected Go host response %q", output)
|
||||
}
|
||||
return Target{OS: parts[0], Arch: parts[1]}, nil
|
||||
}
|
||||
|
||||
func buildTarget(ctx context.Context, repositoryDir, mainPackage, outputPath, version string, target Target) error {
|
||||
func buildTarget(ctx context.Context, goTool *gotool.Tool, repositoryDir, mainPackage, outputPath, version string, target Target) error {
|
||||
ldflags := "-s -w -X " + versioncontract.InjectedVersionLinkerSymbol + "=" + version
|
||||
_, err := runGo(ctx, repositoryDir, map[string]string{"GOOS": target.OS, "GOARCH": target.Arch},
|
||||
_, err := goTool.Run(ctx, repositoryDir, gotool.Static, gotool.Target{OS: target.OS, Arch: target.Arch}, nil,
|
||||
"build", "-o", outputPath, "-mod=readonly", "-trimpath", "-buildvcs=false", "-ldflags", ldflags, mainPackage)
|
||||
return err
|
||||
}
|
||||
|
||||
func runGo(ctx context.Context, directory string, target map[string]string, args ...string) ([]byte, error) {
|
||||
commandArgs := append([]string(nil), args...)
|
||||
command := exec.CommandContext(ctx, "go", commandArgs...)
|
||||
command.Dir = directory
|
||||
command.Env = goEnvironment(target)
|
||||
output, err := command.CombinedOutput()
|
||||
if err == nil {
|
||||
return output, nil
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
err = fmt.Errorf("%w: %v", ctx.Err(), err)
|
||||
}
|
||||
if len(output) != 0 {
|
||||
return output, fmt.Errorf("go %s: %w: %s", strings.Join(commandArgs, " "), err, strings.TrimSpace(string(output)))
|
||||
}
|
||||
return output, fmt.Errorf("go %s: %w", strings.Join(commandArgs, " "), err)
|
||||
}
|
||||
|
||||
func goEnvironment(target map[string]string) []string {
|
||||
controlled := map[string]bool{
|
||||
"GOWORK": true, "GOPROXY": true, "GONOPROXY": true, "GOSUMDB": true,
|
||||
"GOTOOLCHAIN": true, "GOFLAGS": true, "CGO_ENABLED": true, "GOOS": true, "GOARCH": true,
|
||||
}
|
||||
for key := range target {
|
||||
controlled[key] = true
|
||||
}
|
||||
environment := make([]string, 0, len(os.Environ())+len(controlled))
|
||||
for _, entry := range os.Environ() {
|
||||
key, _, ok := strings.Cut(entry, "=")
|
||||
if !ok || !controlled[key] {
|
||||
environment = append(environment, entry)
|
||||
}
|
||||
}
|
||||
values := map[string]string{
|
||||
"GOWORK": "off", "GOPROXY": "off", "GONOPROXY": "none", "GOSUMDB": "off",
|
||||
"GOTOOLCHAIN": "local", "GOFLAGS": "-mod=readonly", "CGO_ENABLED": "0",
|
||||
}
|
||||
for key, value := range target {
|
||||
values[key] = value
|
||||
}
|
||||
keys := make([]string, 0, len(values))
|
||||
for key := range values {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
environment = append(environment, key+"="+values[key])
|
||||
}
|
||||
return environment
|
||||
}
|
||||
|
||||
func hashArtifact(artifact *Artifact) error {
|
||||
file, err := os.Open(artifact.Path)
|
||||
if err != nil {
|
||||
@@ -322,7 +261,6 @@ func hashArtifact(artifact *Artifact) error {
|
||||
|
||||
func checkVersion(ctx context.Context, binaryPath, binaryName, version string) error {
|
||||
command := exec.CommandContext(ctx, binaryPath, "version")
|
||||
command.Env = goEnvironment(nil)
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
|
||||
Reference in New Issue
Block a user