fix: recover config from unparseable ENV file entries

An ENV file holding an entry the parser rejects made every config read for that app fail outright, so the existing recovery that drops invalid keys and rewrites the file never had a chance to run. This went unnoticed because two environment test fixtures had gone stale against parser leniency that no longer exists, and the module manifest was out of date enough that its tests refused to run at all.
This commit is contained in:
Jose Diaz-Gonzalez
2026-09-22 15:59:25 -04:00
parent 86e96f0946
commit 47de42cd79
2 changed files with 34 additions and 7 deletions

View File

@@ -396,20 +396,28 @@ func prettyPrintEnvEntries(prefix string, entries map[string]string) string {
func loadFromFile(name string, filename string) (env *Env, err error) {
envMap := make(map[string]string)
dirty := false
if filename == "-" {
envMap, err = godotenv.Parse(os.Stdin)
if err != nil {
return nil, err
}
}
if _, err := os.Stat(filename); err == nil {
envMap, err = godotenv.Read(filename)
if err != nil {
return nil, err
if _, statErr := os.Stat(filename); statErr == nil {
contents, readErr := os.ReadFile(filename)
if readErr != nil {
return nil, readErr
}
parsed, parseErr := godotenv.Unmarshal(string(contents))
if parseErr != nil {
common.LogInfo1(fmt.Sprintf("Discarding unparseable entries from config for %s: %s", name, parseErr))
parsed = parseLineByLine(string(contents))
dirty = true
}
envMap = parsed
}
dirty := false
for k := range envMap {
if err := validateKey(k); err != nil {
common.LogInfo1(fmt.Sprintf("Deleting invalid key %s from config for %s", k, name))
@@ -431,6 +439,23 @@ func loadFromFile(name string, filename string) (env *Env, err error) {
return
}
// parseLineByLine parses ENVFILE contents a line at a time, discarding the
// lines the parser rejects, so that a single malformed entry does not make the
// rest of the environment unreadable
func parseLineByLine(contents string) map[string]string {
envMap := make(map[string]string)
for _, line := range strings.Split(contents, "\n") {
lineMap, err := godotenv.Unmarshal(line)
if err != nil {
continue
}
for key, value := range lineMap {
envMap[key] = value
}
}
return envMap
}
func loadFromFileJSON(name string, filename string) (env *Env, err error) {
envMap := make(map[string]string)
if filename == "-" {

View File

@@ -46,14 +46,16 @@ func TestMerge(t *testing.T) {
e.Merge(e2)
Expect(e.Map()).To(Equal(pairs("BAR", "baz", "FOO", "bar")))
e3, _ := newEnvFromString("FOO='ba \\nz'")
e3, err := newEnvFromString("FOO=\"ba \\nz\"")
Expect(err).NotTo(HaveOccurred())
e.Merge(e3)
Expect(e.Map()).To(Equal(pairs("BAR", "baz", "FOO", "ba \nz")))
}
func TestExport(t *testing.T) {
RegisterTestingT(t)
e, _ := newEnvFromString("BAR='BAZ'\nFOO='b'ar '\nBAZ='a\\nb'")
e, err := newEnvFromString("BAR='BAZ'\nFOO=\"b'ar \"\nBAZ=\"a\\nb\"")
Expect(err).NotTo(HaveOccurred())
Expect(e.Export(ExportFormatEnvfile)).To(Equal("BAR=\"BAZ\"\nBAZ=\"a\\nb\"\nFOO=\"b'ar \""))
Expect(e.Export(ExportFormatDockerArgs)).To(Equal("--env=BAR='BAZ' --env=BAZ='a\nb' --env=FOO='b'\\''ar '"))
Expect(e.Export(ExportFormatDockerArgsKeys)).To(Equal("--env=BAR --env=BAZ --env=FOO"))