Skip to content

Commit 806db99

Browse files
authored
Merge pull request #1833 from FabianKramm/master
refactor: alwaysResolve & resolve after config expressions
2 parents 5d9c24a + c122699 commit 806db99

File tree

12 files changed

+144
-34
lines changed

12 files changed

+144
-34
lines changed

docs/pages/configuration/variables/source-command.mdx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,26 @@ vars:
8888

8989
### `default`
9090

91-
If the command returns nothing, this is the value that will be used for this variable.
91+
If the command returns nothing, this is the value that will be used for this variable.
92+
93+
### `alwaysResolve`
94+
95+
If enabled, the variable will be loaded even though it is not used within the config. This might be useful for variables that should be resolved after a config expression is applied. E.g.:
96+
97+
`devspace.yaml`:
98+
```
99+
vars:
100+
- name: my-var
101+
value: my-value
102+
alwaysResolve: true
103+
hooks:
104+
- name: my-hook
105+
command: $(cat command.txt)
106+
events: ["after:deploy"]
107+
```
108+
109+
`command.txt`:
110+
```
111+
echo Hello ${my-var}!
112+
```
113+

docs/pages/configuration/variables/source-env.mdx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,24 @@ If `source` is `env` and the environment variable is **not** defined, DevSpace w
3434
### `default`
3535

3636
<FragmentVarsDefault/>
37+
38+
### `alwaysResolve`
39+
40+
If enabled, the variable will be loaded even though it is not used within the config. This might be useful for variables that should be resolved after a config expression is applied. E.g.:
41+
42+
`devspace.yaml`:
43+
```
44+
vars:
45+
- name: my-var
46+
value: my-value
47+
alwaysResolve: true
48+
hooks:
49+
- name: my-hook
50+
command: $(cat command.txt)
51+
events: ["after:deploy"]
52+
```
53+
54+
`command.txt`:
55+
```
56+
echo Hello ${my-var}!
57+
```

docs/pages/configuration/variables/source-input.mdx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,3 +177,24 @@ If the provided value does **<u>not</u>** match the regex in `validationPattern`
177177

