Repository navigation
Conversation
With the expand option, a variable that refers to itself (SELF=a-${SELF})
or two variables that refer to each other made getRawEnv call os.Expand
until the stack overflowed. That is a fatal error, so it cannot be
recovered and the whole program dies. Track the variables being expanded
and expand a reference back to one of them to an empty string, the same
as an undefined variable.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #447 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 3 3
Lines 427 432 +5
=========================================
+ Hits 427 432 +5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
caarlos0
left a comment
There was a problem hiding this comment.
Automated review by GitHub Copilot. Changes requested for one confirmed compatibility regression. The reproduction passes on the base revision and fails on this head. Only focused expansion tests were run; the build was not independently verified.
|
|
||
| if fieldParams.Expand { | ||
| val = os.Expand(val, opts.getRawEnv) | ||
| val = opts.expand(val, map[string]bool{fieldParams.Key: true}) |
There was a problem hiding this comment.
P2: Preserve valid references to earlier cached values. Marking the current key as visited before reading the cache rejects references that are not cyclic. For example:
type config struct {
RawPort string `env:"PORT" envDefault:"3000"`
Port int `env:"PORT,expand" envDefault:"${PORT}"`
}
var cfg config
err := ParseWithOptions(&cfg, Options{
Environment: map[string]string{},
})The first field caches "3000" under PORT. The second field should read that value, but the cycle guard returns an empty string before checking the cache. Parsing returns no error and leaves Port at 0. I confirmed that the same test passes on the base with Port == 3000 and fails on this head with Port == 0.
Do not mark the current key as cyclic when its reference resolves to a nonempty value cached by an earlier field. Keep the recursive cycle guard. Add this case as a regression test with an explicitly empty environment map, asserting no error and both field values.
With
expand, a variable that refers back to itself crashes the program:SELF=a-${SELF}andA=${B}/B=${A}do the same.getRawEnvcallsos.Expandwith itself as the mapping function, so a cycle never ends. A stack overflow is a fatal error, not a panic, so callers can'trecoverfrom it, and a single bad value in the environment takes the whole process down.The fix keeps the recursive expansion (
TestParseExpandWithDefaultOptiondepends on it) but tracks which variables are being expanded. A reference back to one of them expands to an empty string, the same as an undefined variable. The field's own key starts in that set, which is what catchesSELF=a-${SELF}. A variable used twice without a cycle still works:${ONE}${ONE}gives11.Added
TestParseExpandCyclicReference(self reference, a two-variable loop, and the repeated variable case). Before the fix it dies with the stack overflow.go test ./...andgo vet ./...pass, and golangci-lint reports nothing new.