diff --git a/devcontainer/devcontainer.go b/devcontainer/devcontainer.go index 8f2780c0..9fd27406 100644 --- a/devcontainer/devcontainer.go +++ b/devcontainer/devcontainer.go @@ -73,7 +73,7 @@ type Compiled struct { RemoteEnv map[string]string } -func SubstituteVars(s string, workspaceFolder string) string { +func SubstituteVars(s string, workspaceFolder string, lookupEnv func(string) (string, bool)) string { var buf string for { beforeOpen, afterOpen, ok := strings.Cut(s, "${") @@ -85,14 +85,14 @@ func SubstituteVars(s string, workspaceFolder string) string { return buf + s } - buf += beforeOpen + substitute(varExpr, workspaceFolder) + buf += beforeOpen + substitute(varExpr, workspaceFolder, lookupEnv) s = afterClose } } // Spec for variable substitutions: // https://containers.dev/implementors/json_reference/#variables-in-devcontainerjson -func substitute(varExpr string, workspaceFolder string) string { +func substitute(varExpr string, workspaceFolder string, lookupEnv func(string) (string, bool)) string { parts := strings.Split(varExpr, ":") if len(parts) == 1 { switch varExpr { @@ -101,12 +101,16 @@ func substitute(varExpr string, workspaceFolder string) string { case "localWorkspaceFolderBasename", "containerWorkspaceFolderBasename": return filepath.Base(workspaceFolder) default: - return os.Getenv(varExpr) + val, ok := lookupEnv(varExpr) + if ok { + return val + } + return "" } } switch parts[0] { case "env", "localEnv", "containerEnv": - if val, ok := os.LookupEnv(parts[1]); ok { + if val, ok := lookupEnv(parts[1]); ok { return val } if len(parts) == 3 { @@ -131,7 +135,7 @@ func (s Spec) HasDockerfile() bool { // devcontainerDir is the path to the directory where the devcontainer.json file // is located. scratchDir is the path to the directory where the Dockerfile will // be written to if one doesn't exist. -func (s *Spec) Compile(fs billy.Filesystem, devcontainerDir, scratchDir string, fallbackDockerfile, workspaceFolder string, useBuildContexts bool) (*Compiled, error) { +func (s *Spec) Compile(fs billy.Filesystem, devcontainerDir, scratchDir string, fallbackDockerfile, workspaceFolder string, useBuildContexts bool, lookupEnv func(string) (string, bool)) (*Compiled, error) { params := &Compiled{ User: s.ContainerUser, ContainerEnv: s.ContainerEnv, @@ -178,7 +182,7 @@ func (s *Spec) Compile(fs billy.Filesystem, devcontainerDir, scratchDir string, buildArgs := make([]string, 0) for _, key := range buildArgkeys { - val := SubstituteVars(s.Build.Args[key], workspaceFolder) + val := SubstituteVars(s.Build.Args[key], workspaceFolder, lookupEnv) buildArgs = append(buildArgs, key+"="+val) } params.BuildArgs = buildArgs diff --git a/devcontainer/devcontainer_test.go b/devcontainer/devcontainer_test.go index da003223..c864e11e 100644 --- a/devcontainer/devcontainer_test.go +++ b/devcontainer/devcontainer_test.go @@ -87,7 +87,7 @@ func TestCompileWithFeatures(t *testing.T) { dc, err := devcontainer.Parse([]byte(raw)) require.NoError(t, err) fs := memfs.New() - params, err := dc.Compile(fs, "", magicDir, "", "", false) + params, err := dc.Compile(fs, "", magicDir, "", "", false, os.LookupEnv) require.NoError(t, err) // We have to SHA because we get a different MD5 every time! @@ -118,7 +118,7 @@ func TestCompileDevContainer(t *testing.T) { dc := &devcontainer.Spec{ Image: "localhost:5000/envbuilder-test-ubuntu:latest", } - params, err := dc.Compile(fs, "", magicDir, "", "", false) + params, err := dc.Compile(fs, "", magicDir, "", "", false, os.LookupEnv) require.NoError(t, err) require.Equal(t, filepath.Join(magicDir, "Dockerfile"), params.DockerfilePath) require.Equal(t, magicDir, params.BuildContext) @@ -144,7 +144,7 @@ func TestCompileDevContainer(t *testing.T) { _, err = io.WriteString(file, "FROM localhost:5000/envbuilder-test-ubuntu:latest") require.NoError(t, err) _ = file.Close() - params, err := dc.Compile(fs, dcDir, magicDir, "", "/var/workspace", false) + params, err := dc.Compile(fs, dcDir, magicDir, "", "/var/workspace", false, os.LookupEnv) require.NoError(t, err) require.Equal(t, "ARG1=value1", params.BuildArgs[0]) require.Equal(t, "ARG2=workspace", params.BuildArgs[1]) diff --git a/envbuilder.go b/envbuilder.go index 7ea7791d..7e88cbee 100644 --- a/envbuilder.go +++ b/envbuilder.go @@ -290,7 +290,7 @@ func Run(ctx context.Context, options Options) error { options.Logger(notcodersdk.LogLevelInfo, "No Dockerfile or image specified; falling back to the default image...") fallbackDockerfile = defaultParams.DockerfilePath } - buildParams, err = devContainer.Compile(options.Filesystem, devcontainerDir, MagicDir, fallbackDockerfile, options.WorkspaceFolder, false) + buildParams, err = devContainer.Compile(options.Filesystem, devcontainerDir, MagicDir, fallbackDockerfile, options.WorkspaceFolder, false, os.LookupEnv) if err != nil { return fmt.Errorf("compile devcontainer.json: %w", err) } @@ -669,7 +669,7 @@ func Run(ctx context.Context, options Options) error { } sort.Strings(envKeys) for _, envVar := range envKeys { - value := devcontainer.SubstituteVars(env[envVar], options.WorkspaceFolder) + value := devcontainer.SubstituteVars(env[envVar], options.WorkspaceFolder, os.LookupEnv) os.Setenv(envVar, value) } }