Compare commits

...

11 Commits

Author SHA1 Message Date
Johnny Willemsen cd7d7447fe
Merge f80349a6ad into df4cb1c069 2026-06-11 12:33:08 +00:00
Yashwanth Anantharaju df4cb1c069
Update changelog for v6.0.3 (#2446) 2026-06-02 10:31:30 -04:00
Yashwanth Anantharaju 1cce3390c2
Fix checkout init for SHA-256 repositories (#2439)
* Fix checkout init for SHA-256 repositories

* Remove unused object format result field
2026-06-01 11:35:58 -04:00
Johnny Willemsen f80349a6ad
Merge branch 'actions:main' into jwi-pathdocu 2022-09-02 14:11:12 +02:00
Johnny Willemsen 8af5cbfde0
Merge branch 'main' into jwi-pathdocu 2020-10-02 17:20:12 +02:00
Johnny Willemsen d795c2780e
Move env to test step
* .github/workflows/test.yml:
2020-08-26 12:44:28 +02:00
Johnny Willemsen d345e1892f
Back to basic
* .github/workflows/test.yml:
2020-08-26 12:26:01 +02:00
Johnny Willemsen 388793bd97
Use ref as it should
* .github/workflows/test.yml:
2020-08-26 12:25:15 +02:00
Johnny Willemsen 4f92610a97
Added environment based path test
* __test__/verify-environment-path.sh:
      Added.

    * .github/workflows/test.yml:
2020-08-26 12:18:55 +02:00
Johnny Willemsen e67ad3383b
Removed side by side
* README.md:
2020-08-26 12:11:07 +02:00
Johnny Willemsen 146dd77449
The path can be relative or absolute under $GITHUB_WORKSPACE
* README.md:
    * action.yml:
    * adrs/0153-checkout-v2.md:
2020-08-26 12:09:04 +02:00
12 changed files with 319 additions and 10 deletions

View File

@ -29,6 +29,8 @@ jobs:
run: __test__/verify-no-unstaged-changes.sh run: __test__/verify-no-unstaged-changes.sh
test: test:
env:
main_path: main_path_test
strategy: strategy:
matrix: matrix:
runs-on: [ubuntu-latest, macos-latest, windows-latest] runs-on: [ubuntu-latest, macos-latest, windows-latest]
@ -62,6 +64,16 @@ jobs:
shell: bash shell: bash
run: __test__/verify-clean.sh run: __test__/verify-clean.sh
# Use environment variable as path
- name: Environment path test
uses: ./
with:
ref: test-data/v2/basic
path: ${{ env.main_path }}
- name: Verify environment path test
shell: bash
run: __test__/verify-environment-path.sh
# Side by side # Side by side
- name: Checkout side by side 1 - name: Checkout side by side 1
uses: ./ uses: ./

View File

@ -1,5 +1,9 @@
# Changelog # Changelog
## v6.0.3
* Fix checkout init for SHA-256 repositories by @yaananth in https://github.com/actions/checkout/pull/2439
* fix: expand merge commit SHA regex and add SHA-256 test cases by @yaananth in https://github.com/actions/checkout/pull/2414
## v6.0.2 ## v6.0.2
* Fix tag handling: preserve annotations and explicit fetch-tags by @ericsciple in https://github.com/actions/checkout/pull/2356 * Fix tag handling: preserve annotations and explicit fetch-tags by @ericsciple in https://github.com/actions/checkout/pull/2356

View File

@ -105,7 +105,7 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
# Default: true # Default: true
persist-credentials: '' persist-credentials: ''
# Relative path under $GITHUB_WORKSPACE to place the repository # Relative or absolute path under $GITHUB_WORKSPACE to place the repository
path: '' path: ''
# Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching # Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching
@ -257,6 +257,18 @@ Please refer to the [release page](https://github.com/actions/checkout/releases/
``` ```
> - If your secondary repository is private or internal you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private) > - If your secondary repository is private or internal you will need to add the option noted in [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
## Checkout repo with a environment based path
```yaml
env:
main_path: ${{ github.workspace }}/main
steps:
- name: Checkout
uses: actions/checkout@v2
with:
path: ${{ env.main_path }}
```
## Checkout multiple repos (nested) ## Checkout multiple repos (nested)
```yaml ```yaml

View File

@ -378,6 +378,59 @@ describe('Test fetchDepth and fetchTags options', () => {
}) })
}) })
describe('repository initialization object format', () => {
beforeEach(async () => {
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
})
afterEach(() => {
jest.restoreAllMocks()
})
it('initializes SHA-256 repositories with the matching object format', async () => {
mockExec.mockImplementation((path, args, options) => {
if (args.includes('version')) {
options.listeners.stdout(Buffer.from('git version 2.50.1'))
}
return 0
})
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
git = await commandManager.createCommandManager('test', false, false)
await git.init('sha256')
expect(mockExec).toHaveBeenCalledWith(
expect.any(String),
['init', '--object-format=sha256', 'test'],
expect.any(Object)
)
})
it('initializes SHA-1 repositories with existing default arguments', async () => {
mockExec.mockImplementation((path, args, options) => {
if (args.includes('version')) {
options.listeners.stdout(Buffer.from('git version 2.50.1'))
}
return 0
})
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
git = await commandManager.createCommandManager('test', false, false)
await git.init('sha1')
expect(mockExec).toHaveBeenCalledWith(
expect.any(String),
['init', 'test'],
expect.any(Object)
)
})
})
describe('git user-agent with orchestration ID', () => { describe('git user-agent with orchestration ID', () => {
beforeEach(async () => { beforeEach(async () => {
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn()) jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())

View File

@ -0,0 +1,98 @@
import * as core from '@actions/core'
import * as github from '@actions/github'
import * as githubApiHelper from '../lib/github-api-helper'
describe('github-api-helper object format', () => {
let getOctokitSpy: jest.SpyInstance
let debugSpy: jest.SpyInstance
let request: jest.Mock
function mockHashAlgorithmApi(hashAlgorithm: string): void {
request = jest.fn(async () => ({
data: {
hash_algorithm: hashAlgorithm
}
}))
getOctokitSpy = jest.spyOn(github, 'getOctokit').mockReturnValue({
request
} as any)
}
beforeEach(() => {
debugSpy = jest.spyOn(core, 'debug').mockImplementation(jest.fn())
})
afterEach(() => {
jest.restoreAllMocks()
})
it('detects SHA-256 from the repository hash algorithm endpoint', async () => {
mockHashAlgorithmApi('sha256')
await expect(
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
).resolves.toEqual({format: 'sha256', succeeded: true})
expect(getOctokitSpy).toHaveBeenCalledWith(
'token',
expect.objectContaining({baseUrl: 'https://api.github.com'})
)
expect(request).toHaveBeenCalledWith(
'GET /repos/{owner}/{repo}/hash-algorithm',
{owner: 'owner', repo: 'repo'}
)
})
it('detects SHA-1 from the repository hash algorithm endpoint', async () => {
mockHashAlgorithmApi('sha1')
await expect(
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
).resolves.toEqual({format: 'sha1', succeeded: true})
})
it('detects object format from an existing commit without API calls', async () => {
const commitSha =
'9422233ca7ee1b17f1e905d0e141faf0c401556c41cdc6acd71c6bd685da2e92'
getOctokitSpy = jest.spyOn(github, 'getOctokit')
await expect(
githubApiHelper.tryGetRepositoryObjectFormat(
'token',
'owner',
'repo',
undefined,
commitSha
)
).resolves.toEqual({format: 'sha256', succeeded: true})
expect(getOctokitSpy).not.toHaveBeenCalled()
})
it('returns unsuccessful when the hash algorithm endpoint value is not recognized', async () => {
mockHashAlgorithmApi('unknown')
await expect(
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
).resolves.toEqual({format: '', succeeded: false})
expect(debugSpy).toHaveBeenCalledWith(
'Unable to determine repository object format from hash-algorithm endpoint'
)
})
it('returns unsuccessful when the hash algorithm API lookup fails', async () => {
request = jest.fn(async () => {
throw new Error('not found')
})
jest.spyOn(github, 'getOctokit').mockReturnValue({
request
} as any)
await expect(
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
).resolves.toEqual({format: '', succeeded: false})
expect(debugSpy).toHaveBeenCalledWith(
'Unable to determine repository object format from hash-algorithm endpoint: not found'
)
})
})

View File

@ -0,0 +1,6 @@
#!/bin/bash
if [ ! -f "./main_path_test/basic-file.txt" ]; then
echo "Expected file does not exist"
exit 1
fi

View File

@ -53,7 +53,7 @@ inputs:
description: 'Whether to configure the token or SSH key with the local git config' description: 'Whether to configure the token or SSH key with the local git config'
default: true default: true
path: path:
description: 'Relative path under $GITHUB_WORKSPACE to place the repository' description: 'Relative or absolute path under $GITHUB_WORKSPACE to place the repository'
clean: clean:
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching' description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
default: true default: true

View File

@ -65,7 +65,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
description: 'Whether to configure the token or SSH key with the local git config' description: 'Whether to configure the token or SSH key with the local git config'
default: true default: true
path: path:
description: 'Relative path under $GITHUB_WORKSPACE to place the repository' description: 'Relative or absolute path under $GITHUB_WORKSPACE to place the repository'
clean: clean:
description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching' description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
default: true default: true

55
dist/index.js vendored
View File

@ -896,9 +896,14 @@ class GitCommandManager {
getWorkingDirectory() { getWorkingDirectory() {
return this.workingDirectory; return this.workingDirectory;
} }
init() { init(objectFormat) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
yield this.execGit(['init', this.workingDirectory]); const args = ['init'];
if (objectFormat === 'sha256') {
args.push('--object-format=sha256');
}
args.push(this.workingDirectory);
yield this.execGit(args);
}); });
} }
isDetached() { isDetached() {
@ -1486,8 +1491,17 @@ function getSource(settings) {
stateHelper.setRepositoryPath(settings.repositoryPath); stateHelper.setRepositoryPath(settings.repositoryPath);
// Initialize the repository // Initialize the repository
if (!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))) { if (!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))) {
core.startGroup('Determining repository object format');
const objectFormatResult = yield githubApiHelper.tryGetRepositoryObjectFormat(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.githubServerUrl, settings.commit);
const objectFormat = objectFormatResult.succeeded
? objectFormatResult.format
: '';
if (objectFormat === 'sha256') {
core.info('Detected SHA-256 repository object format');
}
core.endGroup();
core.startGroup('Initializing the repository'); core.startGroup('Initializing the repository');
yield git.init(); yield git.init(objectFormat);
yield git.remoteAdd('origin', repositoryUrl); yield git.remoteAdd('origin', repositoryUrl);
core.endGroup(); core.endGroup();
} }
@ -1810,6 +1824,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.downloadRepository = downloadRepository; exports.downloadRepository = downloadRepository;
exports.getDefaultBranch = getDefaultBranch; exports.getDefaultBranch = getDefaultBranch;
exports.tryGetRepositoryObjectFormat = tryGetRepositoryObjectFormat;
const assert = __importStar(__nccwpck_require__(9491)); const assert = __importStar(__nccwpck_require__(9491));
const core = __importStar(__nccwpck_require__(2186)); const core = __importStar(__nccwpck_require__(2186));
const fs = __importStar(__nccwpck_require__(7147)); const fs = __importStar(__nccwpck_require__(7147));
@ -1911,6 +1926,40 @@ function getDefaultBranch(authToken, owner, repo, baseUrl) {
})); }));
}); });
} }
function tryGetRepositoryObjectFormat(authToken, owner, repo, baseUrl, commit) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const commitFormat = getObjectFormat(commit);
if (commitFormat) {
return { format: commitFormat, succeeded: true };
}
try {
const octokit = github.getOctokit(authToken, {
baseUrl: (0, url_helper_1.getServerApiUrl)(baseUrl)
});
const response = yield octokit.request('GET /repos/{owner}/{repo}/hash-algorithm', { owner, repo });
const hashAlgorithm = response.data.hash_algorithm;
if (hashAlgorithm === 'sha256' || hashAlgorithm === 'sha1') {
return { format: hashAlgorithm, succeeded: true };
}
core.debug('Unable to determine repository object format from hash-algorithm endpoint');
return { format: '', succeeded: false };
}
catch (err) {
core.debug(`Unable to determine repository object format from hash-algorithm endpoint: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
return { format: '', succeeded: false };
}
});
}
function getObjectFormat(sha) {
if (/^[0-9a-fA-F]{64}$/.test(sha || '')) {
return 'sha256';
}
if (/^[0-9a-fA-F]{40}$/.test(sha || '')) {
return 'sha1';
}
return '';
}
function downloadArchive(authToken, owner, repo, ref, commit, baseUrl) { function downloadArchive(authToken, owner, repo, ref, commit, baseUrl) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const octokit = github.getOctokit(authToken, { const octokit = github.getOctokit(authToken, {

View File

@ -43,7 +43,7 @@ export interface IGitCommandManager {
getDefaultBranch(repositoryUrl: string): Promise<string> getDefaultBranch(repositoryUrl: string): Promise<string>
getSubmoduleConfigPaths(recursive: boolean): Promise<string[]> getSubmoduleConfigPaths(recursive: boolean): Promise<string[]>
getWorkingDirectory(): string getWorkingDirectory(): string
init(): Promise<void> init(objectFormat?: string): Promise<void>
isDetached(): Promise<boolean> isDetached(): Promise<boolean>
lfsFetch(ref: string): Promise<void> lfsFetch(ref: string): Promise<void>
lfsInstall(): Promise<void> lfsInstall(): Promise<void>
@ -364,8 +364,14 @@ class GitCommandManager {
return this.workingDirectory return this.workingDirectory
} }
async init(): Promise<void> { async init(objectFormat?: string): Promise<void> {
await this.execGit(['init', this.workingDirectory]) const args = ['init']
if (objectFormat === 'sha256') {
args.push('--object-format=sha256')
}
args.push(this.workingDirectory)
await this.execGit(args)
} }
async isDetached(): Promise<boolean> { async isDetached(): Promise<boolean> {

View File

@ -109,8 +109,25 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
if ( if (
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git')) !fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
) { ) {
core.startGroup('Determining repository object format')
const objectFormatResult =
await githubApiHelper.tryGetRepositoryObjectFormat(
settings.authToken,
settings.repositoryOwner,
settings.repositoryName,
settings.githubServerUrl,
settings.commit
)
const objectFormat = objectFormatResult.succeeded
? objectFormatResult.format
: ''
if (objectFormat === 'sha256') {
core.info('Detected SHA-256 repository object format')
}
core.endGroup()
core.startGroup('Initializing the repository') core.startGroup('Initializing the repository')
await git.init() await git.init(objectFormat)
await git.remoteAdd('origin', repositoryUrl) await git.remoteAdd('origin', repositoryUrl)
core.endGroup() core.endGroup()
} }

View File

@ -11,6 +11,11 @@ import {getServerApiUrl} from './url-helper'
const IS_WINDOWS = process.platform === 'win32' const IS_WINDOWS = process.platform === 'win32'
export interface RepositoryObjectFormatResult {
format: string
succeeded: boolean
}
export async function downloadRepository( export async function downloadRepository(
authToken: string, authToken: string,
owner: string, owner: string,
@ -122,6 +127,53 @@ export async function getDefaultBranch(
}) })
} }
export async function tryGetRepositoryObjectFormat(
authToken: string,
owner: string,
repo: string,
baseUrl?: string,
commit?: string
): Promise<RepositoryObjectFormatResult> {
const commitFormat = getObjectFormat(commit)
if (commitFormat) {
return {format: commitFormat, succeeded: true}
}
try {
const octokit = github.getOctokit(authToken, {
baseUrl: getServerApiUrl(baseUrl)
})
const response = await octokit.request(
'GET /repos/{owner}/{repo}/hash-algorithm',
{owner, repo}
)
const hashAlgorithm = response.data.hash_algorithm
if (hashAlgorithm === 'sha256' || hashAlgorithm === 'sha1') {
return {format: hashAlgorithm, succeeded: true}
}
core.debug(
'Unable to determine repository object format from hash-algorithm endpoint'
)
return {format: '', succeeded: false}
} catch (err) {
core.debug(
`Unable to determine repository object format from hash-algorithm endpoint: ${(err as any)?.message ?? err}`
)
return {format: '', succeeded: false}
}
}
function getObjectFormat(sha?: string): string {
if (/^[0-9a-fA-F]{64}$/.test(sha || '')) {
return 'sha256'
}
if (/^[0-9a-fA-F]{40}$/.test(sha || '')) {
return 'sha1'
}
return ''
}
async function downloadArchive( async function downloadArchive(
authToken: string, authToken: string,
owner: string, owner: string,