Skip to content

Commit 1edb703

Browse files
committed
Fix compatibility with actions/checkout@v6
Resolves issue #4228 by temporarily hiding checkout@v6 credential files to prevent duplicate Authorization headers. Solution approach: - Backup /git-credentials-*.config files before configuring auth - Configure action's auth in .git/config as normal - Restore credential files on cleanup This avoids the duplicate header error while properly preserving and restoring checkout's credentials for use in subsequent workflow steps. Based on recommendation from: actions/checkout#2299 (comment)
1 parent b4733b9 commit 1edb703

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

dist/index.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@ class GitConfigHelper {
11031103
this.extraheaderConfigPlaceholderValue = 'AUTHORIZATION: basic ***';
11041104
this.extraheaderConfigValueRegex = '^AUTHORIZATION:';
11051105
this.persistedExtraheaderConfigValue = '';
1106+
this.backedUpCredentialFiles = [];
11061107
this.git = git;
11071108
this.workingDirectory = this.git.getWorkingDirectory();
11081109
}
@@ -1182,12 +1183,16 @@ class GitConfigHelper {
11821183
return __awaiter(this, void 0, void 0, function* () {
11831184
const serverUrl = new url_1.URL(`https://${this.getGitRemote().hostname}`);
11841185
this.extraheaderConfigKey = `http.${serverUrl.origin}/.extraheader`;
1186+
// Backup checkout@v6 credential files if they exist
1187+
yield this.backupCheckoutCredentials();
11851188
// Save and unset persisted extraheader credential in git config if it exists
11861189
this.persistedExtraheaderConfigValue = yield this.getAndUnset();
11871190
});
11881191
}
11891192
restorePersistedAuth() {
11901193
return __awaiter(this, void 0, void 0, function* () {
1194+
// Restore checkout@v6 credential files if they were backed up
1195+
yield this.restoreCheckoutCredentials();
11911196
if (this.persistedExtraheaderConfigValue) {
11921197
try {
11931198
yield this.setExtraheaderConfig(this.persistedExtraheaderConfigValue);
@@ -1224,6 +1229,49 @@ class GitConfigHelper {
12241229
yield this.gitConfigStringReplace(this.extraheaderConfigPlaceholderValue, extraheaderConfigValue);
12251230
});
12261231
}
1232+
backupCheckoutCredentials() {
1233+
return __awaiter(this, void 0, void 0, function* () {
1234+
// Temporarily hide checkout@v6 credential files to avoid duplicate auth headers
1235+
// See: https://github.com/actions/checkout/issues/2299#issuecomment-3565808237
1236+
const runnerTemp = process.env['RUNNER_TEMP'];
1237+
if (!runnerTemp) {
1238+
return;
1239+
}
1240+
try {
1241+
const files = yield fs.promises.readdir(runnerTemp);
1242+
for (const file of files) {
1243+
if (file.startsWith('git-credentials-') && file.endsWith('.config')) {
1244+
const sourcePath = path.join(runnerTemp, file);
1245+
const backupPath = `${sourcePath}.bak`;
1246+
yield fs.promises.rename(sourcePath, backupPath);
1247+
this.backedUpCredentialFiles.push(backupPath);
1248+
core.info(`Temporarily hiding checkout credential file: ${file} (will be restored after)`);
1249+
}
1250+
}
1251+
}
1252+
catch (e) {
1253+
// If directory doesn't exist or we can't read it, just continue
1254+
core.debug(`Could not backup credential files: ${utils.getErrorMessage(e)}`);
1255+
}
1256+
});
1257+
}
1258+
restoreCheckoutCredentials() {
1259+
return __awaiter(this, void 0, void 0, function* () {
1260+
// Restore checkout@v6 credential files that were backed up
1261+
for (const backupPath of this.backedUpCredentialFiles) {
1262+
try {
1263+
const originalPath = backupPath.replace(/\.bak$/, '');
1264+
yield fs.promises.rename(backupPath, originalPath);
1265+
const fileName = path.basename(originalPath);
1266+
core.info(`Restored checkout credential file: ${fileName}`);
1267+
}
1268+
catch (e) {
1269+
core.warning(`Failed to restore credential file ${backupPath}: ${utils.getErrorMessage(e)}`);
1270+
}
1271+
}
1272+
this.backedUpCredentialFiles = [];
1273+
});
1274+
}
12271275
getAndUnset() {
12281276
return __awaiter(this, void 0, void 0, function* () {
12291277
let configValue = '';

src/git-config-helper.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export class GitConfigHelper {
2222
private extraheaderConfigPlaceholderValue = 'AUTHORIZATION: basic ***'
2323
private extraheaderConfigValueRegex = '^AUTHORIZATION:'
2424
private persistedExtraheaderConfigValue = ''
25+
private backedUpCredentialFiles: string[] = []
2526

2627
private constructor(git: GitCommandManager) {
2728
this.git = git
@@ -121,11 +122,15 @@ export class GitConfigHelper {
121122
async savePersistedAuth(): Promise<void> {
122123
const serverUrl = new URL(`https://${this.getGitRemote().hostname}`)
123124
this.extraheaderConfigKey = `http.${serverUrl.origin}/.extraheader`
125+
// Backup checkout@v6 credential files if they exist
126+
await this.backupCheckoutCredentials()
124127
// Save and unset persisted extraheader credential in git config if it exists
125128
this.persistedExtraheaderConfigValue = await this.getAndUnset()
126129
}
127130

128131
async restorePersistedAuth(): Promise<void> {
132+
// Restore checkout@v6 credential files if they were backed up
133+
await this.restoreCheckoutCredentials()
129134
if (this.persistedExtraheaderConfigValue) {
130135
try {
131136
await this.setExtraheaderConfig(this.persistedExtraheaderConfigValue)
@@ -169,6 +174,50 @@ export class GitConfigHelper {
169174
)
170175
}
171176

177+
private async backupCheckoutCredentials(): Promise<void> {
178+
// Temporarily hide checkout@v6 credential files to avoid duplicate auth headers
179+
// See: https://github.com/actions/checkout/issues/2299#issuecomment-3565808237
180+
const runnerTemp = process.env['RUNNER_TEMP']
181+
if (!runnerTemp) {
182+
return
183+
}
184+
185+
try {
186+
const files = await fs.promises.readdir(runnerTemp)
187+
for (const file of files) {
188+
if (file.startsWith('git-credentials-') && file.endsWith('.config')) {
189+
const sourcePath = path.join(runnerTemp, file)
190+
const backupPath = `${sourcePath}.bak`
191+
await fs.promises.rename(sourcePath, backupPath)
192+
this.backedUpCredentialFiles.push(backupPath)
193+
core.info(
194+
`Temporarily hiding checkout credential file: ${file} (will be restored after)`
195+
)
196+
}
197+
}
198+
} catch (e) {
199+
// If directory doesn't exist or we can't read it, just continue
200+
core.debug(`Could not backup credential files: ${utils.getErrorMessage(e)}`)
201+
}
202+
}
203+
204+
private async restoreCheckoutCredentials(): Promise<void> {
205+
// Restore checkout@v6 credential files that were backed up
206+
for (const backupPath of this.backedUpCredentialFiles) {
207+
try {
208+
const originalPath = backupPath.replace(/\.bak$/, '')
209+
await fs.promises.rename(backupPath, originalPath)
210+
const fileName = path.basename(originalPath)
211+
core.info(`Restored checkout credential file: ${fileName}`)
212+
} catch (e) {
213+
core.warning(
214+
`Failed to restore credential file ${backupPath}: ${utils.getErrorMessage(e)}`
215+
)
216+
}
217+
}
218+
this.backedUpCredentialFiles = []
219+
}
220+
172221
private async getAndUnset(): Promise<string> {
173222
let configValue = ''
174223
// Save and unset persisted extraheader credential in git config if it exists

0 commit comments

Comments
 (0)