Skip to content

Commit 81f1ded

Browse files
committed
linuxsettings: implement default settings for Linux
1 parent 4c32c09 commit 81f1ded

File tree

4 files changed

+156
-2
lines changed

4 files changed

+156
-2
lines changed

docs/enterprise-config.md

+34-1
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,42 @@ those of the [Git configuration][config] settings.
5555
The type of each registry key can be either `REG_SZ` (string) or `REG_DWORD`
5656
(integer).
5757

58-
## macOS/Linux
58+
## macOS
5959

6060
Default configuration setting stores has not been implemented.
6161

62+
## Linux
63+
64+
Default settings values come from the `/etc/git-credential-manager/config.d`
65+
directory. Each file in this directory represents a single settings dictionary.
66+
67+
All files in this directory are read at runtime and merged into a single
68+
collection of settings, in the order they are read from the file system.
69+
To provide a stable ordering, it is recommended to prefix each filename with a
70+
number, e.g. `42-my-settings`.
71+
72+
The format of each file is a simple set of key/value pairs, separated by an
73+
`=` sign, and each line separated by a line-feed (`\n`, LF) character.
74+
Comments are identified by a `#` character at the beginning of a line.
75+
76+
For example:
77+
78+
```text
79+
# /etc/git-credential-manager/config.d/00-example1
80+
credential.noguiprompt=0
81+
```
82+
83+
```text
84+
# /etc/git-credential-manager/config.d/01-example2
85+
credential.trace=true
86+
credential.traceMsAuth=true
87+
```
88+
89+
All settings names and values are the same as the [Git configuration][config]
90+
reference.
91+
92+
> Note: These files are read once at startup. If changes are made to these files
93+
they will not be reflected in an already running process.
94+
6295
[environment]: environment.md
6396
[config]: configuration.md

src/shared/Core/CommandContext.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ public CommandContext()
148148
gitPath,
149149
FileSystem.GetCurrentDirectory()
150150
);
151-
Settings = new Settings(Environment, Git);
151+
Settings = new LinuxSettings(Environment, Git, Trace, FileSystem);
152152
}
153153
else
154154
{

src/shared/Core/Constants.cs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public static class Constants
1515
public const string AuthorityIdAuto = "auto";
1616

1717
public const string GcmDataDirectoryName = ".gcm";
18+
public const string LinuxAppDefaultsDirectoryPath = "/etc/git-credential-manager/config.d";
1819

1920
public static readonly Guid DevBoxPartnerId = new("e3171dd9-9a5f-e5be-b36c-cc7c4f3f3bcf");
2021

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Text;
5+
6+
namespace GitCredentialManager.Interop.Posix;
7+
8+
public class LinuxSettings : Settings
9+
{
10+
private readonly ITrace _trace;
11+
private readonly IFileSystem _fs;
12+
13+
private IDictionary<string, string> _extConfigCache;
14+
15+
/// <summary>
16+
/// Reads settings from Git configuration, environment variables, and defaults from the
17+
/// /etc/git-credential-manager.d app configuration directory.
18+
/// </summary>
19+
public LinuxSettings(IEnvironment environment, IGit git, ITrace trace, IFileSystem fs)
20+
: base(environment, git)
21+
{
22+
EnsureArgument.NotNull(trace, nameof(trace));
23+
EnsureArgument.NotNull(fs, nameof(fs));
24+
25+
_trace = trace;
26+
_fs = fs;
27+
28+
PlatformUtils.EnsurePosix();
29+
}
30+
31+
protected override bool TryGetExternalDefault(string section, string scope, string property, out string value)
32+
{
33+
value = null;
34+
35+
_extConfigCache ??= ReadExternalConfiguration();
36+
37+
string name = string.IsNullOrWhiteSpace(scope)
38+
? $"{section}.{property}"
39+
: $"{section}.{scope}.{property}";
40+
41+
// Check if the setting exists in the configuration
42+
if (!_extConfigCache?.TryGetValue(name, out value) ?? false)
43+
{
44+
// No property exists (or failed to read config)
45+
return false;
46+
}
47+
48+
_trace.WriteLine($"Default setting found in app configuration directory: {name}={value}");
49+
return true;
50+
}
51+
52+
private Dictionary<string, string> ReadExternalConfiguration()
53+
{
54+
try
55+
{
56+
// Check for system-wide config files in /etc/git-credential-manager/config.d and concatenate them together
57+
// in alphabetical order to form a single configuration.
58+
const string configDir = Constants.LinuxAppDefaultsDirectoryPath;
59+
if (!_fs.DirectoryExists(configDir))
60+
{
61+
// No configuration directory exists
62+
return null;
63+
}
64+
65+
// Get all the files in the configuration directory
66+
IEnumerable<string> files = _fs.EnumerateFiles(configDir, "*");
67+
68+
// Read the contents of each file and concatenate them together
69+
var combinedFile = new StringBuilder();
70+
foreach (string file in files)
71+
{
72+
using Stream stream = _fs.OpenFileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
73+
using var reader = new StreamReader(stream);
74+
string contents = reader.ReadToEnd();
75+
combinedFile.Append(contents);
76+
combinedFile.Append('\n');
77+
}
78+
79+
// Parse the configuration in to a dictionary
80+
// Each line is a setting in the format: name=value\n
81+
var config = new Dictionary<string, string>(GitConfigurationKeyComparer.Instance);
82+
string[] lines = combinedFile.ToString().Split('\n', StringSplitOptions.RemoveEmptyEntries);
83+
foreach (string line in lines)
84+
{
85+
if (string.IsNullOrWhiteSpace(line))
86+
{
87+
// Skip empty lines
88+
continue;
89+
}
90+
91+
if (line.TrimStart().StartsWith('#'))
92+
{
93+
// Skip comments
94+
continue;
95+
}
96+
97+
string[] parts = line.Split('=', count: 2);
98+
if (parts.Length != 2)
99+
{
100+
// Invalid line format
101+
continue;
102+
}
103+
104+
string k = parts[0];
105+
string v = parts[1];
106+
config[k] = v;
107+
}
108+
109+
return config;
110+
}
111+
catch (Exception ex)
112+
{
113+
// Reading defaults is not critical to the operation of the application
114+
// so we can ignore any errors and just log the failure.
115+
_trace.WriteLine("Failed to read default setting from app configuration directory.");
116+
_trace.WriteException(ex);
117+
return null;
118+
}
119+
}
120+
}

0 commit comments

Comments
 (0)