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);
        }
        Task IConfigurableComponent.UnconfigureAsync(
            IEnvironment environment, EnvironmentVariableTarget environmentTarget,
            IGit git, GitConfigurationLevel configurationLevel)
        {
            string helperKey        = $"{Constants.GitConfiguration.Credential.SectionName}.{Constants.GitConfiguration.Credential.Helper}";
            string gitConfigAppName = GetGitConfigAppName();

            IGitConfiguration targetConfig = git.GetConfiguration(configurationLevel);

            Context.Trace.WriteLine("Removing Git credential helper configuration...");

            // Clear any blank 'reset' entries
            targetConfig.UnsetAll(helperKey, Constants.RegexPatterns.Empty);

            // Clear GCM executable entries
            targetConfig.UnsetAll(helperKey, Regex.Escape(gitConfigAppName));

            // NOTE: We currently only update the PATH in Windows installations and leave removing the GCM executable
            //       on the PATH on other platform to their installers.
            // Remove GCM executable from the PATH
            if (PlatformUtils.IsWindows())
            {
                Context.Trace.WriteLine("Removing application from the PATH...");
                string directoryPath = Path.GetDirectoryName(_appPath);
                environment.RemoveDirectoryFromPath(directoryPath, environmentTarget);
            }

            return(Task.CompletedTask);
        }
        Task IConfigurableComponent.ConfigureAsync(
            IEnvironment environment, EnvironmentVariableTarget environmentTarget,
            IGit git, GitConfigurationLevel configurationLevel)
        {
            // NOTE: We currently only update the PATH in Windows installations and leave putting the GCM executable
            //       on the PATH on other platform to their installers.
            if (PlatformUtils.IsWindows())
            {
                string directoryPath = Path.GetDirectoryName(_appPath);
                if (!environment.IsDirectoryOnPath(directoryPath))
                {
                    Context.Trace.WriteLine("Adding application to PATH...");
                    environment.AddDirectoryToPath(directoryPath, environmentTarget);
                }
                else
                {
                    Context.Trace.WriteLine("Application is already on the PATH.");
                }
            }

            string helperKey        = $"{Constants.GitConfiguration.Credential.SectionName}.{Constants.GitConfiguration.Credential.Helper}";
            string gitConfigAppName = GetGitConfigAppName();

            IGitConfiguration targetConfig = git.GetConfiguration(configurationLevel);

            /*
             * We are looking for the following to be considered already set:
             *
             * [credential]
             *     ...                         # any number of helper entries
             *     helper =                    # an empty value to reset/clear any previous entries
             *     helper = {gitConfigAppName} # the expected executable value in the last position & directly following the empty value
             *
             */

            string[] currentValues = targetConfig.GetRegex(helperKey, Constants.RegexPatterns.Any).ToArray();
            if (currentValues.Length < 2 ||
                !string.IsNullOrWhiteSpace(currentValues[currentValues.Length - 2]) ||    // second to last entry is empty
                currentValues[currentValues.Length - 1] != gitConfigAppName)              // last entry is the expected executable
            {
                Context.Trace.WriteLine("Updating Git credential helper configuration...");

                // Clear any existing entries in the configuration.
                targetConfig.UnsetAll(helperKey, Constants.RegexPatterns.Any);

                // 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.
                targetConfig.SetValue(helperKey, string.Empty);
                targetConfig.ReplaceAll(helperKey, Constants.RegexPatterns.None, gitConfigAppName);
            }
            else
            {
                Context.Trace.WriteLine("Credential helper configuration is already set correctly.");
            }


            return(Task.CompletedTask);
        }
예제 #4
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 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 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);
        }
예제 #7
0
 private IGitConfiguration GetGitConfiguration() => _gitConfig ?? (_gitConfig = _git.GetConfiguration(RepositoryPath));
예제 #8
0
 /// <summary>
 /// Get a snapshot of the configuration for the system and user.
 /// </summary>
 /// <param name="git">Git object.</param>
 /// <returns>Git configuration snapshot.</returns>
 public static IGitConfiguration GetConfiguration(this IGit git) => git.GetConfiguration(null);
        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 = _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
                     *
                     */

                    // 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);
                }
            }
        }
예제 #10
0
        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);
                }
            }
        }
예제 #11
0
 /// <summary>
 /// Get the configuration.
 /// </summary>
 /// <param name="git">Git object.</param>
 /// <returns>Git configuration.</returns>
 public static IGitConfiguration GetConfiguration(this IGit git) => git.GetConfiguration(GitConfigurationLevel.All);
예제 #12
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.TryGetValue(envarName, out value))
                {
                    yield return(value);
                }
            }

            if (section != null && property != null)
            {
                using (var config = _git.GetConfiguration(RepositoryPath))
                {
                    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
                         *
                         */
                        foreach (string scope in RemoteUri.GetGitConfigurationScopes())
                        {
                            // Look for a scoped entry that includes the scheme "protocol://example.com" first as this is more specific
                            if (config.TryGetValue(section, scope, property, 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);
                            if (config.TryGetValue(section, scopeWithoutScheme, property, 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);
                    }
                }
            }
        }