Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ func Load(opts *LoaderOptions) (*types.Project, error) {
if err != nil {
return nil, err
}
mergedProject.FileNames = opts.FileNames
// Record the original user-supplied file list, not opts.FileNames —
// loadExtendProject mutates opts.FileNames in place when it resolves an
// `extends:` directive. Persisting the mutated slice onto the long-lived
// project poisons subsequent Load() calls (e.g. ReloadProject reuses
// p.project.FileNames as input), causing loadExtendProject's Contains
// check to fire on entries the loader itself inserted on the prior pass.
mergedProject.FileNames = fileNames
mergedProject.EnvFileNames = opts.EnvFileNames
mergedProject.IsTuiDisabled = opts.isTuiDisabled || mergedProject.IsTuiDisabled
mergedProject.IsOrderedShutdown = opts.isOrderedShutdown || mergedProject.IsOrderedShutdown
Expand Down
33 changes: 33 additions & 0 deletions src/loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,36 @@ func TestLoadFileWithExtendProject(t *testing.T) {
}
}
}

// TestLoad_ReloadAfterExtends locks in the fix for the reload-after-extends
// bug: Load() must persist the user-supplied FileNames on the returned project,
// not the slice that loadExtendProject mutates while resolving `extends:`.
// ProjectRunner.ReloadProject reuses project.FileNames as input to the next
// Load(); if the extends target leaks into that slice, the Contains check in
// loadExtendProject false-fires on the loader's own bookkeeping.
func TestLoad_ReloadAfterExtends(t *testing.T) {
fixture := filepath.Join("..", "..", "fixtures-code", "process-compose-with-extends.yaml")

opts1 := &LoaderOptions{
FileNames: []string{fixture},
IsInternalLoader: true,
}
project1, err := Load(opts1)
if err != nil {
t.Fatalf("initial load failed: %v", err)
}
if len(project1.FileNames) != 1 || project1.FileNames[0] != fixture {
t.Fatalf("project.FileNames = %v, want [%s]", project1.FileNames, fixture)
}

// Mimic ProjectRunner.ReloadProject: feed the previous project's FileNames
// as the next Load's input. Before the fix this fails with
// "project ... is already specified in files to load".
opts2 := &LoaderOptions{
FileNames: project1.FileNames,
IsInternalLoader: true,
}
if _, err := Load(opts2); err != nil {
t.Fatalf("reload after extends failed: %v", err)
}
}
Loading