Skip to content

Commit fca4f2d

Browse files
committed
feat: Accept custom LookupEnv function in SubstituteVars
I need this change for an off-brand usage of the devcontainer package in custom code.
1 parent f881a74 commit fca4f2d

File tree

3 files changed

+16
-12
lines changed

3 files changed

+16
-12
lines changed

devcontainer/devcontainer.go

+11-7
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ type Compiled struct {
7373
RemoteEnv map[string]string
7474
}
7575

76-
func SubstituteVars(s string, workspaceFolder string) string {
76+
func SubstituteVars(s string, workspaceFolder string, lookupEnv func(string) (string, bool)) string {
7777
var buf string
7878
for {
7979
beforeOpen, afterOpen, ok := strings.Cut(s, "${")
@@ -85,14 +85,14 @@ func SubstituteVars(s string, workspaceFolder string) string {
8585
return buf + s
8686
}
8787

88-
buf += beforeOpen + substitute(varExpr, workspaceFolder)
88+
buf += beforeOpen + substitute(varExpr, workspaceFolder, lookupEnv)
8989
s = afterClose
9090
}
9191
}
9292

9393
// Spec for variable substitutions:
9494
// https://containers.dev/implementors/json_reference/#variables-in-devcontainerjson
95-
func substitute(varExpr string, workspaceFolder string) string {
95+
func substitute(varExpr string, workspaceFolder string, lookupEnv func(string) (string, bool)) string {
9696
parts := strings.Split(varExpr, ":")
9797
if len(parts) == 1 {
9898
switch varExpr {
@@ -101,12 +101,16 @@ func substitute(varExpr string, workspaceFolder string) string {
101101
case "localWorkspaceFolderBasename", "containerWorkspaceFolderBasename":
102102
return filepath.Base(workspaceFolder)
103103
default:
104-
return os.Getenv(varExpr)
104+
val, ok := lookupEnv(varExpr)
105+
if ok {
106+
return val
107+
}
108+
return ""
105109
}
106110
}
107111
switch parts[0] {
108112
case "env", "localEnv", "containerEnv":
109-
if val, ok := os.LookupEnv(parts[1]); ok {
113+
if val, ok := lookupEnv(parts[1]); ok {
110114
return val
111115
}
112116
if len(parts) == 3 {
@@ -131,7 +135,7 @@ func (s Spec) HasDockerfile() bool {
131135
// devcontainerDir is the path to the directory where the devcontainer.json file
132136
// is located. scratchDir is the path to the directory where the Dockerfile will
133137
// be written to if one doesn't exist.
134-
func (s *Spec) Compile(fs billy.Filesystem, devcontainerDir, scratchDir string, fallbackDockerfile, workspaceFolder string, useBuildContexts bool) (*Compiled, error) {
138+
func (s *Spec) Compile(fs billy.Filesystem, devcontainerDir, scratchDir string, fallbackDockerfile, workspaceFolder string, useBuildContexts bool, lookupEnv func(string) (string, bool)) (*Compiled, error) {
135139
params := &Compiled{
136140
User: s.ContainerUser,
137141
ContainerEnv: s.ContainerEnv,
@@ -178,7 +182,7 @@ func (s *Spec) Compile(fs billy.Filesystem, devcontainerDir, scratchDir string,
178182

179183
buildArgs := make([]string, 0)
180184
for _, key := range buildArgkeys {
181-
val := SubstituteVars(s.Build.Args[key], workspaceFolder)
185+
val := SubstituteVars(s.Build.Args[key], workspaceFolder, lookupEnv)
182186
buildArgs = append(buildArgs, key+"="+val)
183187
}
184188
params.BuildArgs = buildArgs

devcontainer/devcontainer_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ func TestCompileWithFeatures(t *testing.T) {
8787
dc, err := devcontainer.Parse([]byte(raw))
8888
require.NoError(t, err)
8989
fs := memfs.New()
90-
params, err := dc.Compile(fs, "", magicDir, "", "", false)
90+
params, err := dc.Compile(fs, "", magicDir, "", "", false, os.LookupEnv)
9191
require.NoError(t, err)
9292

9393
// We have to SHA because we get a different MD5 every time!
@@ -118,7 +118,7 @@ func TestCompileDevContainer(t *testing.T) {
118118
dc := &devcontainer.Spec{
119119
Image: "localhost:5000/envbuilder-test-ubuntu:latest",
120120
}
121-
params, err := dc.Compile(fs, "", magicDir, "", "", false)
121+
params, err := dc.Compile(fs, "", magicDir, "", "", false, os.LookupEnv)
122122
require.NoError(t, err)
123123
require.Equal(t, filepath.Join(magicDir, "Dockerfile"), params.DockerfilePath)
124124
require.Equal(t, magicDir, params.BuildContext)
@@ -144,7 +144,7 @@ func TestCompileDevContainer(t *testing.T) {
144144
_, err = io.WriteString(file, "FROM localhost:5000/envbuilder-test-ubuntu:latest")
145145
require.NoError(t, err)
146146
_ = file.Close()
147-
params, err := dc.Compile(fs, dcDir, magicDir, "", "/var/workspace", false)
147+
params, err := dc.Compile(fs, dcDir, magicDir, "", "/var/workspace", false, os.LookupEnv)
148148
require.NoError(t, err)
149149
require.Equal(t, "ARG1=value1", params.BuildArgs[0])
150150
require.Equal(t, "ARG2=workspace", params.BuildArgs[1])

envbuilder.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ func Run(ctx context.Context, options Options) error {
290290
options.Logger(notcodersdk.LogLevelInfo, "No Dockerfile or image specified; falling back to the default image...")
291291
fallbackDockerfile = defaultParams.DockerfilePath
292292
}
293-
buildParams, err = devContainer.Compile(options.Filesystem, devcontainerDir, MagicDir, fallbackDockerfile, options.WorkspaceFolder, false)
293+
buildParams, err = devContainer.Compile(options.Filesystem, devcontainerDir, MagicDir, fallbackDockerfile, options.WorkspaceFolder, false, os.LookupEnv)
294294
if err != nil {
295295
return fmt.Errorf("compile devcontainer.json: %w", err)
296296
}
@@ -669,7 +669,7 @@ func Run(ctx context.Context, options Options) error {
669669
}
670670
sort.Strings(envKeys)
671671
for _, envVar := range envKeys {
672-
value := devcontainer.SubstituteVars(env[envVar], options.WorkspaceFolder)
672+
value := devcontainer.SubstituteVars(env[envVar], options.WorkspaceFolder, os.LookupEnv)
673673
os.Setenv(envVar, value)
674674
}
675675
}

0 commit comments

Comments
 (0)