public void GitConfiguration_GetString_Name_DoesNotExists_ThrowsException()
        {
            string repoPath = CreateRepository();

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               git     = new GitProcess(trace, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration();

            string randomName = $"{Guid.NewGuid():N}.{Guid.NewGuid():N}";

            Assert.Throws <KeyNotFoundException>(() => config.Get(randomName));
        }
        public Task UnconfigureAsync(
            IEnvironment environment, EnvironmentVariableTarget environmentTarget,
            IGit git, GitConfigurationLevel configurationLevel)
        {
            string useHttpPathKey = $"{KnownGitCfg.Credential.SectionName}.https://dev.azure.com.{KnownGitCfg.Credential.UseHttpPath}";

            Context.Trace.WriteLine("Clearing Git configuration 'credential.useHttpPath' for https://dev.azure.com...");

            IGitConfiguration targetConfig = git.GetConfiguration(configurationLevel);

            targetConfig.Unset(useHttpPathKey);

            return(Task.CompletedTask);
        }
        public void GitConfiguration_GetString_SectionProperty_DoesNotExists_ThrowsException()
        {
            string repoPath = CreateRepository();

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               git     = new GitProcess(trace, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration();

            string randomSection  = Guid.NewGuid().ToString("N");
            string randomProperty = Guid.NewGuid().ToString("N");

            Assert.Throws <KeyNotFoundException>(() => config.Get(randomSection, randomProperty));
        }
        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 string GetAuthority(string orgName)
        {
            EnsureArgument.NotNullOrWhiteSpace(orgName, nameof(orgName));

            _trace.WriteLine($"Looking up cached authority for organization '{orgName}'...");

            IGitConfiguration config = _git.GetConfiguration();

            if (config.TryGet(GitConfigurationLevel.Global, GetAuthorityKey(orgName), out string authority))
            {
                return(authority);
            }

            return(null);
        }
        public void GitConfiguration_Set_Local_SetsLocalConfig()
        {
            string repoPath = CreateRepository(out string workDirPath);

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               git     = new GitProcess(trace, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration(GitConfigurationLevel.Local);

            config.Set("core.foobar", "foo123");

            GitResult localResult = Git(repoPath, workDirPath, "config --local core.foobar");

            Assert.Equal("foo123", localResult.StandardOutput.Trim());
        }
Example #7
0
        public Task UnconfigureAsync(
            IEnvironment environment, EnvironmentVariableTarget environmentTarget,
            IGitConfiguration configuration, GitConfigurationLevel configurationLevel)
        {
            string useHttpPathKey = $"{KnownGitCfg.Credential.SectionName}.https://dev.azure.com.{KnownGitCfg.Credential.UseHttpPath}";

            Context.Trace.WriteLine("Clearing Git configuration 'credential.useHttpPath' for https://dev.azure.com...");

            using (IGitConfiguration targetConfig = configuration.GetFilteredConfiguration(configurationLevel))
            {
                targetConfig.DeleteEntry(useHttpPathKey);
            }

            return(Task.CompletedTask);
        }
        public void GitConfiguration_TryGet_Name_DoesNotExists_ReturnsFalse()
        {
            string repoPath = CreateRepository();

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               git     = new GitProcess(trace, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration();

            string randomName = $"{Guid.NewGuid():N}.{Guid.NewGuid():N}";
            bool   result     = config.TryGet(randomName, out string value);

            Assert.False(result);
            Assert.Null(value);
        }
        public Task UnconfigureAsync(ConfigurationTarget target)
        {
            string useHttpPathKey = $"{KnownGitCfg.Credential.SectionName}.https://dev.azure.com.{KnownGitCfg.Credential.UseHttpPath}";

            _context.Trace.WriteLine("Clearing Git configuration 'credential.useHttpPath' for https://dev.azure.com...");

            GitConfigurationLevel configurationLevel = target == ConfigurationTarget.System
                ? GitConfigurationLevel.System
                : GitConfigurationLevel.Global;

            IGitConfiguration targetConfig = _context.Git.GetConfiguration(configurationLevel);

            targetConfig.Unset(useHttpPathKey);

            return(Task.CompletedTask);
        }
        public void GitConfiguration_Get_Name_Exists_ReturnsString()
        {
            string repoPath = CreateRepository(out string workDirPath);

            ExecGit(repoPath, workDirPath, "config --local user.name john.doe").AssertSuccess();

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               git     = new GitProcess(trace, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration();

            string value = config.Get("user.name");

            Assert.NotNull(value);
            Assert.Equal("john.doe", value);
        }
        public void GitConfiguration_GetString_SectionScopeProperty_NullScope_ReturnsUnscopedString()
        {
            string repoPath = CreateRepository(out string workDirPath);

            Git(repoPath, workDirPath, "config --local user.name john.doe").AssertSuccess();

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               git     = new GitProcess(trace, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration();

            string value = config.Get("user", null, "name");

            Assert.NotNull(value);
            Assert.Equal("john.doe", value);
        }
        public void GitConfiguration_TryGet_SectionProperty_DoesNotExists_ReturnsFalse()
        {
            string repoPath = CreateRepository();

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               git     = new GitProcess(trace, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration();

            string randomSection  = Guid.NewGuid().ToString("N");
            string randomProperty = Guid.NewGuid().ToString("N");
            bool   result         = config.TryGet(randomSection, randomProperty, out string value);

            Assert.False(result);
            Assert.Null(value);
        }
        Task IConfigurableComponent.ConfigureAsync(ConfigurationTarget target)
        {
            string helperKey = $"{Constants.GitConfiguration.Credential.SectionName}.{Constants.GitConfiguration.Credential.Helper}";
            string appPath   = GetGitConfigAppPath();

            GitConfigurationLevel configLevel = target == ConfigurationTarget.System
                    ? GitConfigurationLevel.System
                    : GitConfigurationLevel.Global;

            Context.Trace.WriteLine($"Configuring for config level '{configLevel}'.");

            IGitConfiguration config = Context.Git.GetConfiguration(configLevel);

            // We are looking for the following to be set in the config:
            //
            // [credential]
            //     ...                # any number of helper entries (possibly none)
            //     helper =           # an empty value to reset/clear any previous entries (if applicable)
            //     helper = {appPath} # the expected executable value & directly following the empty value
            //     ...                # any number of helper entries (possibly none, but not the empty value '')
            //
            string[] currentValues = config.GetAll(helperKey).ToArray();

            // Try to locate an existing app entry with a blank reset/clear entry immediately preceding,
            // and no other blank empty/clear entries following (which effectively disable us).
            int appIndex       = Array.FindIndex(currentValues, x => Context.FileSystem.IsSamePath(x, appPath));
            int lastEmptyIndex = Array.FindLastIndex(currentValues, string.IsNullOrWhiteSpace);

            if (appIndex > 0 && string.IsNullOrWhiteSpace(currentValues[appIndex - 1]) && lastEmptyIndex < appIndex)
            {
                Context.Trace.WriteLine("Credential helper configuration is already set correctly.");
            }
            else
            {
                Context.Trace.WriteLine("Updating Git credential helper configuration...");

                // Clear any existing app entries in the configuration
                config.UnsetAll(helperKey, Regex.Escape(appPath));

                // Add an empty value for `credential.helper`, which has the effect of clearing any helper value
                // from any lower-level Git configuration, then add a second value which is the actual executable path.
                config.Add(helperKey, string.Empty);
                config.Add(helperKey, appPath);
            }

            return(Task.CompletedTask);
        }
        public void GitConfiguration_TryGet_SectionScopeProperty_Exists_ReturnsTrueOutString()
        {
            string repoPath = CreateRepository(out string workDirPath);

            Git(repoPath, workDirPath, "config --local user.example.com.name john.doe").AssertSuccess();

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               git     = new GitProcess(trace, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration();

            bool result = config.TryGet("user", "example.com", "name", out string value);

            Assert.True(result);
            Assert.NotNull(value);
            Assert.Equal("john.doe", value);
        }
        Task IConfigurableComponent.UnconfigureAsync(ConfigurationTarget target)
        {
            string helperKey = $"{Constants.GitConfiguration.Credential.SectionName}.{Constants.GitConfiguration.Credential.Helper}";
            string appPath   = GetGitConfigAppPath();

            GitConfigurationLevel configLevel = target == ConfigurationTarget.System
                    ? GitConfigurationLevel.System
                    : GitConfigurationLevel.Global;

            Context.Trace.WriteLine($"Unconfiguring for config level '{configLevel}'.");

            IGitConfiguration config = Context.Git.GetConfiguration(configLevel);

            // We are looking for the following to be set in the config:
            //
            // [credential]
            //     ...                 # any number of helper entries (possibly none)
            //     helper =            # an empty value to reset/clear any previous entries (if applicable)
            //     helper = {appPath} # the expected executable value & directly following the empty value
            //     ...                 # any number of helper entries (possibly none)
            //
            // We should remove the {appPath} entry, and any blank entries immediately preceding IFF there are no more entries following.
            //
            Context.Trace.WriteLine("Removing Git credential helper configuration...");

            string[] currentValues = config.GetAll(helperKey).ToArray();

            int appIndex = Array.FindIndex(currentValues, x => Context.FileSystem.IsSamePath(x, appPath));

            if (appIndex > -1)
            {
                // Check for the presence of a blank entry immediately preceding an app entry in the last position
                if (appIndex > 0 && appIndex == currentValues.Length - 1 &&
                    string.IsNullOrWhiteSpace(currentValues[appIndex - 1]))
                {
                    // Clear the blank entry
                    config.UnsetAll(helperKey, Constants.RegexPatterns.Empty);
                }

                // Clear app entry
                config.UnsetAll(helperKey, Regex.Escape(appPath));
            }

            return(Task.CompletedTask);
        }
Example #16
0
        public void GitConfiguration_TryGet_IsPath_False_ReturnsRawConfig()
        {
            string repoPath = CreateRepository(out string workDirPath);

            ExecGit(repoPath, workDirPath, "config --local example.path ~/test").AssertSuccess();

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               env     = new TestEnvironment();
            var               git     = new GitProcess(trace, env, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration();

            bool result = config.TryGet("example.path", false, out string value);

            Assert.True(result);
            Assert.NotNull(value);
            Assert.Equal($"~/test", value);
        }
Example #17
0
        public void GitConfiguration_TryGet_Name_Exists_ReturnsTrueOutString()
        {
            string repoPath = CreateRepository(out string workDirPath);

            ExecGit(repoPath, workDirPath, "config --local user.name john.doe").AssertSuccess();

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               env     = new TestEnvironment();
            var               git     = new GitProcess(trace, env, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration();

            bool result = config.TryGet("user.name", false, out string value);

            Assert.True(result);
            Assert.NotNull(value);
            Assert.Equal("john.doe", value);
        }
Example #18
0
        public void GitConfiguration_TryGet_BoolWithoutType_ReturnsRawConfig()
        {
            string repoPath = CreateRepository(out string workDirPath);

            ExecGit(repoPath, workDirPath, "config --local example.bool fAlSe").AssertSuccess();

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               env     = new TestEnvironment();
            var               git     = new GitProcess(trace, env, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration();

            bool result = config.TryGet(GitConfigurationLevel.Local, GitConfigurationType.Raw,
                                        "example.bool", out string value);

            Assert.True(result);
            Assert.NotNull(value);
            Assert.Equal("fAlSe", value);
        }
        public void GitConfiguration_UnsetAll_UnsetsAllConfig()
        {
            string repoPath = CreateRepository(out string workDirPath);

            Git(repoPath, workDirPath, "config --local --add core.foobar foo1").AssertSuccess();
            Git(repoPath, workDirPath, "config --local --add core.foobar foo2").AssertSuccess();
            Git(repoPath, workDirPath, "config --local --add core.foobar bar1").AssertSuccess();

            string            gitPath = GetGitPath();
            var               trace   = new NullTrace();
            var               git     = new GitProcess(trace, gitPath, repoPath);
            IGitConfiguration config  = git.GetConfiguration(GitConfigurationLevel.Local);

            config.UnsetAll("core.foobar", "foo*");

            GitResult result = Git(repoPath, workDirPath, "config --local --get-all core.foobar");

            Assert.Equal("bar1", result.StandardOutput.Trim());
        }
Example #20
0
        public void GitConfiguration_TryGet_IsPath_True_ReturnsCanonicalPath()
        {
            string repoPath = CreateRepository(out string workDirPath);

            ExecGit(repoPath, workDirPath, "config --local example.path ~/test").AssertSuccess();

            string            homeDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
            string            gitPath       = GetGitPath();
            var               trace         = new NullTrace();
            var               env           = new TestEnvironment();
            var               git           = new GitProcess(trace, env, gitPath, repoPath);
            IGitConfiguration config        = git.GetConfiguration();

            bool result = config.TryGet("example.path", true, out string value);

            Assert.True(result);
            Assert.NotNull(value);
            Assert.Equal(Path.GetFullPath($"{homeDirectory}/test").Replace("\\", "/"), value);
        }
Example #21
0
        public AzureReposBinding GetBinding(string orgName)
        {
            EnsureArgument.NotNullOrWhiteSpace(orgName, nameof(orgName));

            string orgKey = GetOrgUserKey(orgName);

            IGitConfiguration config = _git.GetConfiguration();

            _trace.WriteLine($"Looking up organization binding for '{orgName}'...");

            // NOT using the short-circuiting OR operator here on purpose - we need both branches to be evaluated
            if (config.TryGet(GitConfigurationLevel.Local, orgKey, out string localUser) |
                config.TryGet(GitConfigurationLevel.Global, orgKey, out string globalUser))
            {
                return(new AzureReposBinding(orgName, globalUser, localUser));
            }

            // No bound user
            return(null);
        }
        public Task ConfigureAsync(
            IEnvironment environment, EnvironmentVariableTarget environmentTarget,
            IGit git, GitConfigurationLevel configurationLevel)
        {
            string useHttpPathKey = $"{KnownGitCfg.Credential.SectionName}.https://dev.azure.com.{KnownGitCfg.Credential.UseHttpPath}";

            IGitConfiguration targetConfig = git.GetConfiguration(configurationLevel);

            if (targetConfig.TryGetValue(useHttpPathKey, out string currentValue) && currentValue.IsTruthy())
            {
                Context.Trace.WriteLine("Git configuration 'credential.useHttpPath' is already set to 'true' for https://dev.azure.com.");
            }
            else
            {
                Context.Trace.WriteLine("Setting Git configuration 'credential.useHttpPath' to 'true' for https://dev.azure.com...");
                targetConfig.SetValue(useHttpPathKey, "true");
            }

            return(Task.CompletedTask);
        }
Example #23
0
        private async Task RunAsync(ConfigurationTarget target, bool configure)
        {
            GitConfigurationLevel     configLevel;
            EnvironmentVariableTarget envTarget;

            switch (target)
            {
            case ConfigurationTarget.User:
                configLevel = GitConfigurationLevel.Global;
                envTarget   = EnvironmentVariableTarget.User;
                break;

            case ConfigurationTarget.System:
                configLevel = GitConfigurationLevel.System;
                envTarget   = EnvironmentVariableTarget.Machine;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(target));
            }

            using (IGitConfiguration config = _context.Git.GetConfiguration())
            {
                foreach (IConfigurableComponent component in _components)
                {
                    if (configure)
                    {
                        _context.Trace.WriteLine($"Configuring component '{component.Name}'...");
                        _context.Streams.Error.WriteLine($"Configuring component '{component.Name}'...");
                        await component.ConfigureAsync(_context.Environment, envTarget, config, configLevel);
                    }
                    else
                    {
                        _context.Trace.WriteLine($"Unconfiguring component '{component.Name}'...");
                        _context.Streams.Error.WriteLine($"Unconfiguring component '{component.Name}'...");
                        await component.UnconfigureAsync(_context.Environment, envTarget, config, configLevel);
                    }
                }
            }
        }
        public Task ConfigureAsync(ConfigurationTarget target)
        {
            string useHttpPathKey = $"{KnownGitCfg.Credential.SectionName}.https://dev.azure.com.{KnownGitCfg.Credential.UseHttpPath}";

            GitConfigurationLevel configurationLevel = target == ConfigurationTarget.System
                ? GitConfigurationLevel.System
                : GitConfigurationLevel.Global;

            IGitConfiguration targetConfig = _context.Git.GetConfiguration();

            if (targetConfig.TryGet(useHttpPathKey, false, out string currentValue) && currentValue.IsTruthy())
            {
                _context.Trace.WriteLine("Git configuration 'credential.useHttpPath' is already set to 'true' for https://dev.azure.com.");
            }
            else
            {
                _context.Trace.WriteLine("Setting Git configuration 'credential.useHttpPath' to 'true' for https://dev.azure.com...");
                targetConfig.Set(configurationLevel, useHttpPathKey, "true");
            }

            return(Task.CompletedTask);
        }
Example #25
0
        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 Task UnconfigureAsync(ConfigurationTarget target)
        {
            string helperKey      = $"{Constants.GitConfiguration.Credential.SectionName}.{Constants.GitConfiguration.Credential.Helper}";
            string useHttpPathKey = $"{KnownGitCfg.Credential.SectionName}.https://dev.azure.com.{KnownGitCfg.Credential.UseHttpPath}";

            _context.Trace.WriteLine("Clearing Git configuration 'credential.useHttpPath' for https://dev.azure.com...");

            GitConfigurationLevel configurationLevel = target == ConfigurationTarget.System
                ? GitConfigurationLevel.System
                : GitConfigurationLevel.Global;

            IGitConfiguration targetConfig = _context.Git.GetConfiguration();

            // On Windows, if there is a "manager-core" entry remaining in the system config then we must not clear
            // the useHttpPath option otherwise this would break the bundled version of GCM in Git for Windows.
            if (!PlatformUtils.IsWindows() || target != ConfigurationTarget.System ||
                targetConfig.GetAll(helperKey).All(x => !string.Equals(x, "manager-core")))
            {
                targetConfig.Unset(configurationLevel, useHttpPathKey);
            }

            return(Task.CompletedTask);
        }
        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);
        }
Example #29
0
 private IGitConfiguration GetGitConfiguration() => _gitConfig ?? (_gitConfig = _git.GetConfiguration(RepositoryPath));
Example #30
0
        /// <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);
                }
            }
        }