Skip to content

Commit

Permalink
Build go binaries in docker (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wojciech Małota-Wójcik authored Aug 19, 2024
1 parent 06e9b4c commit c4340ba
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 19 deletions.
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

0 comments on commit c4340ba

Please sign in to comment.