mirror of https://github.com/actions/checkout
Compare commits
3 Commits
579dce1d99
...
db5603096d
| Author | SHA1 | Date |
|---|---|---|
|
|
db5603096d | |
|
|
900f2210b1 | |
|
|
65f77605c0 |
|
|
@ -137,6 +137,11 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
|
|||
# Default: true
|
||||
show-progress: ''
|
||||
|
||||
# Path to a local bare git [reference repository to minimize network
|
||||
# usage](https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---reference-if-ableltrepositorygt).
|
||||
# If the directory doesn't exists this option will ignore it and log a message.
|
||||
reference: ''
|
||||
|
||||
# Whether to download Git-LFS files
|
||||
# Default: false
|
||||
lfs: ''
|
||||
|
|
|
|||
|
|
@ -1048,6 +1048,7 @@ async function setup(testName: string): Promise<void> {
|
|||
lfsInstall: jest.fn(),
|
||||
log1: jest.fn(),
|
||||
remoteAdd: jest.fn(),
|
||||
referenceAdd: jest.fn(),
|
||||
removeEnvironmentVariable: jest.fn((name: string) => delete git.env[name]),
|
||||
revParse: jest.fn(),
|
||||
setEnvironmentVariable: jest.fn((name: string, value: string) => {
|
||||
|
|
@ -1173,7 +1174,8 @@ async function setup(testName: string): Promise<void> {
|
|||
sshUser: '',
|
||||
workflowOrganizationId: 123456,
|
||||
setSafeDirectory: true,
|
||||
githubServerUrl: githubServerUrl
|
||||
githubServerUrl: githubServerUrl,
|
||||
reference: '/some/path'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -479,6 +479,7 @@ async function setup(testName: string): Promise<void> {
|
|||
lfsInstall: jest.fn(),
|
||||
log1: jest.fn(),
|
||||
remoteAdd: jest.fn(),
|
||||
referenceAdd: jest.fn(),
|
||||
removeEnvironmentVariable: jest.fn(),
|
||||
revParse: jest.fn(),
|
||||
setEnvironmentVariable: jest.fn(),
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ describe('input-helper tests', () => {
|
|||
expect(settings.repositoryOwner).toBe('some-owner')
|
||||
expect(settings.repositoryPath).toBe(gitHubWorkspace)
|
||||
expect(settings.setSafeDirectory).toBe(true)
|
||||
expect(settings.reference).toBe(undefined)
|
||||
})
|
||||
|
||||
it('qualifies ref', async () => {
|
||||
|
|
@ -133,6 +134,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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -80,6 +80,12 @@ inputs:
|
|||
show-progress:
|
||||
description: 'Whether to show progress status output when fetching.'
|
||||
default: true
|
||||
reference:
|
||||
required: false
|
||||
description: >
|
||||
Path to a local bare git [reference repository to minimize network usage](https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---reference-if-ableltrepositorygt).
|
||||
|
||||
If the directory doesn't exists this option will ignore it and log a message.
|
||||
lfs:
|
||||
description: 'Whether to download Git-LFS files'
|
||||
default: false
|
||||
|
|
|
|||
|
|
@ -935,6 +935,13 @@ class GitCommandManager {
|
|||
yield this.execGit(['remote', 'add', remoteName, remoteUrl]);
|
||||
});
|
||||
}
|
||||
referenceAdd(alternateObjects) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const alternatePath = path.join(this.workingDirectory, '.git/objects/info/alternates');
|
||||
core.info(`Adding a git alternate to reference repo at ${alternateObjects}`);
|
||||
yield fs.promises.writeFile(alternatePath, `${alternateObjects}\n`);
|
||||
});
|
||||
}
|
||||
removeEnvironmentVariable(name) {
|
||||
delete this.gitEnv[name];
|
||||
}
|
||||
|
|
@ -1490,6 +1497,17 @@ function getSource(settings) {
|
|||
yield git.init();
|
||||
yield git.remoteAdd('origin', repositoryUrl);
|
||||
core.endGroup();
|
||||
if (settings.reference !== undefined) {
|
||||
const alternateObjects = path.join(settings.reference, '/objects');
|
||||
if (fsHelper.directoryExistsSync(alternateObjects, false)) {
|
||||
core.startGroup('Adding a reference repository');
|
||||
yield git.referenceAdd(alternateObjects);
|
||||
core.endGroup();
|
||||
}
|
||||
else {
|
||||
core.warning(`Reference repository was specified, but directory ${alternateObjects} does not exists`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Disable automatic garbage collection
|
||||
core.startGroup('Disabling automatic garbage collection');
|
||||
|
|
@ -2021,7 +2039,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 = '';
|
||||
}
|
||||
|
|
@ -2095,6 +2113,8 @@ function getInputs() {
|
|||
// Determine the GitHub URL that the repository is being hosted from
|
||||
result.githubServerUrl = core.getInput('github-server-url');
|
||||
core.debug(`GitHub Host URL = ${result.githubServerUrl}`);
|
||||
result.reference = core.getInput('reference');
|
||||
core.debug(`Reference repository = ${result.reference}`);
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
|
@ -2444,7 +2464,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;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ export interface IGitCommandManager {
|
|||
lfsInstall(): Promise<void>
|
||||
log1(format?: string): Promise<string>
|
||||
remoteAdd(remoteName: string, remoteUrl: string): Promise<void>
|
||||
referenceAdd(reference: string): Promise<void>
|
||||
removeEnvironmentVariable(name: string): void
|
||||
revParse(ref: string): Promise<string>
|
||||
setEnvironmentVariable(name: string, value: string): void
|
||||
|
|
@ -401,6 +402,15 @@ class GitCommandManager {
|
|||
await this.execGit(['remote', 'add', remoteName, remoteUrl])
|
||||
}
|
||||
|
||||
async referenceAdd(alternateObjects: string): Promise<void> {
|
||||
const alternatePath = path.join(
|
||||
this.workingDirectory,
|
||||
'.git/objects/info/alternates'
|
||||
)
|
||||
core.info(`Adding a git alternate to reference repo at ${alternateObjects}`)
|
||||
await fs.promises.writeFile(alternatePath, `${alternateObjects}\n`)
|
||||
}
|
||||
|
||||
removeEnvironmentVariable(name: string): void {
|
||||
delete this.gitEnv[name]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,18 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||
await git.init()
|
||||
await git.remoteAdd('origin', repositoryUrl)
|
||||
core.endGroup()
|
||||
|
||||
if (settings.reference !== undefined) {
|
||||
const alternateObjects = path.join(settings.reference, '/objects')
|
||||
|
||||
if (fsHelper.directoryExistsSync(alternateObjects, false)) {
|
||||
core.startGroup('Adding a reference repository')
|
||||
await git.referenceAdd(alternateObjects)
|
||||
core.endGroup()
|
||||
} else {
|
||||
core.warning(`Reference repository was specified, but directory ${alternateObjects} does not exists`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Disable automatic garbage collection
|
||||
|
|
|
|||
|
|
@ -49,6 +49,11 @@ export interface IGitSourceSettings {
|
|||
*/
|
||||
fetchDepth: number
|
||||
|
||||
/**
|
||||
* The local reference repository
|
||||
*/
|
||||
reference: string | undefined
|
||||
|
||||
/**
|
||||
* Fetch tags, even if fetchDepth > 0 (default: false)
|
||||
*/
|
||||
|
|
@ -59,6 +64,11 @@ export interface IGitSourceSettings {
|
|||
*/
|
||||
showProgress: boolean
|
||||
|
||||
/**
|
||||
* The local reference repository
|
||||
*/
|
||||
reference: string | undefined
|
||||
|
||||
/**
|
||||
* Indicates whether to fetch LFS objects
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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 = ''
|
||||
}
|
||||
|
|
@ -161,5 +161,8 @@ export async function getInputs(): Promise<IGitSourceSettings> {
|
|||
result.githubServerUrl = core.getInput('github-server-url')
|
||||
core.debug(`GitHub Host URL = ${result.githubServerUrl}`)
|
||||
|
||||
result.reference = core.getInput('reference')
|
||||
core.debug(`Reference repository = ${result.reference}`)
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue