Read-only mirror — the public repositories of the Forgejo instance, refreshed every 10 minutes. The live instance (fg.dieschoenewolke.de) is reachable only over the VPN; clone here with https://git.dieschoenewolke.de/git/<owner>/<repo>.git.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoreric sciple <ericsciple@users.noreply.github.com>2025-11-13 17:20:02 -0600
committerGitHub <noreply@github.com>2025-11-13 17:20:02 -0600
commit65c48109f791f32b21b183a75cea8fc23cf659e6 (patch)
treeaea96b3e80c21c26a319652e510e7b0429d019a8 /src
parent8a1e468c248961a649547d7e37c1d549f21c9235 (diff)
Cleanup actions/checkout@v6 auth style (#2305)v4.3.1
Diffstat (limited to 'src')
-rw-r--r--src/git-auth-helper.ts107
-rw-r--r--src/git-command-manager.ts100
2 files changed, 206 insertions, 1 deletions
diff --git a/src/git-auth-helper.ts b/src/git-auth-helper.ts
index 126e8e5..0c82ddd 100644
--- a/src/git-auth-helper.ts
+++ b/src/git-auth-helper.ts
@@ -346,8 +346,58 @@ class GitAuthHelper {
}
private async removeToken(): Promise<void> {
- // HTTP extra header
+ // Remove HTTP extra header from local git config and submodule configs
await this.removeGitConfig(this.tokenConfigKey)
+
+ //
+ // Cleanup actions/checkout@v6 style credentials
+ //
+ const skipV6Cleanup = process.env['ACTIONS_CHECKOUT_SKIP_V6_CLEANUP']
+ if (skipV6Cleanup === '1' || skipV6Cleanup?.toLowerCase() === 'true') {
+ core.debug(
+ 'Skipping v6 style cleanup due to ACTIONS_CHECKOUT_SKIP_V6_CLEANUP'
+ )
+ return
+ }
+
+ try {
+ // Collect credentials config paths that need to be removed
+ const credentialsPaths = new Set<string>()
+
+ // Remove includeIf entries that point to git-credentials-*.config files
+ const mainCredentialsPaths = await this.removeIncludeIfCredentials()
+ mainCredentialsPaths.forEach(path => credentialsPaths.add(path))
+
+ // Remove submodule includeIf entries that point to git-credentials-*.config files
+ try {
+ const submoduleConfigPaths =
+ await this.git.getSubmoduleConfigPaths(true)
+ for (const configPath of submoduleConfigPaths) {
+ const submoduleCredentialsPaths =
+ await this.removeIncludeIfCredentials(configPath)
+ submoduleCredentialsPaths.forEach(path => credentialsPaths.add(path))
+ }
+ } catch (err) {
+ core.debug(`Unable to get submodule config paths: ${err}`)
+ }
+
+ // 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']
+ if (runnerTemp && credentialsPath.startsWith(runnerTemp)) {
+ try {
+ await io.rmRF(credentialsPath)
+ } catch (err) {
+ core.debug(
+ `Failed to remove credentials config '${credentialsPath}': ${err}`
+ )
+ }
+ }
+ }
+ } catch (err) {
+ core.debug(`Failed to cleanup v6 style credentials: ${err}`)
+ }
}
private async removeGitConfig(
@@ -371,4 +421,59 @@ class GitAuthHelper {
true
)
}
+
+ /**
+ * Removes includeIf entries that point to git-credentials-*.config files.
+ * This handles cleanup of credentials configured by newer versions of the action.
+ * @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
+ */
+ private async removeIncludeIfCredentials(
+ configPath?: string
+ ): Promise<string[]> {
+ const credentialsPaths = new Set<string>()
+
+ try {
+ // Get all includeIf.gitdir keys
+ const keys = await this.git.tryGetConfigKeys(
+ '^includeIf\\.gitdir:',
+ false, // globalConfig?
+ configPath
+ )
+
+ for (const key of keys) {
+ // Get all values for this key
+ const values = await 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)
+ await this.git.tryConfigUnsetValue(key, value, false, configPath)
+ }
+ }
+ }
+ }
+ } catch (err) {
+ // Ignore errors - this is cleanup code
+ core.debug(
+ `Error during includeIf cleanup${configPath ? ` for ${configPath}` : ''}: ${err}`
+ )
+ }
+
+ return Array.from(credentialsPaths)
+ }
+
+ /**
+ * Tests if a path matches the git-credentials-*.config pattern used by newer versions.
+ * @param path The path to test
+ * @returns True if the path matches the credentials config pattern
+ */
+ private testCredentialsConfigPath(path: string): boolean {
+ return /git-credentials-[0-9a-f-]+\.config$/i.test(path)
+ }
}
diff --git a/src/git-command-manager.ts b/src/git-command-manager.ts
index 8e42a38..9c789ac 100644
--- a/src/git-command-manager.ts
+++ b/src/git-command-manager.ts
@@ -41,6 +41,7 @@ export interface IGitCommandManager {
}
): Promise<void>
getDefaultBranch(repositoryUrl: string): Promise<string>
+ getSubmoduleConfigPaths(recursive: boolean): Promise<string[]>
getWorkingDirectory(): string
init(): Promise<void>
isDetached(): Promise<boolean>
@@ -59,8 +60,24 @@ export interface IGitCommandManager {
tagExists(pattern: string): Promise<boolean>
tryClean(): Promise<boolean>
tryConfigUnset(configKey: string, globalConfig?: boolean): Promise<boolean>
+ tryConfigUnsetValue(
+ configKey: string,
+ configValue: string,
+ globalConfig?: boolean,
+ configFile?: string
+ ): Promise<boolean>
tryDisableAutomaticGarbageCollection(): Promise<boolean>
tryGetFetchUrl(): Promise<string>
+ tryGetConfigValues(
+ configKey: string,
+ globalConfig?: boolean,
+ configFile?: string
+ ): Promise<string[]>
+ tryGetConfigKeys(
+ pattern: string,
+ globalConfig?: boolean,
+ configFile?: string
+ ): Promise<string[]>
tryReset(): Promise<boolean>
version(): Promise<GitVersion>
}
@@ -323,6 +340,21 @@ class GitCommandManager {
throw new Error('Unexpected output when retrieving default branch')
}
+ async getSubmoduleConfigPaths(recursive: boolean): Promise<string[]> {
+ // Get submodule config file paths.
+ // Use `--show-origin` to get the config file path for each submodule.
+ const output = await 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(): string {
return this.workingDirectory
}
@@ -455,6 +487,24 @@ class GitCommandManager {
return output.exitCode === 0
}
+ async tryConfigUnsetValue(
+ configKey: string,
+ configValue: string,
+ globalConfig?: boolean,
+ configFile?: string
+ ): Promise<boolean> {
+ const args = ['config']
+ if (configFile) {
+ args.push('--file', configFile)
+ } else {
+ args.push(globalConfig ? '--global' : '--local')
+ }
+ args.push('--unset', configKey, configValue)
+
+ const output = await this.execGit(args, true)
+ return output.exitCode === 0
+ }
+
async tryDisableAutomaticGarbageCollection(): Promise<boolean> {
const output = await this.execGit(
['config', '--local', 'gc.auto', '0'],
@@ -481,6 +531,56 @@ class GitCommandManager {
return stdout
}
+ async tryGetConfigValues(
+ configKey: string,
+ globalConfig?: boolean,
+ configFile?: string
+ ): Promise<string[]> {
+ const args = ['config']
+ if (configFile) {
+ args.push('--file', configFile)
+ } else {
+ args.push(globalConfig ? '--global' : '--local')
+ }
+ args.push('--get-all', configKey)
+
+ const output = await this.execGit(args, true)
+
+ if (output.exitCode !== 0) {
+ return []
+ }
+
+ return output.stdout
+ .trim()
+ .split('\n')
+ .filter(value => value.trim())
+ }
+
+ async tryGetConfigKeys(
+ pattern: string,
+ globalConfig?: boolean,
+ configFile?: string
+ ): Promise<string[]> {
+ const args = ['config']
+ if (configFile) {
+ args.push('--file', configFile)
+ } else {
+ args.push(globalConfig ? '--global' : '--local')
+ }
+ args.push('--name-only', '--get-regexp', pattern)
+
+ const output = await this.execGit(args, true)
+
+ if (output.exitCode !== 0) {
+ return []
+ }
+
+ return output.stdout
+ .trim()
+ .split('\n')
+ .filter(key => key.trim())
+ }
+
async tryReset(): Promise<boolean> {
const output = await this.execGit(['reset', '--hard', 'HEAD'], true)
return output.exitCode === 0