-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
linuxsettings: implement default settings for Linux
- Loading branch information
1 parent
4c32c09
commit bb94df1
Showing
9 changed files
with
297 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
src/shared/Core.Tests/Interop/Linux/LinuxConfigParserTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
using System.Collections.Generic; | ||
using GitCredentialManager.Interop.Linux; | ||
using GitCredentialManager.Tests.Objects; | ||
using Xunit; | ||
|
||
namespace GitCredentialManager.Tests.Interop.Linux; | ||
|
||
public class LinuxConfigParserTests | ||
{ | ||
[Fact] | ||
public void LinuxConfigParser_Parse() | ||
{ | ||
const string contents = | ||
""" | ||
# | ||
# This is a config file complete with comments | ||
# and empty.. | ||
# lines, as well as lines with.. | ||
# | ||
# only whitespace (like above ^), and.. | ||
invalid lines like this one, not a comment | ||
# Here's the first real properties: | ||
core.overrideMe=This is the first config value | ||
baz.specialChars=I contain special chars like = in my value # this is a comment | ||
# and let's have with a comment that also contains a = in side | ||
# | ||
core.overrideMe=This is the second config value | ||
bar.scope.foo=123456 | ||
core.overrideMe=This is the correct value | ||
###### comments that start ## with whitespace and extra ## inside | ||
strings.one="here we have a dq string" | ||
strings.two='here we have a sq string' | ||
strings.three= 'here we have another sq string' # have another sq string | ||
strings.four="this has 'nested quotes' inside" | ||
strings.five='mixed "quotes" the other way around' | ||
strings.six='this has an \'escaped\' set of quotes' | ||
"""; | ||
|
||
var expected = new Dictionary<string, string> | ||
{ | ||
["core.overrideMe"] = "This is the correct value", | ||
["bar.scope.foo"] = "123456", | ||
["baz.specialChars"] = "I contain special chars like = in my value", | ||
["strings.one"] = "here we have a dq string", | ||
["strings.two"] = "here we have a sq string", | ||
["strings.three"] = "here we have another sq string", | ||
["strings.four"] = "this has 'nested quotes' inside", | ||
["strings.five"] = "mixed \"quotes\" the other way around", | ||
["strings.six"] = "this has an \\'escaped\\' set of quotes", | ||
}; | ||
|
||
var parser = new LinuxConfigParser(new NullTrace()); | ||
|
||
Assert.Equal(expected, parser.Parse(contents)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using System.Collections.Generic; | ||
using GitCredentialManager.Interop.Linux; | ||
using GitCredentialManager.Tests.Objects; | ||
using Xunit; | ||
|
||
namespace GitCredentialManager.Tests.Interop.Linux; | ||
|
||
public class LinuxSettingsTests | ||
{ | ||
[LinuxFact] | ||
public void LinuxSettings_TryGetExternalDefault_CombinesFiles() | ||
{ | ||
var env = new TestEnvironment(); | ||
var git = new TestGit(); | ||
var trace = new NullTrace(); | ||
var fs = new TestFileSystem(); | ||
|
||
var utf8 = EncodingEx.UTF8NoBom; | ||
|
||
fs.Directories = new HashSet<string> | ||
{ | ||
"/", | ||
"/etc", | ||
"/etc/git-credential-manager", | ||
"/etc/git-credential-manager/config.d" | ||
}; | ||
|
||
const string config1 = "core.overrideMe=value1"; | ||
const string config2 = "core.overrideMe=value2"; | ||
const string config3 = "core.overrideMe=value3"; | ||
|
||
fs.Files = new Dictionary<string, byte[]> | ||
{ | ||
["/etc/git-credential-manager/config.d/01-first"] = utf8.GetBytes(config1), | ||
["/etc/git-credential-manager/config.d/02-second"] = utf8.GetBytes(config2), | ||
["/etc/git-credential-manager/config.d/03-third"] = utf8.GetBytes(config3), | ||
}; | ||
|
||
var settings = new LinuxSettings(env, git, trace, fs); | ||
|
||
bool result = settings.TryGetExternalDefault( | ||
"core", null, "overrideMe", out string value); | ||
|
||
Assert.True(result); | ||
Assert.Equal("value3", value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text.RegularExpressions; | ||
|
||
namespace GitCredentialManager.Interop.Linux; | ||
|
||
public class LinuxConfigParser | ||
{ | ||
#if NETFRAMEWORK | ||
private const string SQ = "'"; | ||
private const string DQ = "\""; | ||
private const string Hash = "#"; | ||
#else | ||
private const char SQ = '\''; | ||
private const char DQ = '"'; | ||
private const char Hash = '#'; | ||
#endif | ||
|
||
private static readonly Regex LineRegex = new(@"^\s*(?<key>[a-zA-Z0-9\.-]+)\s*=\s*(?<value>.+?)\s*(?:#.*)?$"); | ||
|
||
private readonly ITrace _trace; | ||
|
||
public LinuxConfigParser(ITrace trace) | ||
{ | ||
EnsureArgument.NotNull(trace, nameof(trace)); | ||
|
||
_trace = trace; | ||
} | ||
|
||
public IDictionary<string, string> Parse(string content) | ||
{ | ||
var result = new Dictionary<string, string>(GitConfigurationKeyComparer.Instance); | ||
|
||
IEnumerable<string> lines = content.Split(['\n'], StringSplitOptions.RemoveEmptyEntries); | ||
|
||
foreach (string line in lines) | ||
{ | ||
// Ignore empty lines or full-line comments | ||
var trimmedLine = line.Trim(); | ||
if (string.IsNullOrEmpty(trimmedLine) || trimmedLine.StartsWith(Hash)) | ||
continue; | ||
|
||
var match = LineRegex.Match(trimmedLine); | ||
if (!match.Success) | ||
{ | ||
_trace.WriteLine($"Invalid config line format: {line}"); | ||
continue; | ||
} | ||
|
||
string key = match.Groups["key"].Value; | ||
string value = match.Groups["value"].Value; | ||
|
||
// Remove enclosing quotes from the value, if any | ||
if ((value.StartsWith(DQ) && value.EndsWith(DQ)) || (value.StartsWith(SQ) && value.EndsWith(SQ))) | ||
value = value.Substring(1, value.Length - 2); | ||
|
||
result[key] = value; | ||
} | ||
|
||
return result; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Text; | ||
using Avalonia.Markup.Xaml.MarkupExtensions; | ||
|
||
namespace GitCredentialManager.Interop.Linux; | ||
|
||
public class LinuxSettings : Settings | ||
{ | ||
private readonly ITrace _trace; | ||
private readonly IFileSystem _fs; | ||
|
||
private IDictionary<string, string> _extConfigCache; | ||
|
||
/// <summary> | ||
/// Reads settings from Git configuration, environment variables, and defaults from the | ||
/// /etc/git-credential-manager.d app configuration directory. | ||
/// </summary> | ||
public LinuxSettings(IEnvironment environment, IGit git, ITrace trace, IFileSystem fs) | ||
: base(environment, git) | ||
{ | ||
EnsureArgument.NotNull(trace, nameof(trace)); | ||
EnsureArgument.NotNull(fs, nameof(fs)); | ||
|
||
_trace = trace; | ||
_fs = fs; | ||
|
||
PlatformUtils.EnsureLinux(); | ||
} | ||
|
||
protected internal override bool TryGetExternalDefault(string section, string scope, string property, out string value) | ||
{ | ||
value = null; | ||
|
||
_extConfigCache ??= ReadExternalConfiguration(); | ||
|
||
string name = string.IsNullOrWhiteSpace(scope) | ||
? $"{section}.{property}" | ||
: $"{section}.{scope}.{property}"; | ||
|
||
// Check if the setting exists in the configuration | ||
if (!_extConfigCache?.TryGetValue(name, out value) ?? false) | ||
{ | ||
// No property exists (or failed to read config) | ||
return false; | ||
} | ||
|
||
_trace.WriteLine($"Default setting found in app configuration directory: {name}={value}"); | ||
return true; | ||
} | ||
|
||
private IDictionary<string, string> ReadExternalConfiguration() | ||
{ | ||
try | ||
{ | ||
// Check for system-wide config files in /etc/git-credential-manager/config.d and concatenate them together | ||
// in alphabetical order to form a single configuration. | ||
const string configDir = Constants.LinuxAppDefaultsDirectoryPath; | ||
if (!_fs.DirectoryExists(configDir)) | ||
{ | ||
// No configuration directory exists | ||
return null; | ||
} | ||
|
||
// Get all the files in the configuration directory | ||
IEnumerable<string> files = _fs.EnumerateFiles(configDir, "*"); | ||
|
||
// Read the contents of each file and concatenate them together | ||
var combinedFile = new StringBuilder(); | ||
foreach (string file in files) | ||
{ | ||
using Stream stream = _fs.OpenFileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read); | ||
using var reader = new StreamReader(stream); | ||
string contents = reader.ReadToEnd(); | ||
combinedFile.Append(contents); | ||
combinedFile.Append('\n'); | ||
} | ||
|
||
var parser = new LinuxConfigParser(_trace); | ||
|
||
return parser.Parse(combinedFile.ToString()); | ||
} | ||
catch (Exception ex) | ||
{ | ||
// Reading defaults is not critical to the operation of the application | ||
// so we can ignore any errors and just log the failure. | ||
_trace.WriteLine("Failed to read default setting from app configuration directory."); | ||
_trace.WriteException(ex); | ||
return null; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters