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

Build go binaries in docker #17

Merged
merged 1 commit into from
Aug 19, 2024
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
11 changes: 10 additions & 1 deletion cmd/builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"path/filepath"

"github.com/outofforest/build"

Expand All @@ -14,10 +15,18 @@ import (
func buildGo(ctx context.Context, deps build.DepsFunc) error {
deps(generateGo)

return golang.Build(ctx, deps, golang.BuildConfig{
if err := golang.Build(ctx, deps, golang.BuildConfig{
Platform: tools.PlatformLocal,
PackagePath: "testapps/golang",
BinOutputPath: "bin/test-go",
}); err != nil {
return err
}

return golang.Build(ctx, deps, golang.BuildConfig{
Platform: tools.PlatformDocker,
PackagePath: "testapps/golang",
BinOutputPath: filepath.Join("bin", tools.PlatformDocker.String(), "test-go"),
})
}

Expand Down
3 changes: 2 additions & 1 deletion commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ func enter(ctx context.Context, deps build.DepsFunc) error {
bash := exec.Command("bash")
bash.Env = append(os.Environ(),
fmt.Sprintf("PS1=%s", "("+build.GetName(ctx)+`) [\u@\h \W]\$ `),
fmt.Sprintf("PATH=%s:%s",
fmt.Sprintf("PATH=%s:%s:%s",
filepath.Join(lo.Must(filepath.EvalSymlinks(lo.Must(filepath.Abs(".")))), "bin"),
filepath.Join(tools.VersionDir(ctx, tools.PlatformLocal), "bin"),
os.Getenv("PATH")),
)
Expand Down
26 changes: 26 additions & 0 deletions pkg/tools/docker/tools.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package docker

import (
"context"
"os/exec"

"github.com/outofforest/build"
"github.com/pkg/errors"
)

// AlpineVersion is the version of the alpine docker image.
const AlpineVersion = "3.20"

// Label used to tag docker resources created by crust.
const (
LabelKey = "io.seinetwork.build"
LabelValue = "build"
)

// EnsureDocker verifies that docker is installed.
func EnsureDocker(_ context.Context, _ build.DepsFunc) error {
if _, err := exec.LookPath("docker"); err != nil {
return errors.Wrap(err, "docker command is not available in PATH")
}
return nil
}
76 changes: 65 additions & 11 deletions pkg/tools/golang/golang.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package golang
import (
"context"
_ "embed"
"fmt"
"io/fs"
"os"
"os/exec"
Expand All @@ -18,6 +19,7 @@ import (

"github.com/sei-protocol/build/pkg/helpers"
"github.com/sei-protocol/build/pkg/tools"
"github.com/sei-protocol/build/pkg/tools/docker"
)

const coverageReportDir = "coverage"
Expand All @@ -43,8 +45,7 @@ type BuildConfig struct {
// Build builds go binary.
func Build(ctx context.Context, deps build.DepsFunc, config BuildConfig) error {
if config.Platform.OS == tools.OSDocker {
return errors.New("building in docker hasn't been implemented yet")
// return buildInDocker(ctx, config)
return buildInDocker(ctx, deps, config)
}
return buildLocally(ctx, deps, config)
}
Expand Down Expand Up @@ -154,8 +155,7 @@ func buildLocally(ctx context.Context, deps build.DepsFunc, config BuildConfig)
config.Platform, tools.PlatformLocal)
}

args, envs := buildArgsAndEnvs(ctx, config, filepath.Join(tools.VersionDir(ctx, config.Platform), "lib"))
args = append(args, "-o", lo.Must(filepath.Abs(config.BinOutputPath)), ".")
args, envs := buildArgsAndEnvs(ctx, config)

cmd := exec.Command(tools.Bin(ctx, "bin/go", config.Platform), args...)
cmd.Dir = config.PackagePath
Expand All @@ -173,29 +173,83 @@ func buildLocally(ctx context.Context, deps build.DepsFunc, config BuildConfig)
return nil
}

func buildArgsAndEnvs(ctx context.Context, config BuildConfig, libDir string) (args, envs []string) {
func buildInDocker(ctx context.Context, deps build.DepsFunc, config BuildConfig) error {
deps(docker.EnsureDocker)

goTool, err := tools.Get(Go)
if err != nil {
return err
}

image := fmt.Sprintf("golang:%s-alpine%s", goTool.GetVersion(), docker.AlpineVersion)

srcDir := lo.Must(filepath.EvalSymlinks(lo.Must(filepath.Abs("."))))
envDir := tools.EnvDir(ctx)

if err := os.MkdirAll(envDir, 0o755); err != nil {
return errors.WithStack(err)
}

args, envs := buildArgsAndEnvs(ctx, config)
if err != nil {
return err
}
runArgs := []string{
"run", "--rm",
"--label", docker.LabelKey + "=" + docker.LabelValue,
"-v", srcDir + ":" + srcDir,
"-v", envDir + ":" + envDir,
"--workdir", filepath.Join(srcDir, config.PackagePath),
"--user", fmt.Sprintf("%d:%d", os.Getuid(), os.Getgid()),
"--name", "sei-build-golang",
}

for _, env := range envs {
runArgs = append(runArgs, "--env", env)
}

runArgs = append(runArgs, image, "/usr/local/go/bin/go")
runArgs = append(runArgs, args...)

cmd := exec.Command("docker", runArgs...)
logger.Get(ctx).Info(
"Building go package in docker",
zap.String("package", config.PackagePath),
zap.String("command", cmd.String()),
)
if err := libexec.Exec(ctx, cmd); err != nil {
return errors.Wrapf(err, "building package '%s' failed", config.PackagePath)
}
return nil
}

func buildArgsAndEnvs(ctx context.Context, config BuildConfig) (args, envs []string) {
ldFlags := []string{"-w", "-s"}

args = []string{
"build",
"-trimpath",
"-buildvcs=false",
}
if len(ldFlags) != 0 {
args = append(args, "-ldflags="+strings.Join(ldFlags, " "))
"-ldflags=" + strings.Join(ldFlags, " "),
"-o", lo.Must(filepath.Abs(config.BinOutputPath)),
".",
}
if len(config.Tags) != 0 {
args = append(args, "-tags="+strings.Join(config.Tags, ","))
}

goOS := config.Platform.OS
if goOS == tools.OSDocker {
goOS = tools.OSLinux
}

cgoEnabled := "0"
if config.CGOEnabled {
cgoEnabled = "1"
}
envs = append(env(ctx),
"LIBRARY_PATH="+libDir,
"CGO_ENABLED="+cgoEnabled,
"GOOS="+config.Platform.OS,
"GOOS="+goOS,
"GOARCH="+config.Platform.Arch,
)

Expand Down Expand Up @@ -232,7 +286,7 @@ func lintConfigPath(ctx context.Context) string {

func env(ctx context.Context) []string {
return []string{
"PATH=" + os.Getenv("PATH"),
"PATH=" + filepath.Join(tools.VersionDir(ctx, tools.PlatformLocal), "bin") + ":" + os.Getenv("PATH"),
"GOPATH=" + filepath.Join(tools.DevDir(ctx), "go"),
"GOCACHE=" + filepath.Join(tools.DevDir(ctx), "go", "cache", "gobuild"),
"GOLANGCI_LINT_CACHE=" + filepath.Join(tools.DevDir(ctx), "go", "cache", "golangci"),
Expand Down
13 changes: 7 additions & 6 deletions pkg/tools/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ func Get(toolName Name) (Tool, error) {
return t, nil
}

// EnvDir returns the directory where local environment is stored.
func EnvDir(ctx context.Context) string {
return filepath.Join(lo.Must(os.UserCacheDir()), build.GetName(ctx))
}

// ToolDownloadDir returns directory where tool is downloaded.
func ToolDownloadDir(ctx context.Context, platform Platform, tool Tool) string {
return filepath.Join(downloadsDir(ctx, platform), string(tool.GetName())+"-"+tool.GetVersion())
Expand All @@ -277,7 +282,7 @@ func ToolLinksDir(ctx context.Context, platform Platform, tool Tool) string {

// DevDir returns directory where development files are stored.
func DevDir(ctx context.Context) string {
return filepath.Join(envDir(ctx), "dev")
return filepath.Join(EnvDir(ctx), "dev")
}

// ShouldReinstall check if tool should be reinstalled due to missing files or links.
Expand Down Expand Up @@ -384,12 +389,8 @@ func Checksum(file string) (string, error) {
return "sha256:" + hex.EncodeToString(hasher.Sum(nil)), nil
}

func envDir(ctx context.Context) string {
return filepath.Join(lo.Must(os.UserCacheDir()), build.GetName(ctx))
}

func platformDir(ctx context.Context, platform Platform) string {
return filepath.Join(envDir(ctx), platform.String())
return filepath.Join(EnvDir(ctx), platform.String())
}

func downloadsDir(ctx context.Context, platform Platform) string {
Expand Down