178178
### `validationMessage`
179179
The `validationMessage` option expects a string stating an error message that is shown to the user when providing a value for the variable that does not match the regex provided in [`validationPattern`](#validationpattern).
180+
181+
### `alwaysResolve`
182+
183+
If enabled, the variable will be loaded even though it is not used within the config. This might be useful for variables that should be resolved after a config expression is applied. E.g.:
184+
185+
`devspace.yaml`:
186+
```
187+
vars:
188+
- name: my-var
189+
value: my-value
190+
alwaysResolve: true
191+
hooks:
192+
- name: my-hook
193+
command: $(cat command.txt)
194+
events: ["after:deploy"]
195+
```
196+
197+
`command.txt`:
198+
```
199+
echo Hello ${my-var}!
200+
```

docs/pages/configuration/variables/source-none.mdx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,24 @@ The `value` option expects a string, integer or boolean defining the value for t
4141

4242
- The used variable is defined before the variable that wants to use it
4343
- The used variable is a predefined variable
44+
45+
### `alwaysResolve`
46+
47+
If enabled, the variable will be loaded even though it is not used within the config. This might be useful for variables that should be resolved after a config expression is applied. E.g.:
48+
49+
`devspace.yaml`:
50+
```
51+
vars:
52+
- name: my-var
53+
value: my-value
54+
alwaysResolve: true
55+
hooks:
56+
- name: my-hook
57+
command: $(cat command.txt)
58+
events: ["after:deploy"]
59+
```
60+
61+
`command.txt`:
62+
```
63+
echo Hello ${my-var}!
64+
```

docs/pages/fragments/config-vars.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
vars: # struct[] | Array of config variables
33
- name: CONFIG_VAR # string | Name of the config variable
44
value: my-value # any | The fixed value of the config variable
5+
alwaysResolve: false # bool | If enabled, the variable will be loaded even though it is not used in config
56
question: "What is CONFIG_VAR?" # string | Question to ask the user if no value is found for variable
67
options: [] # string[] | Options for picker (selector) to show to user (to choose a value for variable)
78
noCache: false # bool | Disable caching and prompt for value on every run.

e2e/tests/config/config.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package config
33
import (
44
"bytes"
55
"fmt"
6+
"github.com/loft-sh/devspace/pkg/devspace/config/loader/variable"
67
"os"
78
"path/filepath"
89

@@ -41,15 +42,18 @@ var _ = DevSpaceDescribe("config", func() {
4142

4243
configBuffer := &bytes.Buffer{}
4344
printCmd := &cmd.PrintCmd{
44-
GlobalFlags: &flags.GlobalFlags{},
45-
Out: configBuffer,
46-
SkipInfo: true,
45+
GlobalFlags: &flags.GlobalFlags{
46+
Namespace: "test-ns",
47+
},
48+
Out: configBuffer,
49+
SkipInfo: true,
4750
}
4851

4952
err = printCmd.Run(f)
5053
framework.ExpectNoError(err)
5154
framework.ExpectLocalFileContentsImmediately(filepath.Join(tempDir, "out.txt"), "test-testimage-latest-dep1")
5255
framework.ExpectLocalFileContentsImmediately(filepath.Join(tempDir, "out2.txt"), "Done")
56+
framework.ExpectLocalFileContentsImmediately(filepath.Join(tempDir, "out3.txt"), "test-ns-resolved-${NOT_RESOLVED}")
5357
})
5458

5559
ginkgo.It("should load multiple profiles in order via --profile", func() {
@@ -1648,7 +1652,7 @@ var _ = DevSpaceDescribe("config", func() {
16481652
framework.ExpectNoError(err)
16491653

16501654
// check if variables were loaded correctly
1651-
framework.ExpectEqual(len(config.Variables()), 4)
1655+
framework.ExpectEqual(len(config.Variables()), 4+len(variable.AlwaysResolvePredefinedVars))
16521656
framework.ExpectEqual(len(config.Generated().Vars), 1)
16531657
framework.ExpectEqual(config.Generated().Vars["TEST_1"], "test")
16541658
framework.ExpectEqual(len(dependencies), 1)
@@ -1675,7 +1679,7 @@ var _ = DevSpaceDescribe("config", func() {
16751679
framework.ExpectNoError(err)
16761680

16771681
// config
1678-
framework.ExpectEqual(len(config.Variables()), 3)
1682+
framework.ExpectEqual(len(config.Variables()), 3+len(variable.AlwaysResolvePredefinedVars))
16791683
framework.ExpectEqual(len(config.Generated().Vars), 2)
16801684
framework.ExpectEqual(config.Generated().Vars["NOT_USED"], "test")
16811685
framework.ExpectEqual(config.Generated().Vars["TEST_2"], "dep1")
@@ -1698,7 +1702,7 @@ var _ = DevSpaceDescribe("config", func() {
16981702
framework.ExpectNoError(err)
16991703

17001704
// check if default config variables were loaded correctly
1701-
framework.ExpectEqual(len(config.Variables()), 2)
1705+
framework.ExpectEqual(len(config.Variables()), 2+len(variable.AlwaysResolvePredefinedVars))
17021706
framework.ExpectEqual(len(config.Generated().Vars), 1)
17031707
framework.ExpectEqual(config.Generated().Vars["NAME"], "default")
17041708
framework.ExpectEqual(len(dependencies), 0)
@@ -1713,7 +1717,7 @@ var _ = DevSpaceDescribe("config", func() {
17131717
framework.ExpectNoError(err)
17141718

17151719
// check if custom config variables were loaded correctly
1716-
framework.ExpectEqual(len(customConfig.Variables()), 2)
1720+
framework.ExpectEqual(len(customConfig.Variables()), 2+len(variable.AlwaysResolvePredefinedVars))
17171721
framework.ExpectEqual(len(customConfig.Generated().Vars), 1)
17181722
framework.ExpectEqual(customConfig.Generated().Vars["NAME"], "custom")
17191723
framework.ExpectEqual(len(customDependencies), 0)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
echo -n '${devspace.namespace}-${ALREADY_RESOLVED}-${NOT_RESOLVED}' > out3.txt

e2e/tests/config/testdata/runtime-variables/devspace.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ version: v1beta11
22
vars:
33
- name: OTHER
44
value: test
5+
- name: NOT_RESOLVED
6+
value: not-resolved
7+
- name: ALREADY_RESOLVED
8+
value: resolved
9+
alwaysResolve: true
510
dependencies:
611
- name: dep1
712
source:
@@ -25,3 +30,6 @@ hooks:
2530
# This should print Done
2631
echo -n ${runtime.hooks.test-123.stdout} > out2.txt
2732
events: ["after:resolveDependencies"]
33+
- name: test-125
34+
command: $(cat command.txt)
35+
events: ["after:resolveDependencies"]

e2e/tests/init/init.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package init
33
import (
44
"bytes"
55
"fmt"
6+
"github.com/loft-sh/devspace/pkg/devspace/config/loader/variable"
67
"os"
78
"path/filepath"
89
"strings"
@@ -57,7 +58,8 @@ var _ = DevSpaceDescribe("init", func() {
5758
config, _, err := framework.LoadConfig(f, filepath.Join(tempDir, "devspace.yaml"))
5859
framework.ExpectNoError(err)
5960

60-
framework.ExpectEqual(config.Variables(), map[string]interface{}{"IMAGE": "username/app"})
61+
framework.ExpectEqual(len(config.Variables()), 1+len(variable.AlwaysResolvePredefinedVars))
62+
framework.ExpectEqual(config.Variables()["IMAGE"], "username/app")
6163
})
6264

6365
ginkgo.It("should create devspace.yml from docker-compose.yaml", func() {

pkg/devspace/config/loader/variable/resolver.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"github.com/pkg/errors"
1616
)
1717

18+
var AlwaysResolvePredefinedVars = []string{"devspace.version", "devspace.random", "devspace.profile", "devspace.userHome", "devspace.timestamp", "devspace.context", "devspace.namespace"}
19+
1820
// NewResolver creates a new resolver that caches resolved variables in memory and in the provided cache
1921
func NewResolver(cache map[string]string, predefinedVariableOptions *PredefinedVariableOptions, vars []*latest.Variable, log log.Logger) Resolver {
2022
return &resolver{
@@ -172,19 +174,12 @@ func (r *resolver) findAndFillVariables(haystack interface{}, exclude []*regexp.
172174
}
173175

174176
// resolve used defined variables
175-
if len(r.vars) > 0 {
176-
newVars := []*latest.Variable{}
177-
for _, v := range r.vars {
178-
if varsUsed[strings.TrimSpace(v.Name)] {
179-
newVars = append(newVars, v)
180-
}
181-
}
182-
183-
for _, definition := range newVars {
184-
name := strings.TrimSpace(definition.Name)
177+
for _, v := range r.vars {
178+
if v.AlwaysResolve || varsUsed[strings.TrimSpace(v.Name)] {
179+
name := strings.TrimSpace(v.Name)
185180

186181
// resolve the variable with definition
187-
_, err := r.resolve(name, definition)
182+
_, err := r.resolve(name, v)
188183
if err != nil {
189184
return nil, err
190185
}
@@ -199,6 +194,12 @@ func (r *resolver) findAndFillVariables(haystack interface{}, exclude []*regexp.
199194
}
200195
}
201196

197+
// try resolving predefined variables
198+
for _, name := range AlwaysResolvePredefinedVars {
199+
// ignore errors here as those variables are probably not used anyways
200+
_, _ = r.resolve(name, nil)
201+
}
202+
202203
return r.fillVariables(haystack, exclude)
203204
}
204205

0 commit comments

Comments
 (0)