From e67fd35be516a2798a61e1ab8275a4c7327312a7 Mon Sep 17 00:00:00 2001 From: Samuel K Date: Thu, 23 Jul 2026 22:46:49 -0500 Subject: [PATCH] fix(devcontainer): exclude synthesized devcontainer configs from git Synthesized configs written for image and auto-detected sources (.devcontainer.devsy.json, .devcontainer.json) were never added to .git/info/exclude, so they showed up as untracked files. Only the imported-path source excluded its artifact. Add the exclude call to saveSynthesizedConfig and getDefaultConfig so every generated config is ignored. --- pkg/devcontainer/config.go | 15 ++++++++++----- pkg/devcontainer/import_external_test.go | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/pkg/devcontainer/config.go b/pkg/devcontainer/config.go index d6e32de50..8fbe49e22 100644 --- a/pkg/devcontainer/config.go +++ b/pkg/devcontainer/config.go @@ -339,13 +339,14 @@ func CleanupImportedDevContainers(workspaceFolder string) error { func (r *runner) saveSynthesizedConfig( c *config.DevContainerConfig, ) (*config.DevContainerConfig, error) { - c.Origin = path.Join( - filepath.ToSlash(r.localWorkspaceFolder), - ".devcontainer."+pkgconfig.BinaryName+".json", - ) + name := ".devcontainer." + pkgconfig.BinaryName + ".json" + c.Origin = path.Join(filepath.ToSlash(r.localWorkspaceFolder), name) if err := config.SaveDevContainerJSON(c); err != nil { return nil, fmt.Errorf("write synthesized devcontainer.json: %w", err) } + if err := r.excludeFromGit(name); err != nil { + log.Debugf("could not add synthesized devcontainer to git exclude: %v", err) + } return c, nil } @@ -361,10 +362,14 @@ func (r *runner) getDefaultConfig( defaultConfig = language.DefaultConfig(r.localWorkspaceFolder) } - defaultConfig.Origin = path.Join(filepath.ToSlash(r.localWorkspaceFolder), ".devcontainer.json") + const name = ".devcontainer.json" + defaultConfig.Origin = path.Join(filepath.ToSlash(r.localWorkspaceFolder), name) if err := config.SaveDevContainerJSON(defaultConfig); err != nil { return nil, fmt.Errorf("write default devcontainer.json: %w", err) } + if err := r.excludeFromGit(name); err != nil { + log.Debugf("could not add default devcontainer to git exclude: %v", err) + } return defaultConfig, nil } diff --git a/pkg/devcontainer/import_external_test.go b/pkg/devcontainer/import_external_test.go index 26eddb069..ab17f3a3d 100644 --- a/pkg/devcontainer/import_external_test.go +++ b/pkg/devcontainer/import_external_test.go @@ -6,6 +6,8 @@ import ( "strings" "testing" + pkgconfig "github.com/devsy-org/devsy/pkg/config" + "github.com/devsy-org/devsy/pkg/devcontainer/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -157,6 +159,22 @@ func TestImportExternalDevContainer_AddsGitExclude(t *testing.T) { assert.Contains(t, string(exclude), importedProfileParent+"/"+importedProfileName) } +func TestSaveSynthesizedConfig_AddsGitExclude(t *testing.T) { + ws := t.TempDir() + require.NoError(t, os.MkdirAll(filepath.Join(ws, ".git", "info"), 0o750)) + r := &runner{localWorkspaceFolder: ws} + + _, err := r.saveSynthesizedConfig(&config.DevContainerConfig{ + ImageContainer: config.ImageContainer{Image: "alpine"}, + }) + require.NoError(t, err) + + // #nosec G304 -- test path + exclude, err := os.ReadFile(filepath.Join(ws, ".git", "info", "exclude")) + require.NoError(t, err) + assert.Contains(t, string(exclude), "/.devcontainer."+pkgconfig.BinaryName+".json") +} + func TestImportExternalDevContainer_NonGitRepoSkipsExclude(t *testing.T) { external := t.TempDir() writeFile(t, filepath.Join(external, "devcontainer.json"), `{"image":"alpine"}`)