public IEnumerable <AzureReposBinding> GetBindings(string orgName = null) { var globalUsers = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); var localUsers = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); IGitConfiguration config = _git.GetConfiguration(); string orgPrefix = $"{AzureDevOpsConstants.UrnOrgPrefix}/"; bool ExtractUserBinding(GitConfigurationEntry entry, IDictionary <string, string> dict) { if (GitConfigurationKeyComparer.TrySplit(entry.Key, out _, out string scope, out _) && Uri.TryCreate(scope, UriKind.Absolute, out Uri uri) && uri.Scheme == AzureDevOpsConstants.UrnScheme && uri.AbsolutePath.StartsWith(orgPrefix)) { string entryOrgName = uri.AbsolutePath.Substring(orgPrefix.Length); if (string.IsNullOrWhiteSpace(orgName) || StringComparer.OrdinalIgnoreCase.Equals(entryOrgName, orgName)) { dict[entryOrgName] = entry.Value; } } return(true); } // Only enumerate local configuration if we are inside a repository if (_git.IsInsideRepository()) { config.Enumerate( GitConfigurationLevel.Local, Constants.GitConfiguration.Credential.SectionName, Constants.GitConfiguration.Credential.UserName, entry => ExtractUserBinding(entry, localUsers)); } config.Enumerate( GitConfigurationLevel.Global, Constants.GitConfiguration.Credential.SectionName, Constants.GitConfiguration.Credential.UserName, entry => ExtractUserBinding(entry, globalUsers)); foreach (string org in globalUsers.Keys.Union(localUsers.Keys)) { // NOT using the short-circuiting OR operator here on purpose - we need both branches to be evaluated if (globalUsers.TryGetValue(org, out string globalUser) | localUsers.TryGetValue(org, out string localUser)) { yield return(new AzureReposBinding(org, globalUser, localUser)); } } }
public IEnumerable <AzureReposBinding> GetBindings(string orgName = null) { var globalUsers = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); var localUsers = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); IGitConfiguration config = _git.GetConfiguration(); string orgPrefix = $"{AzureDevOpsConstants.UrnOrgPrefix}/"; config.Enumerate( Constants.GitConfiguration.Credential.SectionName, Constants.GitConfiguration.Credential.UserName, entry => { if (GitConfigurationKeyComparer.TrySplit(entry.Key, out _, out string scope, out _) && Uri.TryCreate(scope, UriKind.Absolute, out Uri uri) && uri.Scheme == AzureDevOpsConstants.UrnScheme && uri.AbsolutePath.StartsWith(orgPrefix)) { string entryOrgName = uri.AbsolutePath.Substring(orgPrefix.Length); if (orgName is null || StringComparer.OrdinalIgnoreCase.Equals(entryOrgName, orgName)) { if (entry.Level == GitConfigurationLevel.Local) { localUsers[entryOrgName] = entry.Value; } else { globalUsers[entryOrgName] = entry.Value; } } } return(true); });
public void GitConfiguration_Enumerate_CallbackReturnsTrue_InvokesCallbackForEachEntry() { string repoPath = CreateRepository(out string workDirPath); ExecGit(repoPath, workDirPath, "config --local foo.name lancelot").AssertSuccess(); ExecGit(repoPath, workDirPath, "config --local foo.quest seek-holy-grail").AssertSuccess(); ExecGit(repoPath, workDirPath, "config --local foo.favcolor blue").AssertSuccess(); var expectedVisitedEntries = new List <(string name, string value)> { ("foo.name", "lancelot"), ("foo.quest", "seek-holy-grail"), ("foo.favcolor", "blue") }; string gitPath = GetGitPath(); var trace = new NullTrace(); var env = new TestEnvironment(); var git = new GitProcess(trace, env, gitPath, repoPath); IGitConfiguration config = git.GetConfiguration(); var actualVisitedEntries = new List <(string name, string value)>(); bool cb(GitConfigurationEntry entry) { if (entry.Key.StartsWith("foo.")) { actualVisitedEntries.Add((entry.Key, entry.Value)); } // Continue enumeration return(true); } config.Enumerate(cb); Assert.Equal(expectedVisitedEntries, actualVisitedEntries); }
public void Clear() { _trace.WriteLine("Clearing all cached authorities..."); IGitConfiguration config = _git.GetConfiguration(); var orgKeys = new HashSet <string>(GitConfigurationKeyComparer.Instance); config.Enumerate( GitConfigurationLevel.Global, Constants.GitConfiguration.Credential.SectionName, AzureDevOpsConstants.GitConfiguration.Credential.AzureAuthority, entry => { if (GitConfigurationKeyComparer.TrySplit(entry.Key, out _, out string scope, out _) && Uri.TryCreate(scope, UriKind.Absolute, out Uri orgUrn) && orgUrn.Scheme == AzureDevOpsConstants.UrnScheme) { orgKeys.Add(entry.Key); } return(true); });
public void GitConfiguration_Enumerate_CallbackReturnsFalse_InvokesCallbackForEachEntryUntilReturnsFalse() { string repoPath = CreateRepository(out string workDirPath); Git(repoPath, workDirPath, "config --local foo.name lancelot").AssertSuccess(); Git(repoPath, workDirPath, "config --local foo.quest seek-holy-grail").AssertSuccess(); Git(repoPath, workDirPath, "config --local foo.favcolor blue").AssertSuccess(); var expectedVisitedEntries = new List <(string name, string value)> { ("foo.name", "lancelot"), ("foo.quest", "seek-holy-grail") }; string gitPath = GetGitPath(); var trace = new NullTrace(); var git = new GitProcess(trace, gitPath, repoPath); IGitConfiguration config = git.GetConfiguration(); var actualVisitedEntries = new List <(string name, string value)>(); bool cb(string name, string value) { if (name.StartsWith("foo.")) { actualVisitedEntries.Add((name, value)); } // Stop enumeration after 2 'foo' entries return(actualVisitedEntries.Count < 2); } config.Enumerate(cb); Assert.Equal(expectedVisitedEntries, actualVisitedEntries); }
/// <summary> /// Try and get the all values of a specified setting as specified in the environment and Git configuration, /// in the correct order or precedence. /// </summary> /// <param name="envarName">Optional environment variable name.</param> /// <param name="section">Optional Git configuration section name.</param> /// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param> /// <returns>All values for the specified setting, in order of precedence, or an empty collection if no such values are set.</returns> public IEnumerable <string> GetSettingValues(string envarName, string section, string property) { string value; if (envarName != null) { if (_environment.Variables.TryGetValue(envarName, out value)) { yield return(value); } } if (section != null && property != null) { IGitConfiguration config = GetGitConfiguration(); if (RemoteUri != null) { /* * Look for URL scoped "section" configuration entries, starting from the most specific * down to the least specific (stopping before the TLD). * * In a divergence from standard Git configuration rules, we also consider matching URL scopes * without a scheme ("protocol://"). * * For each level of scope, we look for an entry with the scheme included (the default), and then * also one without it specified. This allows you to have one configuration scope for both "http" and * "https" without needing to repeat yourself, for example. * * For example, starting with "https://foo.example.com/bar/buzz" we have: * * 1a. [section "https://foo.example.com/bar/buzz"] * property = value * * 1b. [section "foo.example.com/bar/buzz"] * property = value * * 2a. [section "https://foo.example.com/bar"] * property = value * * 2b. [section "foo.example.com/bar"] * property = value * * 3a. [section "https://foo.example.com"] * property = value * * 3b. [section "foo.example.com"] * property = value * * 4a. [section "https://example.com"] * property = value * * 4b. [section "example.com"] * property = value * */ // Enumerate all configuration entries with the correct section and property name // and make a local copy of them here to avoid needing to call `TryGetValue` on the // IGitConfiguration object multiple times in a loop below. var configEntries = new Dictionary <string, string>(); config.Enumerate((entryName, entryValue) => { string entrySection = entryName.TruncateFromIndexOf('.'); string entryProperty = entryName.TrimUntilLastIndexOf('.'); if (StringComparer.OrdinalIgnoreCase.Equals(entrySection, section) && StringComparer.OrdinalIgnoreCase.Equals(entryProperty, property)) { configEntries[entryName] = entryValue; } // Continue the enumeration return(true); }); foreach (string scope in RemoteUri.GetGitConfigurationScopes()) { string queryName = $"{section}.{scope}.{property}"; // Look for a scoped entry that includes the scheme "protocol://example.com" first as this is more specific if (configEntries.TryGetValue(queryName, out value)) { yield return(value); } // Now look for a scoped entry that omits the scheme "example.com" second as this is less specific string scopeWithoutScheme = scope.TrimUntilIndexOf(Uri.SchemeDelimiter); string queryWithSchemeName = $"{section}.{scopeWithoutScheme}.{property}"; if (configEntries.TryGetValue(queryWithSchemeName, out value)) { yield return(value); } } } /* * Try to look for an un-scoped "section" property setting: * * [section] * property = value * */ if (config.TryGetValue($"{section}.{property}", out value)) { yield return(value); } } }
public IEnumerable <string> GetSettingValues(string envarName, string section, string property, bool isPath) { string value; if (envarName != null) { if (_environment.Variables.TryGetValue(envarName, out value)) { yield return(value); } } if (section != null && property != null) { IGitConfiguration config = _git.GetConfiguration(); if (RemoteUri != null) { /* * Look for URL scoped "section" configuration entries, starting from the most specific * down to the least specific (stopping before the TLD). * * In a divergence from standard Git configuration rules, we also consider matching URL scopes * without a scheme ("protocol://"). * * For each level of scope, we look for an entry with the scheme included (the default), and then * also one without it specified. This allows you to have one configuration scope for both "http" and * "https" without needing to repeat yourself, for example. * * For example, starting with "https://foo.example.com/bar/buzz" we have: * * 1a. [section "https://foo.example.com/bar/buzz"] * property = value * * 1b. [section "foo.example.com/bar/buzz"] * property = value * * 2a. [section "https://foo.example.com/bar"] * property = value * * 2b. [section "foo.example.com/bar"] * property = value * * 3a. [section "https://foo.example.com"] * property = value * * 3b. [section "foo.example.com"] * property = value * * 4a. [section "https://example.com"] * property = value * * 4b. [section "example.com"] * property = value * * It is also important to note that although the section and property names are NOT case * sensitive, the "scope" part IS case sensitive! We must be careful when searching to ensure * we follow Git's rules. * */ // Enumerate all configuration entries with the correct section and property name // and make a local copy of them here to avoid needing to call `TryGetValue` on the // IGitConfiguration object multiple times in a loop below. var configEntries = new Dictionary <string, string>(GitConfigurationKeyComparer.Instance); config.Enumerate(section, property, entry => { configEntries[entry.Key] = entry.Value; // Continue the enumeration return(true); }); foreach (string scope in RemoteUri.GetGitConfigurationScopes()) { string queryName = $"{section}.{scope}.{property}"; // Look for a scoped entry that includes the scheme "protocol://example.com" first as // this is more specific. If `isPath` is true, then re-get the value from the // `GitConfiguration` with `isPath` specified. if (configEntries.TryGetValue(queryName, out value) && (!isPath || config.TryGet(queryName, isPath, out value))) { yield return(value); } // Now look for a scoped entry that omits the scheme "example.com" second as this is less // specific. As above, if `isPath` is true, get the configuration setting again with // `isPath` specified. string scopeWithoutScheme = scope.TrimUntilIndexOf(Uri.SchemeDelimiter); string queryWithSchemeName = $"{section}.{scopeWithoutScheme}.{property}"; if (configEntries.TryGetValue(queryWithSchemeName, out value) && (!isPath || config.TryGet(queryWithSchemeName, isPath, out value))) { yield return(value); } } } /* * Try to look for an un-scoped "section" property setting: * * [section] * property = value * */ if (config.TryGet($"{section}.{property}", isPath, out value)) { yield return(value); } // Check for an externally specified default value if (TryGetExternalDefault(section, property, out string defaultValue)) { yield return(defaultValue); } } }