mirror of
https://github.com/actions/checkout.git
synced 2026-07-05 18:53:46 +08:00
Compare commits
2 Commits
0c6e71a574
...
fix-window
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be385d8fff | ||
|
|
900f2210b1 |
@@ -974,6 +974,46 @@ describe('git-auth-helper tests', () => {
|
||||
).toBe(false)
|
||||
expect((authHelper as any).testCredentialsConfigPath('')).toBe(false)
|
||||
})
|
||||
|
||||
const includeIfCleanupRegex_matchesBothVariants =
|
||||
'includeIf cleanup regex matches both gitdir: and gitdir/i: keys'
|
||||
it(includeIfCleanupRegex_matchesBothVariants, async () => {
|
||||
// The cleanup regex must match both variants so credential
|
||||
// removal works regardless of which was written
|
||||
const regex = /^includeIf\.gitdir(\/i)?:/
|
||||
expect(regex.test('includeIf.gitdir:D:/workspaces/repo/.git.path')).toBe(
|
||||
true
|
||||
)
|
||||
expect(regex.test('includeIf.gitdir/i:D:/Workspaces/repo/.git.path')).toBe(
|
||||
true
|
||||
)
|
||||
expect(regex.test('includeIf.gitdir/i:/github/workspace/.git.path')).toBe(
|
||||
true
|
||||
)
|
||||
expect(regex.test('includeIf.gitdir:~/projects/foo/.git.path')).toBe(true)
|
||||
expect(regex.test('includeIf.onbranch:main.path')).toBe(false)
|
||||
expect(regex.test('include.path')).toBe(false)
|
||||
})
|
||||
|
||||
const includeIfDirective_usesCorrectVariantForPlatform =
|
||||
'includeIf directive uses gitdir/i on Windows and gitdir on other platforms'
|
||||
it(includeIfDirective_usesCorrectVariantForPlatform, async () => {
|
||||
await setup(includeIfDirective_usesCorrectVariantForPlatform)
|
||||
const authHelper = gitAuthHelper.createAuthHelper(git, settings)
|
||||
await authHelper.configureAuth()
|
||||
|
||||
const localConfigContent = (
|
||||
await fs.promises.readFile(localGitConfigPath)
|
||||
).toString()
|
||||
|
||||
if (isWindows) {
|
||||
expect(localConfigContent).toContain('includeIf.gitdir/i:')
|
||||
expect(localConfigContent).not.toContain('includeIf.gitdir:')
|
||||
} else {
|
||||
expect(localConfigContent).toContain('includeIf.gitdir:')
|
||||
expect(localConfigContent).not.toContain('includeIf.gitdir/i:')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
async function setup(testName: string): Promise<void> {
|
||||
|
||||
@@ -133,6 +133,16 @@ describe('input-helper tests', () => {
|
||||
expect(settings.commit).toBe('1111111111222222222233333333334444444444')
|
||||
})
|
||||
|
||||
it('sets ref to empty when explicit sha-256', async () => {
|
||||
inputs.ref =
|
||||
'1111111111222222222233333333334444444444555555555566666666667777'
|
||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
||||
expect(settings.ref).toBeFalsy()
|
||||
expect(settings.commit).toBe(
|
||||
'1111111111222222222233333333334444444444555555555566666666667777'
|
||||
)
|
||||
})
|
||||
|
||||
it('sets sha to empty when explicit ref', async () => {
|
||||
inputs.ref = 'refs/heads/some-other-ref'
|
||||
const settings: IGitSourceSettings = await inputHelper.getInputs()
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import * as assert from 'assert'
|
||||
import * as core from '@actions/core'
|
||||
import * as github from '@actions/github'
|
||||
import * as refHelper from '../lib/ref-helper'
|
||||
import {IGitCommandManager} from '../lib/git-command-manager'
|
||||
|
||||
const commit = '1234567890123456789012345678901234567890'
|
||||
const sha256Commit =
|
||||
'1234567890123456789012345678901234567890123456789012345678901234'
|
||||
let git: IGitCommandManager
|
||||
|
||||
describe('ref-helper tests', () => {
|
||||
@@ -37,6 +41,12 @@ describe('ref-helper tests', () => {
|
||||
expect(checkoutInfo.startPoint).toBeFalsy()
|
||||
})
|
||||
|
||||
it('getCheckoutInfo sha-256 only', async () => {
|
||||
const checkoutInfo = await refHelper.getCheckoutInfo(git, '', sha256Commit)
|
||||
expect(checkoutInfo.ref).toBe(sha256Commit)
|
||||
expect(checkoutInfo.startPoint).toBeFalsy()
|
||||
})
|
||||
|
||||
it('getCheckoutInfo refs/heads/', async () => {
|
||||
const checkoutInfo = await refHelper.getCheckoutInfo(
|
||||
git,
|
||||
@@ -227,4 +237,142 @@ describe('ref-helper tests', () => {
|
||||
'+refs/heads/my/branch:refs/remotes/origin/my/branch'
|
||||
)
|
||||
})
|
||||
|
||||
describe('checkCommitInfo', () => {
|
||||
const repositoryOwner = 'some-owner'
|
||||
const repositoryName = 'some-repo'
|
||||
const ref = 'refs/pull/123/merge'
|
||||
const sha1Head = '1111111111222222222233333333334444444444'
|
||||
const sha1Base = 'aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd'
|
||||
const sha256Head =
|
||||
'1111111111222222222233333333334444444444555555555566666666667777'
|
||||
const sha256Base =
|
||||
'aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff0000'
|
||||
let debugSpy: jest.SpyInstance
|
||||
let getOctokitSpy: jest.SpyInstance
|
||||
let repoGetSpy: jest.Mock
|
||||
let originalEventName: string
|
||||
let originalPayload: unknown
|
||||
let originalRef: string
|
||||
let originalSha: string
|
||||
|
||||
function setPullRequestContext(
|
||||
expectedHeadSha: string,
|
||||
expectedBaseSha: string,
|
||||
mergeCommit: string
|
||||
): void {
|
||||
;(github.context as any).eventName = 'pull_request'
|
||||
github.context.ref = ref
|
||||
github.context.sha = mergeCommit
|
||||
;(github.context as any).payload = {
|
||||
action: 'synchronize',
|
||||
after: expectedHeadSha,
|
||||
number: 123,
|
||||
pull_request: {
|
||||
base: {
|
||||
sha: expectedBaseSha
|
||||
}
|
||||
},
|
||||
repository: {
|
||||
private: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
originalEventName = github.context.eventName
|
||||
originalPayload = github.context.payload
|
||||
originalRef = github.context.ref
|
||||
originalSha = github.context.sha
|
||||
|
||||
jest.spyOn(github.context, 'repo', 'get').mockReturnValue({
|
||||
owner: repositoryOwner,
|
||||
repo: repositoryName
|
||||
})
|
||||
debugSpy = jest.spyOn(core, 'debug').mockImplementation(jest.fn())
|
||||
repoGetSpy = jest.fn(async () => ({}))
|
||||
getOctokitSpy = jest.spyOn(github, 'getOctokit').mockReturnValue({
|
||||
rest: {
|
||||
repos: {
|
||||
get: repoGetSpy
|
||||
}
|
||||
}
|
||||
} as any)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
;(github.context as any).eventName = originalEventName
|
||||
;(github.context as any).payload = originalPayload
|
||||
github.context.ref = originalRef
|
||||
github.context.sha = originalSha
|
||||
jest.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('returns early for SHA-1 merge commit', async () => {
|
||||
setPullRequestContext(sha1Head, sha1Base, commit)
|
||||
|
||||
await refHelper.checkCommitInfo(
|
||||
'token',
|
||||
`Merge ${sha1Head} into ${sha1Base}`,
|
||||
repositoryOwner,
|
||||
repositoryName,
|
||||
ref,
|
||||
commit
|
||||
)
|
||||
|
||||
expect(getOctokitSpy).not.toHaveBeenCalled()
|
||||
expect(repoGetSpy).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('matches SHA-256 merge commit info', async () => {
|
||||
const actualHeadSha =
|
||||
'9999999999888888888877777777776666666666555555555544444444443333'
|
||||
setPullRequestContext(sha256Head, sha256Base, sha256Commit)
|
||||
|
||||
await refHelper.checkCommitInfo(
|
||||
'token',
|
||||
`Merge ${actualHeadSha} into ${sha256Base}`,
|
||||
repositoryOwner,
|
||||
repositoryName,
|
||||
ref,
|
||||
sha256Commit
|
||||
)
|
||||
|
||||
expect(getOctokitSpy).toHaveBeenCalledWith(
|
||||
'token',
|
||||
expect.objectContaining({
|
||||
userAgent: expect.stringContaining(
|
||||
`expected_head_sha=${sha256Head};actual_head_sha=${actualHeadSha}`
|
||||
)
|
||||
})
|
||||
)
|
||||
expect(repoGetSpy).toHaveBeenCalledWith({
|
||||
owner: repositoryOwner,
|
||||
repo: repositoryName
|
||||
})
|
||||
expect(debugSpy).toHaveBeenCalledWith(
|
||||
`Expected head sha ${sha256Head}; actual head sha ${actualHeadSha}`
|
||||
)
|
||||
expect(debugSpy).not.toHaveBeenCalledWith('Unexpected message format')
|
||||
})
|
||||
|
||||
it('does not match 50-char hex as a valid merge', async () => {
|
||||
const invalidHeadSha =
|
||||
'99999999998888888888777777777766666666665555555555'
|
||||
setPullRequestContext(sha1Head, sha1Base, commit)
|
||||
|
||||
await refHelper.checkCommitInfo(
|
||||
'token',
|
||||
`Merge ${invalidHeadSha} into ${sha1Base}`,
|
||||
repositoryOwner,
|
||||
repositoryName,
|
||||
ref,
|
||||
commit
|
||||
)
|
||||
|
||||
expect(getOctokitSpy).not.toHaveBeenCalled()
|
||||
expect(repoGetSpy).not.toHaveBeenCalled()
|
||||
expect(debugSpy).toHaveBeenCalledWith('Unexpected message format')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -98,10 +98,6 @@ inputs:
|
||||
github-server-url:
|
||||
description: The base URL for the GitHub instance that you are trying to clone from, will use environment defaults to fetch from the same instance that the workflow is running from unless specified. Example URLs are https://github.com or https://my-ghes-server.example.com
|
||||
required: false
|
||||
skip-cleanup:
|
||||
description: Skips the cleanup phase on post action hook
|
||||
required: false
|
||||
default: false
|
||||
outputs:
|
||||
ref:
|
||||
description: 'The branch, tag or SHA that was checked out'
|
||||
|
||||
24
dist/index.js
vendored
24
dist/index.js
vendored
@@ -151,6 +151,12 @@ const stateHelper = __importStar(__nccwpck_require__(4866));
|
||||
const urlHelper = __importStar(__nccwpck_require__(9437));
|
||||
const uuid_1 = __nccwpck_require__(5840);
|
||||
const IS_WINDOWS = process.platform === 'win32';
|
||||
// Use case-insensitive gitdir matching on Windows to handle path casing mismatches
|
||||
// between the runner's GITHUB_WORKSPACE and the actual filesystem casing.
|
||||
// See: https://github.com/actions/checkout/issues/2345
|
||||
const INCLUDE_IF_GITDIR = IS_WINDOWS
|
||||
? 'includeIf.gitdir/i:'
|
||||
: 'includeIf.gitdir:';
|
||||
const SSH_COMMAND_KEY = 'core.sshCommand';
|
||||
function createAuthHelper(git, settings) {
|
||||
return new GitAuthHelper(git, settings);
|
||||
@@ -270,7 +276,7 @@ class GitAuthHelper {
|
||||
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?
|
||||
yield this.git.config(`${INCLUDE_IF_GITDIR}${submoduleGitDir}.path`, credentialsConfigPath, false, // globalConfig?
|
||||
false, // add?
|
||||
configPath);
|
||||
// Container submodule git directory
|
||||
@@ -280,7 +286,7 @@ class GitAuthHelper {
|
||||
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?
|
||||
yield this.git.config(`${INCLUDE_IF_GITDIR}${containerSubmoduleGitDir}.path`, containerCredentialsPath, false, // globalConfig?
|
||||
false, // add?
|
||||
configPath);
|
||||
}
|
||||
@@ -410,10 +416,10 @@ class GitAuthHelper {
|
||||
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`;
|
||||
const hostIncludeKey = `${INCLUDE_IF_GITDIR}${gitDir}.path`;
|
||||
yield this.git.config(hostIncludeKey, credentialsConfigPath);
|
||||
// Configure host includeIf for worktrees
|
||||
const hostWorktreeIncludeKey = `includeIf.gitdir:${gitDir}/worktrees/*.path`;
|
||||
const hostWorktreeIncludeKey = `${INCLUDE_IF_GITDIR}${gitDir}/worktrees/*.path`;
|
||||
yield this.git.config(hostWorktreeIncludeKey, credentialsConfigPath);
|
||||
// Container git directory
|
||||
const workingDirectory = this.git.getWorkingDirectory();
|
||||
@@ -425,10 +431,10 @@ class GitAuthHelper {
|
||||
// Container credentials config path
|
||||
const containerCredentialsPath = path.posix.join('/github/runner_temp', path.basename(credentialsConfigPath));
|
||||
// Configure container includeIf
|
||||
const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path`;
|
||||
const containerIncludeKey = `${INCLUDE_IF_GITDIR}${containerGitDir}.path`;
|
||||
yield this.git.config(containerIncludeKey, containerCredentialsPath);
|
||||
// Configure container includeIf for worktrees
|
||||
const containerWorktreeIncludeKey = `includeIf.gitdir:${containerGitDir}/worktrees/*.path`;
|
||||
const containerWorktreeIncludeKey = `${INCLUDE_IF_GITDIR}${containerGitDir}/worktrees/*.path`;
|
||||
yield this.git.config(containerWorktreeIncludeKey, containerCredentialsPath);
|
||||
}
|
||||
});
|
||||
@@ -565,7 +571,7 @@ class GitAuthHelper {
|
||||
const credentialsPaths = new Set();
|
||||
try {
|
||||
// Get all includeIf.gitdir keys
|
||||
const keys = yield this.git.tryGetConfigKeys('^includeIf\\.gitdir:', false, // globalConfig?
|
||||
const keys = yield this.git.tryGetConfigKeys('^includeIf\\.gitdir(/i)?:', false, // globalConfig?
|
||||
configPath);
|
||||
for (const key of keys) {
|
||||
// Get all values for this key
|
||||
@@ -2021,7 +2027,7 @@ function getInputs() {
|
||||
}
|
||||
}
|
||||
// SHA?
|
||||
else if (result.ref.match(/^[0-9a-fA-F]{40}$/)) {
|
||||
else if (result.ref.match(/^(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})$/)) {
|
||||
result.commit = result.ref;
|
||||
result.ref = '';
|
||||
}
|
||||
@@ -2444,7 +2450,7 @@ function checkCommitInfo(token, commitInfo, repositoryOwner, repositoryName, ref
|
||||
return;
|
||||
}
|
||||
// Extract details from message
|
||||
const match = commitInfo.match(/Merge ([0-9a-f]{40}) into ([0-9a-f]{40})/);
|
||||
const match = commitInfo.match(/Merge ([0-9a-f]{40}|[0-9a-f]{64}) into ([0-9a-f]{40}|[0-9a-f]{64})/);
|
||||
if (!match) {
|
||||
core.debug('Unexpected message format');
|
||||
return;
|
||||
|
||||
@@ -13,6 +13,12 @@ import {IGitCommandManager} from './git-command-manager'
|
||||
import {IGitSourceSettings} from './git-source-settings'
|
||||
|
||||
const IS_WINDOWS = process.platform === 'win32'
|
||||
// Use case-insensitive gitdir matching on Windows to handle path casing mismatches
|
||||
// between the runner's GITHUB_WORKSPACE and the actual filesystem casing.
|
||||
// See: https://github.com/actions/checkout/issues/2345
|
||||
const INCLUDE_IF_GITDIR = IS_WINDOWS
|
||||
? 'includeIf.gitdir/i:'
|
||||
: 'includeIf.gitdir:'
|
||||
const SSH_COMMAND_KEY = 'core.sshCommand'
|
||||
|
||||
export interface IGitAuthHelper {
|
||||
@@ -182,7 +188,7 @@ class GitAuthHelper {
|
||||
|
||||
// Configure host includeIf
|
||||
await this.git.config(
|
||||
`includeIf.gitdir:${submoduleGitDir}.path`,
|
||||
`${INCLUDE_IF_GITDIR}${submoduleGitDir}.path`,
|
||||
credentialsConfigPath,
|
||||
false, // globalConfig?
|
||||
false, // add?
|
||||
@@ -204,7 +210,7 @@ class GitAuthHelper {
|
||||
|
||||
// Configure container includeIf
|
||||
await this.git.config(
|
||||
`includeIf.gitdir:${containerSubmoduleGitDir}.path`,
|
||||
`${INCLUDE_IF_GITDIR}${containerSubmoduleGitDir}.path`,
|
||||
containerCredentialsPath,
|
||||
false, // globalConfig?
|
||||
false, // add?
|
||||
@@ -371,11 +377,11 @@ class GitAuthHelper {
|
||||
gitDir = gitDir.replace(/\\/g, '/') // Use forward slashes, even on Windows
|
||||
|
||||
// Configure host includeIf
|
||||
const hostIncludeKey = `includeIf.gitdir:${gitDir}.path`
|
||||
const hostIncludeKey = `${INCLUDE_IF_GITDIR}${gitDir}.path`
|
||||
await this.git.config(hostIncludeKey, credentialsConfigPath)
|
||||
|
||||
// Configure host includeIf for worktrees
|
||||
const hostWorktreeIncludeKey = `includeIf.gitdir:${gitDir}/worktrees/*.path`
|
||||
const hostWorktreeIncludeKey = `${INCLUDE_IF_GITDIR}${gitDir}/worktrees/*.path`
|
||||
await this.git.config(hostWorktreeIncludeKey, credentialsConfigPath)
|
||||
|
||||
// Container git directory
|
||||
@@ -397,11 +403,11 @@ class GitAuthHelper {
|
||||
)
|
||||
|
||||
// Configure container includeIf
|
||||
const containerIncludeKey = `includeIf.gitdir:${containerGitDir}.path`
|
||||
const containerIncludeKey = `${INCLUDE_IF_GITDIR}${containerGitDir}.path`
|
||||
await this.git.config(containerIncludeKey, containerCredentialsPath)
|
||||
|
||||
// Configure container includeIf for worktrees
|
||||
const containerWorktreeIncludeKey = `includeIf.gitdir:${containerGitDir}/worktrees/*.path`
|
||||
const containerWorktreeIncludeKey = `${INCLUDE_IF_GITDIR}${containerGitDir}/worktrees/*.path`
|
||||
await this.git.config(
|
||||
containerWorktreeIncludeKey,
|
||||
containerCredentialsPath
|
||||
@@ -554,7 +560,7 @@ class GitAuthHelper {
|
||||
try {
|
||||
// Get all includeIf.gitdir keys
|
||||
const keys = await this.git.tryGetConfigKeys(
|
||||
'^includeIf\\.gitdir:',
|
||||
'^includeIf\\.gitdir(/i)?:',
|
||||
false, // globalConfig?
|
||||
configPath
|
||||
)
|
||||
|
||||
@@ -118,9 +118,4 @@ export interface IGitSourceSettings {
|
||||
* User override on the GitHub Server/Host URL that hosts the repository to be cloned
|
||||
*/
|
||||
githubServerUrl: string | undefined
|
||||
|
||||
/**
|
||||
* Disable the post action cleanup phase
|
||||
*/
|
||||
skipCleanup: boolean
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
||||
}
|
||||
}
|
||||
// SHA?
|
||||
else if (result.ref.match(/^[0-9a-fA-F]{40}$/)) {
|
||||
else if (result.ref.match(/^(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})$/)) {
|
||||
result.commit = result.ref
|
||||
result.ref = ''
|
||||
}
|
||||
|
||||
@@ -30,12 +30,6 @@ async function run(): Promise<void> {
|
||||
}
|
||||
|
||||
async function cleanup(): Promise<void> {
|
||||
const sourceSettings = await inputHelper.getInputs()
|
||||
|
||||
if (sourceSettings.skipCleanup) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await gitSourceProvider.cleanup(stateHelper.RepositoryPath)
|
||||
} catch (error) {
|
||||
|
||||
@@ -258,7 +258,9 @@ export async function checkCommitInfo(
|
||||
}
|
||||
|
||||
// Extract details from message
|
||||
const match = commitInfo.match(/Merge ([0-9a-f]{40}) into ([0-9a-f]{40})/)
|
||||
const match = commitInfo.match(
|
||||
/Merge ([0-9a-f]{40}|[0-9a-f]{64}) into ([0-9a-f]{40}|[0-9a-f]{64})/
|
||||
)
|
||||
if (!match) {
|
||||
core.debug('Unexpected message format')
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user