Skip to content

Commit a474c16

Browse files
authored
[devbox.json] Added env in config (#609)
## Summary Added ability to add env variables in devbox.json Current implementation only supports literal string values as well as `$PATH` and `$PWD` variables. ## How was it tested? 1. Create a devbox.json similar to below: ```json "env": { "my_wd": "$PWD/test", "my_path": "$PATH:my_stuff/bin", "my_key": "string value" } ``` 2. Run `DEVBOX_FEATURE_NIXLESS_SHELL=1 DEVBOX_FEATURE_ENV_CONFIG=1 ./devbox shell` 3. Inside devbox shell `echo $my_wd`, `echo $my_path` and `echo $my_key` and confirm values are correctly setup.
1 parent 5a97bb9 commit a474c16

File tree

6 files changed

+61
-10
lines changed

6 files changed

+61
-10
lines changed

devbox.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type Devbox interface {
2828
Generate() error
2929
GenerateDevcontainer(force bool) error
3030
GenerateDockerfile(force bool) error
31-
GenerateEnvrc(force bool) error
31+
GenerateEnvrc(force bool, source string) error
3232
Info(pkg string, markdown bool) error
3333
ListScripts() []string
3434
PluginEnv() (string, error)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package featureflag
2+
3+
// EnvConfig allows declaring env variables in Config(devbox.json)
4+
var EnvConfig = disabled("ENV_CONFIG")

internal/boxcli/generate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func runGenerateCmd(cmd *cobra.Command, args []string, flags *generateCmdFlags)
114114
case "dockerfile":
115115
return box.GenerateDockerfile(flags.force)
116116
case "direnv":
117-
return box.GenerateEnvrc(flags.force)
117+
return box.GenerateEnvrc(flags.force, "generate")
118118
}
119119
return nil
120120
}

internal/boxcli/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func runInitCmd(cmd *cobra.Command, args []string) error {
3434
if err != nil {
3535
return errors.WithStack(err)
3636
}
37-
err = box.GenerateEnvrc(false)
37+
err = box.GenerateEnvrc(false, "init")
3838
if err != nil {
3939
return errors.WithStack(err)
4040
}

internal/impl/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ type Config struct {
2727
// It's differentiated from Packages() which also includes global packages.
2828
RawPackages []string `cue:"[...string]" json:"packages"`
2929

30+
// Env allows specifying env variables
31+
Env map[string]string `json:"env,omitempty"`
3032
// Shell configures the devbox shell environment.
3133
Shell struct {
3234
// InitHook contains commands that will run at shell startup.

internal/impl/devbox.go

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ func InitConfig(dir string, writer io.Writer) (created bool, err error) {
5454
Commit: plansdk.DefaultNixpkgsCommit,
5555
},
5656
}
57+
if featureflag.EnvConfig.Enabled() {
58+
// TODO: after removing feature flag we can decide if we want
59+
// to have omitempty for Env in Config or not.
60+
config.Env = map[string]string{}
61+
}
5762
// package suggestion
5863
pkgsToSuggest, err := initrec.Get(dir)
5964
if err != nil {
@@ -517,12 +522,11 @@ func (d *Devbox) GenerateDockerfile(force bool) error {
517522
}
518523

519524
// generates a .envrc file that makes direnv integration convenient
520-
func (d *Devbox) GenerateEnvrc(force bool) error {
525+
func (d *Devbox) GenerateEnvrc(force bool, source string) error {
521526
envrcfilePath := filepath.Join(d.projectDir, ".envrc")
522527
filesExist := fileutil.Exists(envrcfilePath)
523528
// confirm .envrc doesn't exist and don't overwrite an existing .envrc
524529
if force || !filesExist {
525-
// .envrc file creation
526530
if commandExists("direnv") {
527531
// prompt for direnv allow
528532
var result string
@@ -535,17 +539,22 @@ func (d *Devbox) GenerateEnvrc(force bool) error {
535539
}
536540

537541
if strings.ToLower(result) == "y" {
538-
if !filesExist { // don't overwrite an existing .envrc
539-
err := generate.CreateEnvrc(tmplFS, d.projectDir)
540-
if err != nil {
541-
return errors.WithStack(err)
542-
}
542+
// .envrc file creation
543+
err := generate.CreateEnvrc(tmplFS, d.projectDir)
544+
if err != nil {
545+
return errors.WithStack(err)
543546
}
544547
cmd := exec.Command("direnv", "allow")
545548
err = cmd.Run()
546549
if err != nil {
547550
return errors.WithStack(err)
548551
}
552+
} else if source == "generate" {
553+
// .envrc file creation
554+
err := generate.CreateEnvrc(tmplFS, d.projectDir)
555+
if err != nil {
556+
return errors.WithStack(err)
557+
}
549558
}
550559
}
551560
} else {
@@ -747,6 +756,13 @@ func (d *Devbox) computeNixEnv() (map[string]string, error) {
747756

748757
env["PATH"] = fmt.Sprintf("%s:%s:%s:%s", pluginVirtenvPath, nixProfilePath, nixPath, hostPath)
749758

759+
if featureflag.EnvConfig.Enabled() {
760+
// Include env variables in config
761+
for k, v := range d.configEnvs(env) {
762+
env[k] = v
763+
}
764+
}
765+
750766
return env, nil
751767
}
752768

@@ -881,6 +897,35 @@ func (d *Devbox) pluginVirtenvPath() string {
881897
return filepath.Join(d.projectDir, plugin.VirtenvBinPath)
882898
}
883899

900+
// configEnvs takes the computed env variables (nix + plugin) and adds env
901+
// variables defined in Config. It also parses variables in config
902+
// that are referenced by $VAR or ${VAR} and replaces them with
903+
// their value in the computed env variables. Note, this doesn't
904+
// allow env variables from outside the shell to be referenced so
905+
// no leaked variables are caused by this function.
906+
func (d *Devbox) configEnvs(computedEnv map[string]string) map[string]string {
907+
mapperfunc := func(value string) string {
908+
// Special variables that should return correct value
909+
switch value {
910+
case "PWD":
911+
return d.ProjectDir()
912+
}
913+
// check if referenced variables exists in computed environment
914+
if v, ok := computedEnv[value]; ok {
915+
return v
916+
}
917+
return ""
918+
}
919+
configEnvs := map[string]string{}
920+
// Include env variables in config
921+
for key, value := range d.cfg.Env {
922+
// parse values for "$VAR" or "${VAR}"
923+
parsedValue := os.Expand(value, mapperfunc)
924+
configEnvs[key] = parsedValue
925+
}
926+
return configEnvs
927+
}
928+
884929
// Move to a utility package?
885930
func IsDevboxShellEnabled() bool {
886931
inDevboxShell, err := strconv.ParseBool(os.Getenv("DEVBOX_SHELL_ENABLED"))

0 commit comments

Comments
 (0)