Files
coder/cli/gitauth/vscode.go
T
Zach 2488cf0d41 fix(agent): don't overwrite existing vscode git auth settings (#22871)
OverrideVSCodeConfigs previously unconditionally set
`git.useIntegratedAskPass` and `github.gitAuthentication` to false,
clobbering any values provided by template authors via module settings
(e.g. the vscode-web module's settings block). This change only set
these keys when they are not already present, so template-provided
values are preserved.

Registry PR [#758](https://github.com/coder/registry/pull/758) fixed the
module side (run.sh merges template-author settings into the existing
settings.json instead of overwriting the file). But the agent still
unconditionally stamped false onto both keys before the script ran, so
the merge base always contained the agent's values and template authors
couldn't set them to anything else. This change fixes the agent side by
only writing defaults when the keys are absent.
2026-03-12 13:39:24 -06:00

96 lines
2.9 KiB
Go

package gitauth
import (
"encoding/json"
"errors"
"os"
"path/filepath"
"github.com/adrg/xdg"
"github.com/spf13/afero"
"golang.org/x/xerrors"
)
// OverrideVSCodeConfigs overwrites a few properties to consume
// GIT_ASKPASS from the host instead of VS Code-specific authentication.
func OverrideVSCodeConfigs(fs afero.Fs) error {
home, err := os.UserHomeDir()
if err != nil {
return err
}
mutate := func(m map[string]interface{}) {
// These defaults prevent VS Code from overriding
// GIT_ASKPASS and using its own GitHub authentication,
// which would circumvent cloning with Coder-configured
// providers. We only set them if they are not already
// present so that template authors can override them
// via module settings (e.g. the vscode-web module).
if _, ok := m["git.useIntegratedAskPass"]; !ok {
m["git.useIntegratedAskPass"] = false
}
if _, ok := m["github.gitAuthentication"]; !ok {
m["github.gitAuthentication"] = false
}
}
for _, configPath := range []string{
// code-server's default configuration path.
filepath.Join(xdg.DataHome, "code-server", "Machine", "settings.json"),
// vscode-remote's default configuration path.
filepath.Join(home, ".vscode-server", "data", "Machine", "settings.json"),
// vscode-insiders' default configuration path.
filepath.Join(home, ".vscode-insiders-server", "data", "Machine", "settings.json"),
// cursor default configuration path.
filepath.Join(home, ".cursor-server", "data", "Machine", "settings.json"),
// windsurf default configuration path.
filepath.Join(home, ".windsurf-server", "data", "Machine", "settings.json"),
// vscodium default configuration path.
filepath.Join(home, ".vscodium-server", "data", "Machine", "settings.json"),
} {
_, err := fs.Stat(configPath)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
return xerrors.Errorf("stat %q: %w", configPath, err)
}
m := map[string]interface{}{}
mutate(m)
data, err := json.MarshalIndent(m, "", "\t")
if err != nil {
return xerrors.Errorf("marshal: %w", err)
}
err = fs.MkdirAll(filepath.Dir(configPath), 0o700)
if err != nil {
return xerrors.Errorf("mkdir all: %w", err)
}
err = afero.WriteFile(fs, configPath, data, 0o600)
if err != nil {
return xerrors.Errorf("write %q: %w", configPath, err)
}
continue
}
data, err := afero.ReadFile(fs, configPath)
if err != nil {
return xerrors.Errorf("read %q: %w", configPath, err)
}
mapping := map[string]interface{}{}
err = json.Unmarshal(data, &mapping)
if err != nil {
return xerrors.Errorf("unmarshal %q: %w", configPath, err)
}
mutate(mapping)
data, err = json.MarshalIndent(mapping, "", "\t")
if err != nil {
return xerrors.Errorf("marshal %q: %w", configPath, err)
}
err = afero.WriteFile(fs, configPath, data, 0o600)
if err != nil {
return xerrors.Errorf("write %q: %w", configPath, err)
}
}
return nil
}