From 8e4be9ae1244fa3ca78fd2d317e8b4a80ddaf6d8 Mon Sep 17 00:00:00 2001 From: eric sciple Date: Tue, 14 Oct 2025 22:10:23 +0000 Subject: [PATCH] Add container path support for submodules and improve code readability --- dist/index.js | 43 ++++++++++++++++-------- src/git-auth-helper.ts | 75 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 91 insertions(+), 27 deletions(-) diff --git a/dist/index.js b/dist/index.js index b1bd84b..dddd213 100644 --- a/dist/index.js +++ b/dist/index.js @@ -270,18 +270,33 @@ class GitAuthHelper { // Remove possible previous HTTPS instead of SSH yield this.removeGitConfig(this.insteadOfKey, true); 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 - const configPaths = output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || []; - for (const configPath of configPaths) { - core.debug(`Replacing token placeholder in '${configPath}'`); - yield this.replaceTokenPlaceholder(configPath); + // Use the same credentials config file created for the main repo + const credentialsConfigPath = yield this.getCredentialsConfigPath(); + const githubWorkspace = process.env['GITHUB_WORKSPACE']; + assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined'); + const containerCredentialsPath = path.posix.join('/github/runner_temp', path.basename(credentialsConfigPath)); + // Calculate container git directory base path + const workingDirectory = this.git.getWorkingDirectory(); + let relativePath = path.relative(githubWorkspace, workingDirectory); + relativePath = relativePath.replace(/\\/g, '/'); + const containerWorkspaceBase = path.posix.join('/github/workspace', relativePath); + // Get submodule paths. + // `git rev-parse --show-toplevel` returns the absolute path of each submodule's working tree. + const submodulePaths = yield this.git.submoduleForeach(`git rev-parse --show-toplevel`, 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 submodulePath of submodulePaths.split('\n').filter(x => x)) { + // Configure host path includeIf. + // Use forward slashes for git config, even on Windows. + let submoduleGitDir = path.join(submodulePath, '.git'); + submoduleGitDir = submoduleGitDir.replace(/\\/g, '/'); + yield this.git.config(`includeIf.gitdir:${submoduleGitDir}.path`, credentialsConfigPath, false, false, path.join(submodulePath, '.git', 'config')); + // Configure container path includeIf. + // Use forward slashes for git config, even on Windows. + let submoduleRelativePath = path.relative(workingDirectory, submodulePath); + submoduleRelativePath = submoduleRelativePath.replace(/\\/g, '/'); + const containerSubmoduleGitDir = path.posix.join(containerWorkspaceBase, submoduleRelativePath, '.git'); + yield this.git.config(`includeIf.gitdir:${containerSubmoduleGitDir}.path`, containerCredentialsPath, false, false, path.join(submodulePath, '.git', 'config')); } if (this.settings.sshKey) { // Configure core.sshCommand @@ -388,8 +403,6 @@ class GitAuthHelper { // For local config, use includeIf.gitdir to match the .git directory. // Configure for both host and container paths to support Docker container actions. 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, '/'); const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`; @@ -464,6 +477,8 @@ class GitAuthHelper { yield this.removeGitConfig(includeKey); } this.credentialsIncludeKeys = []; + // Remove includeIf entries from submodules + 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 { diff --git a/src/git-auth-helper.ts b/src/git-auth-helper.ts index 35e0ddf..b7ac196 100644 --- a/src/git-auth-helper.ts +++ b/src/git-auth-helper.ts @@ -171,23 +171,66 @@ class GitAuthHelper { await this.removeGitConfig(this.insteadOfKey, true) if (this.settings.persistCredentials) { - // TODO: UPDATE THIS + // Use the same credentials config file created for the main repo + const credentialsConfigPath = await this.getCredentialsConfigPath() + const githubWorkspace = process.env['GITHUB_WORKSPACE'] + assert.ok(githubWorkspace, 'GITHUB_WORKSPACE is not defined') - // 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 = 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"`, + const containerCredentialsPath = path.posix.join( + '/github/runner_temp', + path.basename(credentialsConfigPath) + ) + + // Calculate container git directory base path + const workingDirectory = this.git.getWorkingDirectory() + let relativePath = path.relative(githubWorkspace, workingDirectory) + relativePath = relativePath.replace(/\\/g, '/') + const containerWorkspaceBase = path.posix.join( + '/github/workspace', + relativePath + ) + + // Get submodule paths. + // `git rev-parse --show-toplevel` returns the absolute path of each submodule's working tree. + const submodulePaths = await this.git.submoduleForeach( + `git rev-parse --show-toplevel`, this.settings.nestedSubmodules ) - // Replace the placeholder - const configPaths: string[] = - output.match(/(?<=(^|\n)file:)[^\t]+(?=\tremote\.origin\.url)/g) || [] - for (const configPath of configPaths) { - core.debug(`Replacing token placeholder in '${configPath}'`) - await this.replaceTokenPlaceholder(configPath) + // 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 submodulePath of submodulePaths.split('\n').filter(x => x)) { + // Configure host path includeIf. + // Use forward slashes for git config, even on Windows. + let submoduleGitDir = path.join(submodulePath, '.git') + submoduleGitDir = submoduleGitDir.replace(/\\/g, '/') + await this.git.config( + `includeIf.gitdir:${submoduleGitDir}.path`, + credentialsConfigPath, + false, + false, + path.join(submodulePath, '.git', 'config') + ) + + // Configure container path includeIf. + // Use forward slashes for git config, even on Windows. + let submoduleRelativePath = path.relative( + workingDirectory, + submodulePath + ) + submoduleRelativePath = submoduleRelativePath.replace(/\\/g, '/') + const containerSubmoduleGitDir = path.posix.join( + containerWorkspaceBase, + submoduleRelativePath, + '.git' + ) + await this.git.config( + `includeIf.gitdir:${containerSubmoduleGitDir}.path`, + containerCredentialsPath, + false, + false, + path.join(submodulePath, '.git', 'config') + ) } if (this.settings.sshKey) { @@ -407,6 +450,12 @@ class GitAuthHelper { } this.credentialsIncludeKeys = [] + // Remove includeIf entries from submodules + 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 {