-
Notifications
You must be signed in to change notification settings - Fork 49
Add EXPORT_ENV_FILE parameter #55
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -194,6 +194,11 @@ type Options struct { | |
// This is useful for self-signed certificates. | ||
SSLCertBase64 string `env:"SSL_CERT_BASE64"` | ||
|
||
// ExportEnvFile is an optional file path to a .env file where | ||
// envbuilder will dump environment variables from devcontainer.json and | ||
// the built container image. | ||
ExportEnvFile string `env:"EXPORT_ENV_FILE"` | ||
|
||
// Logger is the logger to use for all operations. | ||
Logger func(level codersdk.LogLevel, format string, args ...interface{}) | ||
|
||
|
@@ -524,6 +529,7 @@ func Run(ctx context.Context, options Options) error { | |
}) | ||
} | ||
|
||
skippedRebuild := false | ||
build := func() (v1.Image, error) { | ||
_, err := options.Filesystem.Stat(MagicFile) | ||
if err == nil && options.SkipRebuild { | ||
|
@@ -537,6 +543,7 @@ func Run(ctx context.Context, options Options) error { | |
return nil, fmt.Errorf("image from remote: %w", err) | ||
} | ||
endStage("🏗️ Found image from remote!") | ||
skippedRebuild = true | ||
return image, nil | ||
} | ||
|
||
|
@@ -668,6 +675,25 @@ func Run(ctx context.Context, options Options) error { | |
} | ||
_ = file.Close() | ||
|
||
var exportEnvFile *os.File | ||
// Do not export env if we skipped a rebuild, because ENV directives | ||
// from the Dockerfile would not have been processed and we'd miss these | ||
// in the export. We should have generated a complete set of environment | ||
// on the intial build, so exporting environment variables a second time | ||
// isn't useful anyway. | ||
if options.ExportEnvFile != "" && !skippedRebuild { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A mention as to why we don't want to do this on rebuild would be helpful. Essentially your GitHub comments! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a comment explaining this. |
||
exportEnvFile, err = os.Create(options.ExportEnvFile) | ||
if err != nil { | ||
return fmt.Errorf("failed to open EXPORT_ENV_FILE %q: %w", options.ExportEnvFile, err) | ||
} | ||
} | ||
exportEnv := func(key, value string) { | ||
if exportEnvFile == nil { | ||
return | ||
} | ||
fmt.Fprintf(exportEnvFile, "%s=%s\n", key, value) | ||
} | ||
|
||
configFile, err := image.ConfigFile() | ||
if err != nil { | ||
return fmt.Errorf("get image config: %w", err) | ||
|
@@ -695,6 +721,7 @@ func Run(ctx context.Context, options Options) error { | |
if container.RemoteEnv != nil { | ||
for key, value := range container.RemoteEnv { | ||
os.Setenv(key, value) | ||
exportEnv(key, value) | ||
} | ||
} | ||
} | ||
|
@@ -724,10 +751,16 @@ func Run(ctx context.Context, options Options) error { | |
for _, env := range configFile.Config.Env { | ||
pair := strings.SplitN(env, "=", 2) | ||
os.Setenv(pair[0], pair[1]) | ||
exportEnv(pair[0], pair[1]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Environment variables declared with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Weird, it works in the test but isn't working in my actual setup 🤔. Trying to dig into this... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, found the problem. Env vars from the Dockerfile get skipped when |
||
} | ||
for _, env := range buildParams.Env { | ||
pair := strings.SplitN(env, "=", 2) | ||
os.Setenv(pair[0], pair[1]) | ||
exportEnv(pair[0], pair[1]) | ||
} | ||
|
||
if exportEnvFile != nil { | ||
exportEnvFile.Close() | ||
} | ||
|
||
username := configFile.Config.User | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -353,6 +353,39 @@ func TestExitBuildOnFailure(t *testing.T) { | |
require.ErrorContains(t, err, "parsing dockerfile") | ||
} | ||
|
||
func TestExportEnvFile(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great test! 😍 |
||
t.Parallel() | ||
|
||
// Ensures that a Git repository with a devcontainer.json is cloned and built. | ||
url := createGitServer(t, gitServerOptions{ | ||
files: map[string]string{ | ||
".devcontainer/devcontainer.json": `{ | ||
"name": "Test", | ||
"build": { | ||
"dockerfile": "Dockerfile" | ||
}, | ||
"build": { | ||
"dockerfile": "Dockerfile" | ||
}, | ||
"remoteEnv": { | ||
"FROM_DEVCONTAINER_JSON": "bar" | ||
} | ||
}`, | ||
".devcontainer/Dockerfile": "FROM alpine:latest\nENV FROM_DOCKERFILE=foo", | ||
}, | ||
}) | ||
ctr, err := runEnvbuilder(t, options{env: []string{ | ||
"GIT_URL=" + url, | ||
"EXPORT_ENV_FILE=/env", | ||
}}) | ||
require.NoError(t, err) | ||
|
||
output := execContainer(t, ctr, "cat /env") | ||
require.Contains(t, strings.TrimSpace(output), | ||
`FROM_DOCKERFILE=foo | ||
FROM_DEVCONTAINER_JSON=bar`) | ||
} | ||
|
||
func TestPrivateRegistry(t *testing.T) { | ||
t.Parallel() | ||
t.Run("NoAuth", func(t *testing.T) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment describing the use case would also be helpful, mainly because of the
export
directive, which is technicallysh
syntax, not.env
file.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should make this a proper
.env
file being exported instead. Would that make it less useful?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed this to create a
.env
file instead (key/value pairs separated by=
, no quoting). The sh syntax is more useful for me, but I can use something like this to process the.env
file: https://stackoverflow.com/a/66118031