Skip to content

fix: merge VS Code settings instead of overwriting them #19009

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
20 changes: 14 additions & 6 deletions cli/gitauth/vscode.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,27 @@ import (
"golang.org/x/xerrors"
)

// OverrideVSCodeConfigs overwrites a few properties to consume
// GIT_ASKPASS from the host instead of VS Code-specific authentication.
// OverrideVSCodeConfigs merges essential Coder settings with existing VS Code settings
// to ensure GIT_ASKPASS and Git authentication work properly with Coder.
func OverrideVSCodeConfigs(fs afero.Fs) error {
home, err := os.UserHomeDir()
if err != nil {
return err
}
mutate := func(m map[string]interface{}) {
// Define the essential settings that Coder needs to override
coderSettings := map[string]interface{}{
// This prevents VS Code from overriding GIT_ASKPASS, which
// we use to automatically authenticate Git providers.
m["git.useIntegratedAskPass"] = false
"git.useIntegratedAskPass": false,
// This prevents VS Code from using it's own GitHub authentication
// which would circumvent cloning with Coder-configured providers.
m["github.gitAuthentication"] = false
"github.gitAuthentication": false,
}
mutate := func(m map[string]interface{}) {
// Merge Coder's essential settings with existing settings
for key, value := range coderSettings {
m[key] = value
}
}

for _, configPath := range []string{
Expand All @@ -47,7 +54,8 @@ func OverrideVSCodeConfigs(fs afero.Fs) error {
return xerrors.Errorf("stat %q: %w", configPath, err)
}

m := map[string]interface{}{}
// Create new settings file with only Coder's essential settings
Copy link
Preview

Copilot AI Jul 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is misleading as it states 'only Coder's essential settings' but the code actually creates an empty map that will be populated by the mutate function with Coder settings. Consider updating to 'Create new settings file with Coder's essential settings' to be more accurate.

Suggested change
// Create new settings file with only Coder's essential settings
// Create new settings file and populate it with Coder's essential settings

Copilot uses AI. Check for mistakes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! You're absolutely right - the comment is misleading. I'll update it to be more accurate: "Create new settings file and populate it with Coder's essential settings". Thanks for the suggestion! 👍

m := make(map[string]interface{})
mutate(m)
data, err := json.MarshalIndent(m, "", "\t")
if err != nil {
Expand Down
100 changes: 95 additions & 5 deletions cli/gitauth/vscode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@ func TestOverrideVSCodeConfigs(t *testing.T) {
require.Equal(t, false, mapping["github.gitAuthentication"])
}
})
t.Run("Append", func(t *testing.T) {
t.Run("MergeWithExistingSettings", func(t *testing.T) {
t.Parallel()
fs := afero.NewMemMapFs()
mapping := map[string]interface{}{
"hotdogs": "something",
// Create existing settings with user preferences
existingSettings := map[string]interface{}{
"workbench.colorTheme": "Dracula",
"editor.fontSize": 14,
"editor.tabSize": 2,
"files.autoSave": "onWindowChange",
}
data, err := json.Marshal(mapping)
data, err := json.MarshalIndent(existingSettings, "", "\t")
require.NoError(t, err)
for _, configPath := range configPaths {
err = afero.WriteFile(fs, configPath, data, 0o600)
Expand All @@ -56,9 +60,95 @@ func TestOverrideVSCodeConfigs(t *testing.T) {
mapping := map[string]interface{}{}
err = json.Unmarshal(data, &mapping)
require.NoError(t, err)
// Verify Coder settings are applied
require.Equal(t, false, mapping["git.useIntegratedAskPass"])
require.Equal(t, false, mapping["github.gitAuthentication"])
// Verify user settings are preserved
require.Equal(t, "Dracula", mapping["workbench.colorTheme"])
require.Equal(t, float64(14), mapping["editor.fontSize"])
require.Equal(t, float64(2), mapping["editor.tabSize"])
require.Equal(t, "onWindowChange", mapping["files.autoSave"])
// Verify no duplication - should have exactly 6 settings
require.Len(t, mapping, 6)
}
})

t.Run("MergeWithExistingCoderSettings", func(t *testing.T) {
t.Parallel()
fs := afero.NewMemMapFs()
// Create existing settings that include Coder-specific settings with different values
existingSettings := map[string]interface{}{
"workbench.colorTheme": "Dark+",
"git.useIntegratedAskPass": true, // This should be overridden to false
"github.gitAuthentication": true, // This should be overridden to false
"editor.wordWrap": "on",
"terminal.integrated.shell.linux": "/bin/bash",
}
data, err := json.MarshalIndent(existingSettings, "", "\t")
require.NoError(t, err)
for _, configPath := range configPaths {
err = afero.WriteFile(fs, configPath, data, 0o600)
require.NoError(t, err)
}
err = gitauth.OverrideVSCodeConfigs(fs)
require.NoError(t, err)
for _, configPath := range configPaths {
data, err := afero.ReadFile(fs, configPath)
require.NoError(t, err)
mapping := map[string]interface{}{}
err = json.Unmarshal(data, &mapping)
require.NoError(t, err)
// Verify Coder settings override existing values
require.Equal(t, false, mapping["git.useIntegratedAskPass"])
require.Equal(t, false, mapping["github.gitAuthentication"])
// Verify user settings are preserved
require.Equal(t, "Dark+", mapping["workbench.colorTheme"])
require.Equal(t, "on", mapping["editor.wordWrap"])
require.Equal(t, "/bin/bash", mapping["terminal.integrated.shell.linux"])
// Verify no duplication - should have exactly 5 settings
require.Len(t, mapping, 5)
}
})

t.Run("ValidJSONOutput", func(t *testing.T) {
t.Parallel()
fs := afero.NewMemMapFs()
// Test with complex existing settings to ensure valid JSON output
existingSettings := map[string]interface{}{
"workbench.colorCustomizations": map[string]interface{}{
"editor.background": "#1e1e1e",
"sideBar.background": "#252526",
},
"extensions.recommendations": []string{"ms-python.python", "golang.go"},
"git.useIntegratedAskPass": true,
"editor.rulers": []int{80, 120},
}
data, err := json.MarshalIndent(existingSettings, "", "\t")
require.NoError(t, err)
for _, configPath := range configPaths {
err = afero.WriteFile(fs, configPath, data, 0o600)
require.NoError(t, err)
}
err = gitauth.OverrideVSCodeConfigs(fs)
require.NoError(t, err)
for _, configPath := range configPaths {
data, err := afero.ReadFile(fs, configPath)
require.NoError(t, err)
// Verify the output is valid JSON
mapping := map[string]interface{}{}
err = json.Unmarshal(data, &mapping)
require.NoError(t, err, "Output should be valid JSON")
// Verify complex structures are preserved
colorCustomizations, ok := mapping["workbench.colorCustomizations"].(map[string]interface{})
require.True(t, ok, "Complex objects should be preserved")
require.Equal(t, "#1e1e1e", colorCustomizations["editor.background"])
// Verify arrays are preserved
recommendations, ok := mapping["extensions.recommendations"].([]interface{})
require.True(t, ok, "Arrays should be preserved")
require.Len(t, recommendations, 2)
// Verify Coder settings are applied
require.Equal(t, false, mapping["git.useIntegratedAskPass"])
require.Equal(t, false, mapping["github.gitAuthentication"])
require.Equal(t, "something", mapping["hotdogs"])
}
})
}
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy