mirror of
				https://github.com/actions/checkout.git
				synced 2025-10-28 16:18:37 +08:00 
			
		
		
		
	Compare commits
	
		
			7 Commits
		
	
	
		
			ebeaf55947
			...
			a7fcda9e31
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | a7fcda9e31 | ||
|   | 762bf756aa | ||
|   | 96c6589494 | ||
|   | 0f2eb6b146 | ||
|   | a60fb6cabe | ||
|   | 8e4be9ae12 | ||
|   | 74fe54f098 | 
|  | @ -595,11 +595,14 @@ describe('git-auth-helper tests', () => { | |||
|       await authHelper.configureSubmoduleAuth() | ||||
| 
 | ||||
|       // Assert
 | ||||
|       // Should get submodule config paths (1 call) and configure insteadOf (2 calls for two values)
 | ||||
|       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(4) | ||||
|       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( | ||||
|         /unset-all.*insteadOf/ | ||||
|       ) | ||||
|       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/) | ||||
|       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch( | ||||
|         /show-origin.*remote\.origin\.url/ | ||||
|       ) | ||||
|       expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch( | ||||
|         /url.*insteadOf.*git@github.com:/ | ||||
|       ) | ||||
|  | @ -634,11 +637,14 @@ describe('git-auth-helper tests', () => { | |||
|       await authHelper.configureSubmoduleAuth() | ||||
| 
 | ||||
|       // Assert
 | ||||
|       // Should get submodule config paths (1 call) and configure sshCommand (1 call)
 | ||||
|       expect(mockSubmoduleForeach).toHaveBeenCalledTimes(3) | ||||
|       expect(mockSubmoduleForeach.mock.calls[0][0]).toMatch( | ||||
|         /unset-all.*insteadOf/ | ||||
|       ) | ||||
|       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch(/http.*extraheader/) | ||||
|       expect(mockSubmoduleForeach.mock.calls[1][0]).toMatch( | ||||
|         /show-origin.*remote\.origin\.url/ | ||||
|       ) | ||||
|       expect(mockSubmoduleForeach.mock.calls[2][0]).toMatch(/core\.sshCommand/) | ||||
|     } | ||||
|   ) | ||||
|  | @ -776,6 +782,7 @@ async function setup(testName: string): Promise<void> { | |||
|   await fs.promises.mkdir(tempHomedir, {recursive: true}) | ||||
|   process.env['RUNNER_TEMP'] = runnerTemp | ||||
|   process.env['HOME'] = tempHomedir | ||||
|   process.env['GITHUB_WORKSPACE'] = workspace | ||||
| 
 | ||||
|   // Create git config
 | ||||
|   globalGitConfigPath = path.join(tempHomedir, '.gitconfig') | ||||
|  |  | |||
							
								
								
									
										87
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										87
									
								
								dist/index.js
									
									
									
									
										vendored
									
									
								
							|  | @ -163,7 +163,7 @@ class GitAuthHelper { | |||
|         this.sshKnownHostsPath = ''; | ||||
|         this.temporaryHomePath = ''; | ||||
|         this.credentialsConfigPath = ''; // Path to separate credentials config file in RUNNER_TEMP
 | ||||
|         this.credentialsIncludeKeys = []; // Track includeIf/include config keys for cleanup
 | ||||
|         this.credentialsIncludeKeys = []; // Track includeIf config keys for cleanup
 | ||||
|         this.git = gitCommandManager; | ||||
|         this.settings = gitSourceSettings || {}; | ||||
|         // Token auth header
 | ||||
|  | @ -268,20 +268,37 @@ 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) { | ||||
|                 // TODO: UPDATE THIS
 | ||||
|                 // 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
 | ||||
|                 // Credentials config path
 | ||||
|                 const credentialsConfigPath = yield this.getCredentialsConfigPath(); | ||||
|                 // Container credentials config path
 | ||||
|                 const containerCredentialsPath = path.posix.join('/github/runner_temp', path.basename(credentialsConfigPath)); | ||||
|                 // Container repo path
 | ||||
|                 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, '/'); | ||||
|                 const containerRepoPath = path.posix.join('/github/workspace', relativePath); | ||||
|                 // Get submodule config file paths.
 | ||||
|                 // Use `--show-origin` to get the config file path for each submodule.
 | ||||
|                 const output = yield this.git.submoduleForeach(`git config --local --show-origin --name-only --get-regexp remote.origin.url`, this.settings.nestedSubmodules); | ||||
|                 // Extract config file paths from the output (lines starting with "file:").
 | ||||
|                 const configPaths = output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []; | ||||
|                 // 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); | ||||
|                     // The config file is at .git/modules/submodule-name/config
 | ||||
|                     let submoduleConfigDir = path.dirname(configPath); | ||||
|                     submoduleConfigDir = submoduleConfigDir.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | ||||
|                     // Configure host includeIf
 | ||||
|                     yield this.git.config(`includeIf.gitdir:${submoduleConfigDir}.path`, credentialsConfigPath, false, false, configPath); | ||||
|                     // Configure container includeIf
 | ||||
|                     let relativeSubmoduleConfigDir = path.relative(githubWorkspace, submoduleConfigDir); | ||||
|                     relativeSubmoduleConfigDir = relativeSubmoduleConfigDir.replace(/\\/g, '/'); // Use forward slashes, even on Windows
 | ||||
|                     const containerSubmoduleGitDir = path.posix.join('/github/workspace', relativeSubmoduleConfigDir); | ||||
|                     yield this.git.config(`includeIf.gitdir:${containerSubmoduleGitDir}.path`, containerCredentialsPath, false, false, configPath); | ||||
|                 } | ||||
|                 if (this.settings.sshKey) { | ||||
|                     // Configure core.sshCommand
 | ||||
|  | @ -385,27 +402,23 @@ class GitAuthHelper { | |||
|                 yield this.git.config('include.path', credentialsConfigPath, true); | ||||
|             } | ||||
|             else { | ||||
|                 // For local config, use includeIf.gitdir to match the .git directory.
 | ||||
|                 // Configure for both host and container paths to support Docker container actions.
 | ||||
|                 // Host git directory
 | ||||
|                 let gitDir = path.join(this.git.getWorkingDirectory(), '.git'); | ||||
|                 console.log(`Git dir: ${gitDir}`); | ||||
|                 core.info(`Git dir: ${gitDir}`); | ||||
|                 // Use forward slashes for git config, even on Windows
 | ||||
|                 gitDir = gitDir.replace(/\\/g, '/'); | ||||
|                 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); | ||||
|                 this.credentialsIncludeKeys.push(hostIncludeKey); | ||||
|                 // Configure for container scenario where paths are mapped to fixed locations
 | ||||
|                 // Container git directory
 | ||||
|                 const githubWorkspace = process.env['GITHUB_WORKSPACE']; | ||||
|                 assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined'); | ||||
|                 // Calculate the relative path of the working directory from GITHUB_WORKSPACE
 | ||||
|                 const workingDirectory = this.git.getWorkingDirectory(); | ||||
|                 let relativePath = path.relative(githubWorkspace, workingDirectory); | ||||
|                 // Container paths: GITHUB_WORKSPACE -> /github/workspace, RUNNER_TEMP -> /github/runner_temp
 | ||||
|                 // Use forward slashes for git config
 | ||||
|                 relativePath = relativePath.replace(/\\/g, '/'); | ||||
|                 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); | ||||
|                 this.credentialsIncludeKeys.push(containerIncludeKey); | ||||
|  | @ -452,18 +465,22 @@ class GitAuthHelper { | |||
|             } | ||||
|             // SSH command
 | ||||
|             yield this.removeGitConfig(SSH_COMMAND_KEY); | ||||
|             yield this.removeSubmoduleGitConfig(SSH_COMMAND_KEY); | ||||
|         }); | ||||
|     } | ||||
|     removeToken() { | ||||
|         return __awaiter(this, void 0, void 0, function* () { | ||||
|             var _a; | ||||
|             // HTTP extra header
 | ||||
|             // Remove HTTP extra header
 | ||||
|             yield this.removeGitConfig(this.tokenConfigKey); | ||||
|             // Remove include/includeIf config entries
 | ||||
|             yield this.removeSubmoduleGitConfig(this.tokenConfigKey); | ||||
|             // Remove includeIf
 | ||||
|             for (const includeKey of this.credentialsIncludeKeys) { | ||||
|                 yield this.removeGitConfig(includeKey); | ||||
|             } | ||||
|             this.credentialsIncludeKeys = []; | ||||
|             // Remove submodule includeIf
 | ||||
|             yield this.git.submoduleForeach(`sh -c "git config --local --get-regexp '^includeIf\\.' && git config --local --remove-section includeIf || :"`, true); | ||||
|             // Remove credentials config file
 | ||||
|             if (this.credentialsConfigPath) { | ||||
|                 try { | ||||
|  | @ -476,18 +493,20 @@ class GitAuthHelper { | |||
|             } | ||||
|         }); | ||||
|     } | ||||
|     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`); | ||||
|                 } | ||||
|     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`); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|     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); | ||||
|         }); | ||||
|     } | ||||
|  |  | |||
|  | @ -44,7 +44,7 @@ class GitAuthHelper { | |||
|   private sshKnownHostsPath = '' | ||||
|   private temporaryHomePath = '' | ||||
|   private credentialsConfigPath = '' // Path to separate credentials config file in RUNNER_TEMP
 | ||||
|   private credentialsIncludeKeys: string[] = [] // Track includeIf/include config keys for cleanup
 | ||||
|   private credentialsIncludeKeys: string[] = [] // Track includeIf config keys for cleanup
 | ||||
| 
 | ||||
|   constructor( | ||||
|     gitCommandManager: IGitCommandManager, | ||||
|  | @ -168,26 +168,76 @@ class GitAuthHelper { | |||
| 
 | ||||
|   async configureSubmoduleAuth(): Promise<void> { | ||||
|     // Remove possible previous HTTPS instead of SSH
 | ||||
|     await this.removeGitConfig(this.insteadOfKey, true) | ||||
|     await this.removeSubmoduleGitConfig(this.insteadOfKey) | ||||
| 
 | ||||
|     if (this.settings.persistCredentials) { | ||||
|       // TODO: UPDATE THIS
 | ||||
|       // Credentials config path
 | ||||
|       const credentialsConfigPath = await this.getCredentialsConfigPath() | ||||
| 
 | ||||
|       // 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
 | ||||
|       // Container credentials config path
 | ||||
|       const containerCredentialsPath = path.posix.join( | ||||
|         '/github/runner_temp', | ||||
|         path.basename(credentialsConfigPath) | ||||
|       ) | ||||
| 
 | ||||
|       // Container repo path
 | ||||
|       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, '/') | ||||
|       const containerRepoPath = path.posix.join( | ||||
|         '/github/workspace', | ||||
|         relativePath | ||||
|       ) | ||||
| 
 | ||||
|       // Get submodule config file paths.
 | ||||
|       // Use `--show-origin` to get the config file path for each submodule.
 | ||||
|       const output = await 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"`, | ||||
|         `git config --local --show-origin --name-only --get-regexp remote.origin.url`, | ||||
|         this.settings.nestedSubmodules | ||||
|       ) | ||||
| 
 | ||||
|       // Replace the placeholder
 | ||||
|       const configPaths: string[] = | ||||
|       // Extract config file paths from the output (lines starting with "file:").
 | ||||
|       const configPaths = | ||||
|         output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [] | ||||
| 
 | ||||
|       // 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}'`) | ||||
|         await this.replaceTokenPlaceholder(configPath) | ||||
|         // The config file is at .git/modules/submodule-name/config
 | ||||
|         let submoduleConfigDir = path.dirname(configPath) | ||||
|         submoduleConfigDir = submoduleConfigDir.replace(/\\/g, '/') // Use forward slashes, even on Windows
 | ||||
| 
 | ||||
|         // Configure host includeIf
 | ||||
|         await this.git.config( | ||||
|           `includeIf.gitdir:${submoduleConfigDir}.path`, | ||||
|           credentialsConfigPath, | ||||
|           false, | ||||
|           false, | ||||
|           configPath | ||||
|         ) | ||||
| 
 | ||||
|         // Configure container includeIf
 | ||||
|         let relativeSubmoduleConfigDir = path.relative( | ||||
|           githubWorkspace, | ||||
|           submoduleConfigDir | ||||
|         ) | ||||
|         relativeSubmoduleConfigDir = relativeSubmoduleConfigDir.replace( | ||||
|           /\\/g, | ||||
|           '/' | ||||
|         ) // Use forward slashes, even on Windows
 | ||||
|         const containerSubmoduleGitDir = path.posix.join( | ||||
|           '/github/workspace', | ||||
|           relativeSubmoduleConfigDir | ||||
|         ) | ||||
|         await this.git.config( | ||||
|           `includeIf.gitdir:${containerSubmoduleGitDir}.path`, | ||||
|           containerCredentialsPath, | ||||
|           false, | ||||
|           false, | ||||
|           configPath | ||||
|         ) | ||||
|       } | ||||
| 
 | ||||
|       if (this.settings.sshKey) { | ||||
|  | @ -316,38 +366,34 @@ class GitAuthHelper { | |||
|       // Global config file is temporary
 | ||||
|       await this.git.config('include.path', credentialsConfigPath, true) | ||||
|     } else { | ||||
|       // For local config, use includeIf.gitdir to match the .git directory.
 | ||||
|       // Configure for both host and container paths to support Docker container actions.
 | ||||
|       // Host git directory
 | ||||
|       let gitDir = path.join(this.git.getWorkingDirectory(), '.git') | ||||
|       console.log(`Git dir: ${gitDir}`) | ||||
|       core.info(`Git dir: ${gitDir}`) | ||||
|       // Use forward slashes for git config, even on Windows
 | ||||
|       gitDir = gitDir.replace(/\\/g, '/') | ||||
|       gitDir = gitDir.replace(/\\/g, '/') // Use forward slashes, even on Windows
 | ||||
| 
 | ||||
|       // Configure host includeIf
 | ||||
|       const hostIncludeKey = `includeIf.gitdir:${gitDir}.path` | ||||
|       await this.git.config(hostIncludeKey, credentialsConfigPath) | ||||
|       this.credentialsIncludeKeys.push(hostIncludeKey) | ||||
| 
 | ||||
|       // Configure for container scenario where paths are mapped to fixed locations
 | ||||
|       // Container git directory
 | ||||
|       const githubWorkspace = process.env['GITHUB_WORKSPACE'] | ||||
|       assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined') | ||||
|        | ||||
|       // Calculate the relative path of the working directory from GITHUB_WORKSPACE
 | ||||
|       const workingDirectory = this.git.getWorkingDirectory() | ||||
|       let relativePath = path.relative(githubWorkspace, workingDirectory) | ||||
| 
 | ||||
|       // Container paths: GITHUB_WORKSPACE -> /github/workspace, RUNNER_TEMP -> /github/runner_temp
 | ||||
|       // Use forward slashes for git config
 | ||||
|       relativePath = relativePath.replace(/\\/g, '/') | ||||
|       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` | ||||
|       await this.git.config(containerIncludeKey, containerCredentialsPath) | ||||
|       this.credentialsIncludeKeys.push(containerIncludeKey) | ||||
|  | @ -397,18 +443,26 @@ class GitAuthHelper { | |||
| 
 | ||||
|     // SSH command
 | ||||
|     await this.removeGitConfig(SSH_COMMAND_KEY) | ||||
|     await this.removeSubmoduleGitConfig(SSH_COMMAND_KEY) | ||||
|   } | ||||
| 
 | ||||
|   private async removeToken(): Promise<void> { | ||||
|     // HTTP extra header
 | ||||
|     // Remove HTTP extra header
 | ||||
|     await this.removeGitConfig(this.tokenConfigKey) | ||||
|     await this.removeSubmoduleGitConfig(this.tokenConfigKey) | ||||
| 
 | ||||
|     // Remove include/includeIf config entries
 | ||||
|     // Remove includeIf
 | ||||
|     for (const includeKey of this.credentialsIncludeKeys) { | ||||
|       await this.removeGitConfig(includeKey) | ||||
|     } | ||||
|     this.credentialsIncludeKeys = [] | ||||
| 
 | ||||
|     // Remove submodule includeIf
 | ||||
|     await this.git.submoduleForeach( | ||||
|       `sh -c "git config --local --get-regexp '^includeIf\\.' && git config --local --remove-section includeIf || :"`, | ||||
|       true | ||||
|     ) | ||||
| 
 | ||||
|     // Remove credentials config file
 | ||||
|     if (this.credentialsConfigPath) { | ||||
|       try { | ||||
|  | @ -422,23 +476,20 @@ class GitAuthHelper { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private async removeGitConfig( | ||||
|     configKey: string, | ||||
|     submoduleOnly: boolean = false | ||||
|   ): Promise<void> { | ||||
|     if (!submoduleOnly) { | ||||
|       if ( | ||||
|         (await this.git.configExists(configKey)) && | ||||
|         !(await this.git.tryConfigUnset(configKey)) | ||||
|       ) { | ||||
|         // Load the config contents
 | ||||
|         core.warning(`Failed to remove '${configKey}' from the git config`) | ||||
|       } | ||||
|   private async removeGitConfig(configKey: string): Promise<void> { | ||||
|     if ( | ||||
|       (await this.git.configExists(configKey)) && | ||||
|       !(await this.git.tryConfigUnset(configKey)) | ||||
|     ) { | ||||
|       // Load the config contents
 | ||||
|       core.warning(`Failed to remove '${configKey}' from the git config`) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private async removeSubmoduleGitConfig(configKey: string): Promise<void> { | ||||
|     const pattern = regexpHelper.escape(configKey) | ||||
|     await 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 | ||||
|     ) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user