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

Enable setting environment variables in .ko.yaml #419

Merged
merged 2 commits into from
Aug 23, 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
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ configuration section in your `.ko.yaml`.
builds:
- id: foo
main: ./foobar/foo
env:
- GOPRIVATE=git.internal.example.com,source.developers.google.com
flags:
- -tags
- netgo
Expand All @@ -144,6 +146,8 @@ builds:
- -X main.version={{.Env.VERSION}}
- id: bar
main: ./foobar/bar/main.go
env:
- GOCACHE=/workspace/.gocache
ldflags:
- -s
- -w
Expand All @@ -156,7 +160,7 @@ with the intended import path.

_Please note:_ Even though the configuration section is similar to the
[GoReleaser `builds` section](https://goreleaser.com/customization/build/),
only the `flags` and `ldflags` fields are currently supported. Also, the
only the `env`, `flags` and `ldflags` fields are currently supported. Also, the
templating support is currently limited to environment variables only.

## Naming Images
Expand Down
4 changes: 3 additions & 1 deletion pkg/build/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ type Config struct {
Ldflags StringArray `yaml:",omitempty"`
Flags FlagArray `yaml:",omitempty"`

// Env allows setting environment variables for `go build`
Env []string `yaml:",omitempty"`

// Other GoReleaser fields that are not supported or do not make sense
// in the context of ko, for reference or for future use:
// Goos []string `yaml:",omitempty"`
Expand All @@ -87,7 +90,6 @@ type Config struct {
// Gomips []string `yaml:",omitempty"`
// Targets []string `yaml:",omitempty"`
// Binary string `yaml:",omitempty"`
// Env []string `yaml:",omitempty"`
// Lang string `yaml:",omitempty"`
// Asmflags StringArray `yaml:",omitempty"`
// Gcflags StringArray `yaml:",omitempty"`
Expand Down
42 changes: 27 additions & 15 deletions pkg/build/gobuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,29 @@ func build(ctx context.Context, ip string, dir string, platform v1.Platform, con
cmd := exec.CommandContext(ctx, "go", args...)
cmd.Dir = dir

// Last one wins
env, err := buildEnv(platform, config.Env)
if err != nil {
return "", fmt.Errorf("could not create env for %s: %v", ip, err)
}
cmd.Env = env

var output bytes.Buffer
cmd.Stderr = &output
cmd.Stdout = &output

log.Printf("Building %s for %s", ip, platformToString(platform))
if err := cmd.Run(); err != nil {
os.RemoveAll(tmpDir)
log.Printf("Unexpected error running \"go build\": %v\n%v", err, output.String())
return "", err
}
return file, nil
}

// buildEnv creates the environment variables used by the `go build` command.
// From `os/exec.Cmd`: If Env contains duplicate environment keys, only the last
// value in the slice for each duplicate key is used.
func buildEnv(platform v1.Platform, configEnv []string) ([]string, error) {
defaultEnv := []string{
"CGO_ENABLED=0",
"GOOS=" + platform.OS,
Expand All @@ -388,26 +410,16 @@ func build(ctx context.Context, ip string, dir string, platform v1.Platform, con
if strings.HasPrefix(platform.Architecture, "arm") && platform.Variant != "" {
goarm, err := getGoarm(platform)
if err != nil {
return "", fmt.Errorf("goarm failure for %s: %v", ip, err)
return nil, fmt.Errorf("goarm failure: %v", err)
}
if goarm != "" {
defaultEnv = append(defaultEnv, "GOARM="+goarm)
}
}

cmd.Env = append(defaultEnv, os.Environ()...)

var output bytes.Buffer
cmd.Stderr = &output
cmd.Stdout = &output

log.Printf("Building %s for %s", ip, platformToString(platform))
if err := cmd.Run(); err != nil {
os.RemoveAll(tmpDir)
log.Printf("Unexpected error running \"go build\": %v\n%v", err, output.String())
return "", err
}
return file, nil
env := append(defaultEnv, os.Environ()...)
env = append(env, configEnv...)
return env, nil
}

func appFilename(importpath string) string {
Expand Down
77 changes: 77 additions & 0 deletions pkg/build/gobuild_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,83 @@ func TestGoBuildIsSupportedRefWithModules(t *testing.T) {
}
}

func TestBuildEnv(t *testing.T) {
tests := []struct {
description string
platform v1.Platform
configEnv []string
expectedEnvs map[string]string
}{
{
description: "defaults",
platform: v1.Platform{
OS: "linux",
Architecture: "amd64",
},
expectedEnvs: map[string]string{
"GOOS": "linux",
"GOARCH": "amd64",
"CGO_ENABLED": "0",
},
},
{
description: "override a default value",
configEnv: []string{"CGO_ENABLED=1"},
expectedEnvs: map[string]string{
"CGO_ENABLED": "1",
},
},
{
description: "override an envvar and add an envvar",
configEnv: []string{"CGO_ENABLED=1", "GOPRIVATE=git.internal.example.com,source.developers.google.com"},
expectedEnvs: map[string]string{
"CGO_ENABLED": "1",
"GOPRIVATE": "git.internal.example.com,source.developers.google.com",
},
},
{
description: "arm variant",
platform: v1.Platform{
Architecture: "arm",
Variant: "v7",
},
expectedEnvs: map[string]string{
"GOARCH": "arm",
"GOARM": "7",
},
},
{
description: "arm64 variant",
platform: v1.Platform{
Architecture: "arm64",
Variant: "v8",
},
expectedEnvs: map[string]string{
"GOARCH": "arm64",
"GOARM": "7",
},
},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
env, err := buildEnv(test.platform, test.configEnv)
if err != nil {
t.Fatalf("unexpected error running buildEnv(): %v", err)
}
envs := map[string]string{}
for _, e := range env {
split := strings.SplitN(e, "=", 2)
envs[split[0]] = split[1]
}
for key, val := range test.expectedEnvs {
if envs[key] != val {
t.Errorf("buildEnv(): expected %s=%s, got %s=%s", key, val, key, envs[key])
}
}
})
}
}

// A helper method we use to substitute for the default "build" method.
func writeTempFile(_ context.Context, s string, _ string, _ v1.Platform, _ Config) (string, error) {
tmpDir, err := ioutil.TempDir("", "ko")
Expand Down