mirror of
				https://github.com/actions/checkout.git
				synced 2025-11-04 05:19:14 +08:00 
			
		
		
		
	Persist creds to a separate file (#2286)
This commit is contained in:
		
							
								
								
									
										338
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										338
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							@@ -162,6 +162,7 @@ class GitAuthHelper {
 | 
			
		||||
        this.sshKeyPath = '';
 | 
			
		||||
        this.sshKnownHostsPath = '';
 | 
			
		||||
        this.temporaryHomePath = '';
 | 
			
		||||
        this.credentialsConfigPath = ''; // Path to separate credentials config file in RUNNER_TEMP
 | 
			
		||||
        this.git = gitCommandManager;
 | 
			
		||||
        this.settings = gitSourceSettings || {};
 | 
			
		||||
        // Token auth header
 | 
			
		||||
@@ -229,15 +230,17 @@ class GitAuthHelper {
 | 
			
		||||
    configureGlobalAuth() {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            // 'configureTempGlobalConfig' noops if already set, just returns the path
 | 
			
		||||
            const newGitConfigPath = yield this.configureTempGlobalConfig();
 | 
			
		||||
            yield this.configureTempGlobalConfig();
 | 
			
		||||
            try {
 | 
			
		||||
                // Configure the token
 | 
			
		||||
                yield this.configureToken(newGitConfigPath, true);
 | 
			
		||||
                yield this.configureToken(true);
 | 
			
		||||
                // Configure HTTPS instead of SSH
 | 
			
		||||
                yield this.git.tryConfigUnset(this.insteadOfKey, true);
 | 
			
		||||
                if (!this.settings.sshKey) {
 | 
			
		||||
                    for (const insteadOfValue of this.insteadOfValues) {
 | 
			
		||||
                        yield this.git.config(this.insteadOfKey, insteadOfValue, true, true);
 | 
			
		||||
                        yield this.git.config(this.insteadOfKey, insteadOfValue, true, // globalConfig?
 | 
			
		||||
                        true // add?
 | 
			
		||||
                        );
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@@ -252,19 +255,34 @@ class GitAuthHelper {
 | 
			
		||||
    configureSubmoduleAuth() {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            // Remove possible previous HTTPS instead of SSH
 | 
			
		||||
            yield this.removeGitConfig(this.insteadOfKey, true);
 | 
			
		||||
            yield this.removeSubmoduleGitConfig(this.insteadOfKey);
 | 
			
		||||
            if (this.settings.persistCredentials) {
 | 
			
		||||
                // Configure a placeholder value. This approach avoids the credential being captured
 | 
			
		||||
                // by process creation audit events, which are commonly logged. For more information,
 | 
			
		||||
                // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | 
			
		||||
                const output = yield this.git.submoduleForeach(
 | 
			
		||||
                // wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
 | 
			
		||||
                `sh -c "git config --local '${this.tokenConfigKey}' '${this.tokenPlaceholderConfigValue}' && git config --local --show-origin --name-only --get-regexp remote.origin.url"`, this.settings.nestedSubmodules);
 | 
			
		||||
                // Replace the placeholder
 | 
			
		||||
                const configPaths = output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [];
 | 
			
		||||
                // Get the credentials config file path in RUNNER_TEMP
 | 
			
		||||
                const credentialsConfigPath = this.getCredentialsConfigPath();
 | 
			
		||||
                // Container credentials config path
 | 
			
		||||
                const containerCredentialsPath = path.posix.join('/github/runner_temp', path.basename(credentialsConfigPath));
 | 
			
		||||
                // Get submodule config file paths.
 | 
			
		||||
                const configPaths = yield this.git.getSubmoduleConfigPaths(this.settings.nestedSubmodules);
 | 
			
		||||
                // For each submodule, configure includeIf entries pointing to the shared credentials file.
 | 
			
		||||
                // Configure both host and container paths to support Docker container actions.
 | 
			
		||||
                for (const configPath of configPaths) {
 | 
			
		||||
                    core.debug(`Replacing token placeholder in '${configPath}'`);
 | 
			
		||||
                    yield this.replaceTokenPlaceholder(configPath);
 | 
			
		||||
                    // Submodule Git directory
 | 
			
		||||
                    let submoduleGitDir = path.dirname(configPath); // The config file is at .git/modules/submodule-name/config
 | 
			
		||||
                    submoduleGitDir = submoduleGitDir.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | 
			
		||||
                    // Configure host includeIf
 | 
			
		||||
                    yield this.git.config(`includeIf.gitdir:${submoduleGitDir}.path`, credentialsConfigPath, false, // globalConfig?
 | 
			
		||||
                    false, // add?
 | 
			
		||||
                    configPath);
 | 
			
		||||
                    // Container submodule git directory
 | 
			
		||||
                    const githubWorkspace = process.env['GITHUB_WORKSPACE'];
 | 
			
		||||
                    assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined');
 | 
			
		||||
                    let relativeSubmoduleGitDir = path.relative(githubWorkspace, submoduleGitDir);
 | 
			
		||||
                    relativeSubmoduleGitDir = relativeSubmoduleGitDir.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | 
			
		||||
                    const containerSubmoduleGitDir = path.posix.join('/github/workspace', relativeSubmoduleGitDir);
 | 
			
		||||
                    // Configure container includeIf
 | 
			
		||||
                    yield this.git.config(`includeIf.gitdir:${containerSubmoduleGitDir}.path`, containerCredentialsPath, false, // globalConfig?
 | 
			
		||||
                    false, // add?
 | 
			
		||||
                    configPath);
 | 
			
		||||
                }
 | 
			
		||||
                if (this.settings.sshKey) {
 | 
			
		||||
                    // Configure core.sshCommand
 | 
			
		||||
@@ -295,6 +313,10 @@ class GitAuthHelper {
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Configures SSH authentication by writing the SSH key and known hosts,
 | 
			
		||||
     * and setting up the GIT_SSH_COMMAND environment variable.
 | 
			
		||||
     */
 | 
			
		||||
    configureSsh() {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            if (!this.settings.sshKey) {
 | 
			
		||||
@@ -351,43 +373,88 @@ class GitAuthHelper {
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    configureToken(configPath, globalConfig) {
 | 
			
		||||
    /**
 | 
			
		||||
     * Configures token-based authentication by creating a credentials config file
 | 
			
		||||
     * and setting up includeIf entries to reference it.
 | 
			
		||||
     * @param globalConfig Whether to configure global config instead of local
 | 
			
		||||
     */
 | 
			
		||||
    configureToken(globalConfig) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            // Validate args
 | 
			
		||||
            assert.ok((configPath && globalConfig) || (!configPath && !globalConfig), 'Unexpected configureToken parameter combinations');
 | 
			
		||||
            // Default config path
 | 
			
		||||
            if (!configPath && !globalConfig) {
 | 
			
		||||
                configPath = path.join(this.git.getWorkingDirectory(), '.git', 'config');
 | 
			
		||||
            }
 | 
			
		||||
            // Configure a placeholder value. This approach avoids the credential being captured
 | 
			
		||||
            // by process creation audit events, which are commonly logged. For more information,
 | 
			
		||||
            // refer to https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | 
			
		||||
            yield this.git.config(this.tokenConfigKey, this.tokenPlaceholderConfigValue, globalConfig);
 | 
			
		||||
            // Replace the placeholder
 | 
			
		||||
            yield this.replaceTokenPlaceholder(configPath || '');
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    replaceTokenPlaceholder(configPath) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            assert.ok(configPath, 'configPath is not defined');
 | 
			
		||||
            let content = (yield fs.promises.readFile(configPath)).toString();
 | 
			
		||||
            // Get the credentials config file path in RUNNER_TEMP
 | 
			
		||||
            const credentialsConfigPath = this.getCredentialsConfigPath();
 | 
			
		||||
            // Write placeholder to the separate credentials config file using git config.
 | 
			
		||||
            // This approach avoids the credential being captured by process creation audit events,
 | 
			
		||||
            // which are commonly logged. For more information, refer to
 | 
			
		||||
            // https://docs.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/command-line-process-auditing
 | 
			
		||||
            yield this.git.config(this.tokenConfigKey, this.tokenPlaceholderConfigValue, false, // globalConfig?
 | 
			
		||||
            false, // add?
 | 
			
		||||
            credentialsConfigPath);
 | 
			
		||||
            // Replace the placeholder in the credentials config file
 | 
			
		||||
            let content = (yield fs.promises.readFile(credentialsConfigPath)).toString();
 | 
			
		||||
            const placeholderIndex = content.indexOf(this.tokenPlaceholderConfigValue);
 | 
			
		||||
            if (placeholderIndex < 0 ||
 | 
			
		||||
                placeholderIndex != content.lastIndexOf(this.tokenPlaceholderConfigValue)) {
 | 
			
		||||
                throw new Error(`Unable to replace auth placeholder in ${configPath}`);
 | 
			
		||||
                throw new Error(`Unable to replace auth placeholder in ${credentialsConfigPath}`);
 | 
			
		||||
            }
 | 
			
		||||
            assert.ok(this.tokenConfigValue, 'tokenConfigValue is not defined');
 | 
			
		||||
            content = content.replace(this.tokenPlaceholderConfigValue, this.tokenConfigValue);
 | 
			
		||||
            yield fs.promises.writeFile(configPath, content);
 | 
			
		||||
            yield fs.promises.writeFile(credentialsConfigPath, content);
 | 
			
		||||
            // Add include or includeIf to reference the credentials config
 | 
			
		||||
            if (globalConfig) {
 | 
			
		||||
                // Global config file is temporary
 | 
			
		||||
                yield this.git.config('include.path', credentialsConfigPath, true // globalConfig?
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                // Host git directory
 | 
			
		||||
                let gitDir = path.join(this.git.getWorkingDirectory(), '.git');
 | 
			
		||||
                gitDir = gitDir.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | 
			
		||||
                // Configure host includeIf
 | 
			
		||||
                const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`;
 | 
			
		||||
                yield this.git.config(hostIncludeKey, credentialsConfigPath);
 | 
			
		||||
                // Container git directory
 | 
			
		||||
                const workingDirectory = this.git.getWorkingDirectory();
 | 
			
		||||
                const githubWorkspace = process.env['GITHUB_WORKSPACE'];
 | 
			
		||||
                assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined');
 | 
			
		||||
                let relativePath = path.relative(githubWorkspace, workingDirectory);
 | 
			
		||||
                relativePath = relativePath.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | 
			
		||||
                const containerGitDir = path.posix.join('/github/workspace', relativePath, '.git');
 | 
			
		||||
                // Container credentials config path
 | 
			
		||||
                const containerCredentialsPath = path.posix.join('/github/runner_temp', path.basename(credentialsConfigPath));
 | 
			
		||||
                // Configure container includeIf
 | 
			
		||||
                const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path`;
 | 
			
		||||
                yield this.git.config(containerIncludeKey, containerCredentialsPath);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets or creates the path to the credentials config file in RUNNER_TEMP.
 | 
			
		||||
     * @returns The absolute path to the credentials config file
 | 
			
		||||
     */
 | 
			
		||||
    getCredentialsConfigPath() {
 | 
			
		||||
        if (this.credentialsConfigPath) {
 | 
			
		||||
            return this.credentialsConfigPath;
 | 
			
		||||
        }
 | 
			
		||||
        const runnerTemp = process.env['RUNNER_TEMP'] || '';
 | 
			
		||||
        assert.ok(runnerTemp, 'RUNNER_TEMP is not defined');
 | 
			
		||||
        // Create a unique filename for this checkout instance
 | 
			
		||||
        const configFileName = `git-credentials-${(0, uuid_1.v4)()}.config`;
 | 
			
		||||
        this.credentialsConfigPath = path.join(runnerTemp, configFileName);
 | 
			
		||||
        core.debug(`Credentials config path: ${this.credentialsConfigPath}`);
 | 
			
		||||
        return this.credentialsConfigPath;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes SSH authentication configuration by cleaning up SSH keys,
 | 
			
		||||
     * known hosts files, and SSH command configurations.
 | 
			
		||||
     */
 | 
			
		||||
    removeSsh() {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            var _a;
 | 
			
		||||
            var _a, _b;
 | 
			
		||||
            // SSH key
 | 
			
		||||
            const keyPath = this.sshKeyPath || stateHelper.SshKeyPath;
 | 
			
		||||
            if (keyPath) {
 | 
			
		||||
                try {
 | 
			
		||||
                    core.info(`Removing SSH key '${keyPath}'`);
 | 
			
		||||
                    yield io.rmRF(keyPath);
 | 
			
		||||
                }
 | 
			
		||||
                catch (err) {
 | 
			
		||||
@@ -399,37 +466,136 @@ class GitAuthHelper {
 | 
			
		||||
            const knownHostsPath = this.sshKnownHostsPath || stateHelper.SshKnownHostsPath;
 | 
			
		||||
            if (knownHostsPath) {
 | 
			
		||||
                try {
 | 
			
		||||
                    core.info(`Removing SSH known hosts '${knownHostsPath}'`);
 | 
			
		||||
                    yield io.rmRF(knownHostsPath);
 | 
			
		||||
                }
 | 
			
		||||
                catch (_b) {
 | 
			
		||||
                    // Intentionally empty
 | 
			
		||||
                catch (err) {
 | 
			
		||||
                    core.debug(`${(_b = err === null || err === void 0 ? void 0 : err.message) !== null && _b !== void 0 ? _b : err}`);
 | 
			
		||||
                    core.warning(`Failed to remove SSH known hosts '${knownHostsPath}'`);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // SSH command
 | 
			
		||||
            core.info('Removing SSH command configuration');
 | 
			
		||||
            yield this.removeGitConfig(SSH_COMMAND_KEY);
 | 
			
		||||
            yield this.removeSubmoduleGitConfig(SSH_COMMAND_KEY);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes token-based authentication by cleaning up HTTP headers,
 | 
			
		||||
     * includeIf entries, and credentials config files.
 | 
			
		||||
     */
 | 
			
		||||
    removeToken() {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            // HTTP extra header
 | 
			
		||||
            var _a;
 | 
			
		||||
            // Remove HTTP extra header
 | 
			
		||||
            core.info('Removing HTTP extra header');
 | 
			
		||||
            yield this.removeGitConfig(this.tokenConfigKey);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    removeGitConfig(configKey_1) {
 | 
			
		||||
        return __awaiter(this, arguments, void 0, function* (configKey, submoduleOnly = false) {
 | 
			
		||||
            if (!submoduleOnly) {
 | 
			
		||||
                if ((yield this.git.configExists(configKey)) &&
 | 
			
		||||
                    !(yield this.git.tryConfigUnset(configKey))) {
 | 
			
		||||
                    // Load the config contents
 | 
			
		||||
                    core.warning(`Failed to remove '${configKey}' from the git config`);
 | 
			
		||||
            yield this.removeSubmoduleGitConfig(this.tokenConfigKey);
 | 
			
		||||
            // Collect credentials config paths that need to be removed
 | 
			
		||||
            const credentialsPaths = new Set();
 | 
			
		||||
            // Remove includeIf entries that point to git-credentials-*.config files
 | 
			
		||||
            core.info('Removing includeIf entries pointing to credentials config files');
 | 
			
		||||
            const mainCredentialsPaths = yield this.removeIncludeIfCredentials();
 | 
			
		||||
            mainCredentialsPaths.forEach(path => credentialsPaths.add(path));
 | 
			
		||||
            // Remove submodule includeIf entries that point to git-credentials-*.config files
 | 
			
		||||
            const submoduleConfigPaths = yield this.git.getSubmoduleConfigPaths(true);
 | 
			
		||||
            for (const configPath of submoduleConfigPaths) {
 | 
			
		||||
                const submoduleCredentialsPaths = yield this.removeIncludeIfCredentials(configPath);
 | 
			
		||||
                submoduleCredentialsPaths.forEach(path => credentialsPaths.add(path));
 | 
			
		||||
            }
 | 
			
		||||
            // Remove credentials config files
 | 
			
		||||
            for (const credentialsPath of credentialsPaths) {
 | 
			
		||||
                // Only remove credentials config files if they are under RUNNER_TEMP
 | 
			
		||||
                const runnerTemp = process.env['RUNNER_TEMP'];
 | 
			
		||||
                assert.ok(runnerTemp, 'RUNNER_TEMP is not defined');
 | 
			
		||||
                if (credentialsPath.startsWith(runnerTemp)) {
 | 
			
		||||
                    try {
 | 
			
		||||
                        core.info(`Removing credentials config '${credentialsPath}'`);
 | 
			
		||||
                        yield io.rmRF(credentialsPath);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch (err) {
 | 
			
		||||
                        core.debug(`${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
 | 
			
		||||
                        core.warning(`Failed to remove credentials config '${credentialsPath}'`);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    core.debug(`Skipping removal of credentials config '${credentialsPath}' - not under RUNNER_TEMP`);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes a git config key from the local repository config.
 | 
			
		||||
     * @param configKey The git config key to remove
 | 
			
		||||
     */
 | 
			
		||||
    removeGitConfig(configKey) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            if ((yield this.git.configExists(configKey)) &&
 | 
			
		||||
                !(yield this.git.tryConfigUnset(configKey))) {
 | 
			
		||||
                // Load the config contents
 | 
			
		||||
                core.warning(`Failed to remove '${configKey}' from the git config`);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes a git config key from all submodule configs.
 | 
			
		||||
     * @param configKey The git config key to remove
 | 
			
		||||
     */
 | 
			
		||||
    removeSubmoduleGitConfig(configKey) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const pattern = regexpHelper.escape(configKey);
 | 
			
		||||
            yield this.git.submoduleForeach(
 | 
			
		||||
            // wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline
 | 
			
		||||
            // Wrap the pipeline in quotes to make sure it's handled properly by submoduleForeach, rather than just the first part of the pipeline.
 | 
			
		||||
            `sh -c "git config --local --name-only --get-regexp '${pattern}' && git config --local --unset-all '${configKey}' || :"`, true);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes includeIf entries that point to git-credentials-*.config files.
 | 
			
		||||
     * @param configPath Optional path to a specific git config file to operate on
 | 
			
		||||
     * @returns Array of unique credentials config file paths that were found and removed
 | 
			
		||||
     */
 | 
			
		||||
    removeIncludeIfCredentials(configPath) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const credentialsPaths = new Set();
 | 
			
		||||
            try {
 | 
			
		||||
                // Get all includeIf.gitdir keys
 | 
			
		||||
                const keys = yield this.git.tryGetConfigKeys('^includeIf\\.gitdir:', false, // globalConfig?
 | 
			
		||||
                configPath);
 | 
			
		||||
                for (const key of keys) {
 | 
			
		||||
                    // Get all values for this key
 | 
			
		||||
                    const values = yield this.git.tryGetConfigValues(key, false, // globalConfig?
 | 
			
		||||
                    configPath);
 | 
			
		||||
                    if (values.length > 0) {
 | 
			
		||||
                        // Remove only values that match git-credentials-<uuid>.config pattern
 | 
			
		||||
                        for (const value of values) {
 | 
			
		||||
                            if (this.testCredentialsConfigPath(value)) {
 | 
			
		||||
                                credentialsPaths.add(value);
 | 
			
		||||
                                yield this.git.tryConfigUnsetValue(key, value, false, configPath);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (err) {
 | 
			
		||||
                // Ignore errors - this is cleanup code
 | 
			
		||||
                if (configPath) {
 | 
			
		||||
                    core.debug(`Error during includeIf cleanup for ${configPath}: ${err}`);
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    core.debug(`Error during includeIf cleanup: ${err}`);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return Array.from(credentialsPaths);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Tests if a path matches the git-credentials-*.config pattern.
 | 
			
		||||
     * @param path The path to test
 | 
			
		||||
     * @returns True if the path matches the credentials config pattern
 | 
			
		||||
     */
 | 
			
		||||
    testCredentialsConfigPath(path) {
 | 
			
		||||
        return /git-credentials-[0-9a-f-]+\.config$/i.test(path);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -627,9 +793,15 @@ class GitCommandManager {
 | 
			
		||||
            yield this.execGit(args);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    config(configKey, configValue, globalConfig, add) {
 | 
			
		||||
    config(configKey, configValue, globalConfig, add, configFile) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const args = ['config', globalConfig ? '--global' : '--local'];
 | 
			
		||||
            const args = ['config'];
 | 
			
		||||
            if (configFile) {
 | 
			
		||||
                args.push('--file', configFile);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                args.push(globalConfig ? '--global' : '--local');
 | 
			
		||||
            }
 | 
			
		||||
            if (add) {
 | 
			
		||||
                args.push('--add');
 | 
			
		||||
            }
 | 
			
		||||
@@ -706,6 +878,16 @@ class GitCommandManager {
 | 
			
		||||
            throw new Error('Unexpected output when retrieving default branch');
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    getSubmoduleConfigPaths(recursive) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            // Get submodule config file paths.
 | 
			
		||||
            // Use `--show-origin` to get the config file path for each submodule.
 | 
			
		||||
            const output = yield this.submoduleForeach(`git config --local --show-origin --name-only --get-regexp remote.origin.url`, recursive);
 | 
			
		||||
            // Extract config file paths from the output (lines starting with "file:").
 | 
			
		||||
            const configPaths = output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [];
 | 
			
		||||
            return configPaths;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    getWorkingDirectory() {
 | 
			
		||||
        return this.workingDirectory;
 | 
			
		||||
    }
 | 
			
		||||
@@ -836,6 +1018,20 @@ class GitCommandManager {
 | 
			
		||||
            return output.exitCode === 0;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    tryConfigUnsetValue(configKey, configValue, globalConfig, configFile) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const args = ['config'];
 | 
			
		||||
            if (configFile) {
 | 
			
		||||
                args.push('--file', configFile);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                args.push(globalConfig ? '--global' : '--local');
 | 
			
		||||
            }
 | 
			
		||||
            args.push('--unset', configKey, configValue);
 | 
			
		||||
            const output = yield this.execGit(args, true);
 | 
			
		||||
            return output.exitCode === 0;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    tryDisableAutomaticGarbageCollection() {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const output = yield this.execGit(['config', '--local', 'gc.auto', '0'], true);
 | 
			
		||||
@@ -855,6 +1051,46 @@ class GitCommandManager {
 | 
			
		||||
            return stdout;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    tryGetConfigValues(configKey, globalConfig, configFile) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const args = ['config'];
 | 
			
		||||
            if (configFile) {
 | 
			
		||||
                args.push('--file', configFile);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                args.push(globalConfig ? '--global' : '--local');
 | 
			
		||||
            }
 | 
			
		||||
            args.push('--get-all', configKey);
 | 
			
		||||
            const output = yield this.execGit(args, true);
 | 
			
		||||
            if (output.exitCode !== 0) {
 | 
			
		||||
                return [];
 | 
			
		||||
            }
 | 
			
		||||
            return output.stdout
 | 
			
		||||
                .trim()
 | 
			
		||||
                .split('\n')
 | 
			
		||||
                .filter(value => value.trim());
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    tryGetConfigKeys(pattern, globalConfig, configFile) {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const args = ['config'];
 | 
			
		||||
            if (configFile) {
 | 
			
		||||
                args.push('--file', configFile);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                args.push(globalConfig ? '--global' : '--local');
 | 
			
		||||
            }
 | 
			
		||||
            args.push('--name-only', '--get-regexp', pattern);
 | 
			
		||||
            const output = yield this.execGit(args, true);
 | 
			
		||||
            if (output.exitCode !== 0) {
 | 
			
		||||
                return [];
 | 
			
		||||
            }
 | 
			
		||||
            return output.stdout
 | 
			
		||||
                .trim()
 | 
			
		||||
                .split('\n')
 | 
			
		||||
                .filter(key => key.trim());
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    tryReset() {
 | 
			
		||||
        return __awaiter(this, void 0, void 0, function* () {
 | 
			
		||||
            const output = yield this.execGit(['reset', '--hard', 'HEAD'], true);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user