mirror of
https://github.com/actions/checkout.git
synced 2026-09-16 12:53:06 +00:00
set the commit output when falling back to the REST API
When Git is not available on the runner, getSource() downloads the repository using the REST API and returns early, before the commit output is set. The ref output is still set by main.ts after getSource() returns, so the step reports a ref but an empty commit, even though action.yml documents commit unconditionally. Set the output from the resolved settings on that path. The REST API fallback does not create a local Git repository, so git log -1 cannot be used there.
This commit is contained in:
parent
f548e57e54
commit
7282dbb854
190
__test__/git-source-provider.test.ts
Normal file
190
__test__/git-source-provider.test.ts
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
import {jest, describe, it, expect, beforeEach} from '@jest/globals'
|
||||||
|
|
||||||
|
// Mock @actions/core before loading git-source-provider
|
||||||
|
const mockSetOutput = jest.fn()
|
||||||
|
jest.unstable_mockModule('@actions/core', () => ({
|
||||||
|
setOutput: mockSetOutput,
|
||||||
|
setSecret: jest.fn(),
|
||||||
|
setFailed: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
warning: jest.fn(),
|
||||||
|
info: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
startGroup: jest.fn(),
|
||||||
|
endGroup: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.unstable_mockModule('@actions/io', () => ({
|
||||||
|
cp: jest.fn(),
|
||||||
|
mkdirP: jest.fn(),
|
||||||
|
mv: jest.fn(),
|
||||||
|
rmRF: jest.fn(),
|
||||||
|
which: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.unstable_mockModule('../src/fs-helper.js', () => ({
|
||||||
|
directoryExistsSync: jest.fn(() => true),
|
||||||
|
existsSync: jest.fn(() => true),
|
||||||
|
fileExistsSync: jest.fn(() => false)
|
||||||
|
}))
|
||||||
|
|
||||||
|
const mockCreateCommandManager = jest.fn()
|
||||||
|
jest.unstable_mockModule('../src/git-command-manager.js', () => ({
|
||||||
|
createCommandManager: mockCreateCommandManager,
|
||||||
|
MinimumGitVersion: '2.18',
|
||||||
|
MinimumGitSparseCheckoutVersion: '2.28'
|
||||||
|
}))
|
||||||
|
|
||||||
|
const mockDownloadRepository = jest.fn()
|
||||||
|
jest.unstable_mockModule('../src/github-api-helper.js', () => ({
|
||||||
|
downloadRepository: mockDownloadRepository,
|
||||||
|
getDefaultBranch: jest.fn(async () => 'refs/heads/main'),
|
||||||
|
tryGetRepositoryObjectFormat: jest.fn(async () => ({
|
||||||
|
format: 'sha1',
|
||||||
|
succeeded: true
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.unstable_mockModule('../src/git-auth-helper.js', () => ({
|
||||||
|
createAuthHelper: jest.fn(() => ({
|
||||||
|
configureAuth: jest.fn(),
|
||||||
|
configureGlobalAuth: jest.fn(),
|
||||||
|
configureSubmoduleAuth: jest.fn(),
|
||||||
|
configureTempGlobalConfig: jest.fn(),
|
||||||
|
removeAuth: jest.fn(),
|
||||||
|
removeGlobalAuth: jest.fn(),
|
||||||
|
removeGlobalConfig: jest.fn()
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.unstable_mockModule('../src/git-directory-helper.js', () => ({
|
||||||
|
prepareExistingDirectory: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.unstable_mockModule('../src/ref-helper.js', () => ({
|
||||||
|
checkCommitInfo: jest.fn(),
|
||||||
|
getCheckoutInfo: jest.fn(async () => ({
|
||||||
|
ref: 'main',
|
||||||
|
startPoint: 'refs/remotes/origin/main'
|
||||||
|
})),
|
||||||
|
getRefSpec: jest.fn(() => ['+refs/heads/main:refs/remotes/origin/main']),
|
||||||
|
getRefSpecForAllHistory: jest.fn(() => [
|
||||||
|
'+refs/heads/main*:refs/remotes/origin/main*'
|
||||||
|
]),
|
||||||
|
testRef: jest.fn(async () => true)
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.unstable_mockModule('../src/state-helper.js', () => ({
|
||||||
|
setRepositoryPath: jest.fn(),
|
||||||
|
setSafeDirectory: jest.fn(),
|
||||||
|
IsPost: false,
|
||||||
|
PostSetSafeDirectory: false,
|
||||||
|
RepositoryPath: ''
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Dynamic imports after mocking
|
||||||
|
const gitSourceProvider = await import('../src/git-source-provider.js')
|
||||||
|
type IGitSourceSettings =
|
||||||
|
import('../src/git-source-settings.js').IGitSourceSettings
|
||||||
|
|
||||||
|
const commitSha = '1234567890123456789012345678901234567890'
|
||||||
|
|
||||||
|
function getSettings(): IGitSourceSettings {
|
||||||
|
return {
|
||||||
|
allowUnsafePrCheckout: false,
|
||||||
|
authToken: 'token',
|
||||||
|
clean: true,
|
||||||
|
commit: commitSha,
|
||||||
|
fetchDepth: 1,
|
||||||
|
fetchTags: false,
|
||||||
|
filter: undefined,
|
||||||
|
githubServerUrl: undefined,
|
||||||
|
lfs: false,
|
||||||
|
nestedSubmodules: false,
|
||||||
|
persistCredentials: true,
|
||||||
|
ref: 'refs/heads/main',
|
||||||
|
repositoryName: 'my-repo',
|
||||||
|
repositoryOwner: 'my-org',
|
||||||
|
repositoryPath: '/home/runner/work/my-repo/my-repo',
|
||||||
|
setSafeDirectory: false,
|
||||||
|
showProgress: false,
|
||||||
|
// Matches getInputs(), which leaves sparseCheckout undefined when the input is empty
|
||||||
|
sparseCheckout: undefined,
|
||||||
|
sparseCheckoutConeMode: true,
|
||||||
|
sshKey: '',
|
||||||
|
sshKnownHosts: '',
|
||||||
|
sshStrict: true,
|
||||||
|
sshUser: '',
|
||||||
|
submodules: false,
|
||||||
|
workflowOrganizationId: undefined
|
||||||
|
} as unknown as IGitSourceSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
// A minimal git command manager, for the cases that do not fall back to the REST API.
|
||||||
|
function getGitCommandManager(): any {
|
||||||
|
return {
|
||||||
|
checkout: jest.fn(),
|
||||||
|
config: jest.fn(),
|
||||||
|
disableSparseCheckout: jest.fn(),
|
||||||
|
init: jest.fn(),
|
||||||
|
log1: jest.fn(async (format?: string) =>
|
||||||
|
format ? `${commitSha}\n` : `commit ${commitSha}\n`
|
||||||
|
),
|
||||||
|
remoteAdd: jest.fn(),
|
||||||
|
fetch: jest.fn(),
|
||||||
|
tryDisableAutomaticGarbageCollection: jest.fn(async () => true),
|
||||||
|
version: jest.fn(async () => ({checkMinimum: () => false}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('git-source-provider tests', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sets the commit output when downloading using the REST API', async () => {
|
||||||
|
// Arrange
|
||||||
|
mockCreateCommandManager.mockImplementation(async () => {
|
||||||
|
throw new Error('Git is not installed')
|
||||||
|
})
|
||||||
|
const settings = getSettings()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await gitSourceProvider.getSource(settings)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(mockDownloadRepository).toHaveBeenCalled()
|
||||||
|
expect(mockSetOutput).toHaveBeenCalledWith('commit', commitSha)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sets the commit output when downloading using the REST API without a commit', async () => {
|
||||||
|
// Arrange
|
||||||
|
mockCreateCommandManager.mockImplementation(async () => {
|
||||||
|
throw new Error('Git is not installed')
|
||||||
|
})
|
||||||
|
const settings = getSettings()
|
||||||
|
settings.commit = ''
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await gitSourceProvider.getSource(settings)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(mockDownloadRepository).toHaveBeenCalled()
|
||||||
|
expect(mockSetOutput).toHaveBeenCalledWith('commit', '')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sets the commit output from git when git is available (control)', async () => {
|
||||||
|
// Arrange
|
||||||
|
const git = getGitCommandManager()
|
||||||
|
mockCreateCommandManager.mockImplementation(async () => git)
|
||||||
|
const settings = getSettings()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await gitSourceProvider.getSource(settings)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(mockDownloadRepository).not.toHaveBeenCalled()
|
||||||
|
expect(git.checkout).toHaveBeenCalled()
|
||||||
|
expect(mockSetOutput).toHaveBeenCalledWith('commit', commitSha)
|
||||||
|
})
|
||||||
|
})
|
||||||
3
dist/index.js
vendored
3
dist/index.js
vendored
@ -41755,6 +41755,9 @@ async function getSource(settings) {
|
|||||||
throw new Error(`Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${MinimumGitVersion} or higher to the PATH.`);
|
throw new Error(`Input 'ssh-key' not supported when falling back to download using the GitHub REST API. To create a local Git repository instead, add Git ${MinimumGitVersion} or higher to the PATH.`);
|
||||||
}
|
}
|
||||||
await downloadRepository(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath, settings.githubServerUrl);
|
await downloadRepository(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.ref, settings.commit, settings.repositoryPath, settings.githubServerUrl);
|
||||||
|
// Set the commit output. The REST API fallback does not create a local
|
||||||
|
// Git repository, so the SHA can only come from the resolved settings.
|
||||||
|
setOutput('commit', settings.commit);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Save state for POST action
|
// Save state for POST action
|
||||||
|
|||||||
@ -99,6 +99,10 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
|
|||||||
settings.repositoryPath,
|
settings.repositoryPath,
|
||||||
settings.githubServerUrl
|
settings.githubServerUrl
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Set the commit output. The REST API fallback does not create a local
|
||||||
|
// Git repository, so the SHA can only come from the resolved settings.
|
||||||
|
core.setOutput('commit', settings.commit)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user