Compare commits

..

2 Commits

Author SHA1 Message Date
Yashwanth Anantharaju 5a33ab7146
Merge 67bd696108 into 900f2210b1 2026-05-21 20:56:39 +00:00
Yashwanth Anantharaju 67bd696108 Fix checkout init for SHA-256 repositories 2026-05-21 16:56:34 -04:00
8 changed files with 455 additions and 51 deletions

View File

@ -1103,6 +1103,7 @@ async function setup(testName: string): Promise<void> {
), ),
tryDisableAutomaticGarbageCollection: jest.fn(), tryDisableAutomaticGarbageCollection: jest.fn(),
tryGetFetchUrl: jest.fn(), tryGetFetchUrl: jest.fn(),
tryGetObjectFormat: jest.fn(async () => ({format: '', succeeded: true})),
tryGetConfigValues: jest.fn( tryGetConfigValues: jest.fn(
async ( async (
key: string, key: string,

View File

@ -378,6 +378,169 @@ describe('Test fetchDepth and fetchTags options', () => {
}) })
}) })
describe('repository object format', () => {
beforeEach(async () => {
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn())
})
afterEach(() => {
jest.restoreAllMocks()
})
it('detects SHA-256 from a 64-character HEAD oid', async () => {
mockExec.mockImplementation((path, args, options) => {
if (args.includes('version')) {
options.listeners.stdout(Buffer.from('git version 2.50.1'))
}
if (args.includes('ls-remote')) {
options.listeners.stdout(
Buffer.from(
'ref: refs/heads/main\tHEAD\n' +
'9422233ca7ee1b17f1e905d0e141faf0c401556c41cdc6acd71c6bd685da2e92\tHEAD\n'
)
)
}
return 0
})
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
git = await commandManager.createCommandManager('test', false, false)
const objectFormat = await git.tryGetObjectFormat(
'https://github.com/example/repo'
)
expect(objectFormat).toEqual({format: 'sha256', succeeded: true})
expect(mockExec).toHaveBeenCalledWith(
expect.any(String),
[
'-c',
'protocol.version=2',
'ls-remote',
'--quiet',
'--exit-code',
'--symref',
'https://github.com/example/repo',
'HEAD'
],
expect.objectContaining({
ignoreReturnCode: true,
silent: true
})
)
})
it('detects SHA-1 from a 40-character HEAD oid', async () => {
mockExec.mockImplementation((path, args, options) => {
if (args.includes('version')) {
options.listeners.stdout(Buffer.from('git version 2.50.1'))
}
if (args.includes('ls-remote')) {
options.listeners.stdout(
Buffer.from(
'ref: refs/heads/main\tHEAD\n' +
'c988866043f035e6a46509872215f91d879044c9\tHEAD\n'
)
)
}
return 0
})
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
git = await commandManager.createCommandManager('test', false, false)
await expect(
git.tryGetObjectFormat('https://github.com/example/repo')
).resolves.toEqual({format: 'sha1', succeeded: true})
})
it('returns unsuccessful when HEAD does not resolve to a recognized object id', async () => {
mockExec.mockImplementation((path, args, options) => {
if (args.includes('version')) {
options.listeners.stdout(Buffer.from('git version 2.50.1'))
}
if (args.includes('ls-remote')) {
options.listeners.stdout(Buffer.from('ref: refs/heads/main\tHEAD\n'))
}
return 0
})
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
git = await commandManager.createCommandManager('test', false, false)
await expect(
git.tryGetObjectFormat('https://github.com/example/repo')
).resolves.toEqual({format: '', succeeded: false})
})
it('returns unsuccessful when object format detection cannot reach the remote', async () => {
mockExec.mockImplementation((path, args, options) => {
if (args.includes('version')) {
options.listeners.stdout(Buffer.from('git version 2.50.1'))
return 0
}
return 128
})
jest.spyOn(exec, 'exec').mockImplementation(mockExec)
git = await commandManager.createCommandManager('test', false, false)
await expect(
git.tryGetObjectFormat('https://github.com/example/repo')
).resolves.toEqual({format: '', succeeded: false})
})
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

@ -501,6 +501,7 @@ async function setup(testName: string): Promise<void> {
await fs.promises.stat(path.join(repositoryPath, '.git')) await fs.promises.stat(path.join(repositoryPath, '.git'))
return repositoryUrl return repositoryUrl
}), }),
tryGetObjectFormat: jest.fn(async () => ({format: '', succeeded: true})),
tryGetConfigValues: jest.fn(), tryGetConfigValues: jest.fn(),
tryGetConfigKeys: jest.fn(), tryGetConfigKeys: jest.fn(),
tryReset: jest.fn(async () => { tryReset: jest.fn(async () => {

View File

@ -5,16 +5,29 @@ import * as githubApiHelper from '../lib/github-api-helper'
describe('github-api-helper object format', () => { describe('github-api-helper object format', () => {
let getOctokitSpy: jest.SpyInstance let getOctokitSpy: jest.SpyInstance
let debugSpy: jest.SpyInstance let debugSpy: jest.SpyInstance
let request: jest.Mock let repoGet: jest.Mock
let branchGet: jest.Mock
function mockHashAlgorithmApi(hashAlgorithm: string): void { function mockObjectFormatApi(defaultBranch: string, commitSha: string): void {
request = jest.fn(async () => ({ repoGet = jest.fn(async () => ({
data: { data: {
hash_algorithm: hashAlgorithm default_branch: defaultBranch
}
}))
branchGet = jest.fn(async () => ({
data: {
commit: {
sha: commitSha
}
} }
})) }))
getOctokitSpy = jest.spyOn(github, 'getOctokit').mockReturnValue({ getOctokitSpy = jest.spyOn(github, 'getOctokit').mockReturnValue({
request rest: {
repos: {
get: repoGet,
getBranch: branchGet
}
}
} as any) } as any)
} }
@ -26,29 +39,37 @@ describe('github-api-helper object format', () => {
jest.restoreAllMocks() jest.restoreAllMocks()
}) })
it('detects SHA-256 from the repository hash algorithm endpoint', async () => { it('detects SHA-256 from the default branch commit SHA', async () => {
mockHashAlgorithmApi('sha256') const commitSha =
'9422233ca7ee1b17f1e905d0e141faf0c401556c41cdc6acd71c6bd685da2e92'
mockObjectFormatApi('main', commitSha)
await expect( await expect(
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo') githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
).resolves.toEqual({format: 'sha256', succeeded: true}) ).resolves.toEqual({
defaultBranch: 'main',
format: 'sha256',
succeeded: true
})
expect(getOctokitSpy).toHaveBeenCalledWith( expect(getOctokitSpy).toHaveBeenCalledWith(
'token', 'token',
expect.objectContaining({baseUrl: 'https://api.github.com'}) expect.objectContaining({baseUrl: 'https://api.github.com'})
) )
expect(request).toHaveBeenCalledWith( expect(repoGet).toHaveBeenCalledWith({owner: 'owner', repo: 'repo'})
'GET /repos/{owner}/{repo}/hash-algorithm', expect(branchGet).toHaveBeenCalledWith({
{owner: 'owner', repo: 'repo'} owner: 'owner',
) repo: 'repo',
branch: 'main'
})
}) })
it('detects SHA-1 from the repository hash algorithm endpoint', async () => { it('detects SHA-1 from the default branch commit SHA', async () => {
mockHashAlgorithmApi('sha1') mockObjectFormatApi('main', 'c988866043f035e6a46509872215f91d879044c9')
await expect( await expect(
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo') githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
).resolves.toEqual({format: 'sha1', succeeded: true}) ).resolves.toEqual({defaultBranch: 'main', format: 'sha1', succeeded: true})
}) })
it('detects object format from an existing commit without API calls', async () => { it('detects object format from an existing commit without API calls', async () => {
@ -62,6 +83,7 @@ describe('github-api-helper object format', () => {
'owner', 'owner',
'repo', 'repo',
undefined, undefined,
undefined,
commitSha commitSha
) )
).resolves.toEqual({format: 'sha256', succeeded: true}) ).resolves.toEqual({format: 'sha256', succeeded: true})
@ -69,30 +91,74 @@ describe('github-api-helper object format', () => {
expect(getOctokitSpy).not.toHaveBeenCalled() expect(getOctokitSpy).not.toHaveBeenCalled()
}) })
it('returns unsuccessful when the hash algorithm endpoint value is not recognized', async () => { it('uses a branch ref directly without looking up the default branch', async () => {
mockHashAlgorithmApi('unknown') const commitSha = 'c988866043f035e6a46509872215f91d879044c9'
repoGet = jest.fn()
branchGet = jest.fn(async () => ({
data: {
commit: {
sha: commitSha
}
}
}))
getOctokitSpy = jest.spyOn(github, 'getOctokit').mockReturnValue({
rest: {
repos: {
get: repoGet,
getBranch: branchGet
}
}
} as any)
await expect(
githubApiHelper.tryGetRepositoryObjectFormat(
'token',
'owner',
'repo',
undefined,
'refs/heads/feature'
)
).resolves.toEqual({format: 'sha1', succeeded: true})
expect(repoGet).not.toHaveBeenCalled()
expect(branchGet).toHaveBeenCalledWith({
owner: 'owner',
repo: 'repo',
branch: 'feature'
})
})
it('returns unsuccessful when the default branch commit SHA is not recognized', async () => {
mockObjectFormatApi('main', 'not-a-sha')
await expect( await expect(
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo') githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
).resolves.toEqual({format: '', succeeded: false}) ).resolves.toEqual({format: '', succeeded: false})
expect(debugSpy).toHaveBeenCalledWith( expect(debugSpy).toHaveBeenCalledWith(
'Unable to determine repository object format from hash-algorithm endpoint' 'Unable to determine repository object format from commit SHA'
) )
}) })
it('returns unsuccessful when the hash algorithm API lookup fails', async () => { it('returns unsuccessful when the repository API lookup fails', async () => {
request = jest.fn(async () => { repoGet = jest.fn(async () => {
throw new Error('not found') throw new Error('not found')
}) })
branchGet = jest.fn()
jest.spyOn(github, 'getOctokit').mockReturnValue({ jest.spyOn(github, 'getOctokit').mockReturnValue({
request rest: {
repos: {
get: repoGet,
getBranch: branchGet
}
}
} as any) } as any)
await expect( await expect(
githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo') githubApiHelper.tryGetRepositoryObjectFormat('token', 'owner', 'repo')
).resolves.toEqual({format: '', succeeded: false}) ).resolves.toEqual({format: '', succeeded: false})
expect(branchGet).not.toHaveBeenCalled()
expect(debugSpy).toHaveBeenCalledWith( expect(debugSpy).toHaveBeenCalledWith(
'Unable to determine repository object format from hash-algorithm endpoint: not found' 'Unable to determine repository object format: not found'
) )
}) })
}) })

95
dist/index.js vendored
View File

@ -1061,6 +1061,45 @@ class GitCommandManager {
return stdout; return stdout;
}); });
} }
tryGetObjectFormat(repositoryUrl) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
try {
const output = yield this.execGit([
'-c',
'protocol.version=2',
'ls-remote',
'--quiet',
'--exit-code',
'--symref',
repositoryUrl,
'HEAD'
], true, true);
if (output.exitCode !== 0) {
core.debug(`Unable to determine repository object format: git ls-remote exited with ${output.exitCode}`);
return { format: '', succeeded: false };
}
for (const line of output.stdout.trim().split('\n')) {
const [oid, ref] = line.split('\t');
if (ref !== 'HEAD') {
continue;
}
if (/^[0-9a-fA-F]{64}$/.test(oid)) {
return { format: 'sha256', succeeded: true };
}
if (/^[0-9a-fA-F]{40}$/.test(oid)) {
return { format: 'sha1', succeeded: true };
}
}
core.debug('Unable to determine repository object format from HEAD');
return { format: '', succeeded: false };
}
catch (err) {
core.debug(`Unable to determine repository object format: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
return { format: '', succeeded: false };
}
});
}
tryGetConfigValues(configKey, globalConfig, configFile) { tryGetConfigValues(configKey, globalConfig, configFile) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const args = ['config']; const args = ['config'];
@ -1489,13 +1528,18 @@ function getSource(settings) {
} }
// Save state for POST action // Save state for POST action
stateHelper.setRepositoryPath(settings.repositoryPath); stateHelper.setRepositoryPath(settings.repositoryPath);
let defaultBranch = '';
// 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'); core.startGroup('Determining repository object format');
const objectFormatResult = yield githubApiHelper.tryGetRepositoryObjectFormat(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.githubServerUrl, settings.commit); let objectFormatResult = yield githubApiHelper.tryGetRepositoryObjectFormat(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.githubServerUrl, settings.ref, settings.commit);
if (!objectFormatResult.succeeded) {
objectFormatResult = yield git.tryGetObjectFormat(repositoryUrl);
}
const objectFormat = objectFormatResult.succeeded const objectFormat = objectFormatResult.succeeded
? objectFormatResult.format ? objectFormatResult.format
: ''; : '';
defaultBranch = objectFormatResult.defaultBranch || '';
if (objectFormat === 'sha256') { if (objectFormat === 'sha256') {
core.info('Detected SHA-256 repository object format'); core.info('Detected SHA-256 repository object format');
} }
@ -1525,6 +1569,10 @@ function getSource(settings) {
if (settings.sshKey) { if (settings.sshKey) {
settings.ref = yield git.getDefaultBranch(repositoryUrl); settings.ref = yield git.getDefaultBranch(repositoryUrl);
} }
else if (defaultBranch) {
core.info(`Default branch '${defaultBranch}'`);
settings.ref = `refs/heads/${defaultBranch}`;
}
else { else {
settings.ref = yield githubApiHelper.getDefaultBranch(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.githubServerUrl); settings.ref = yield githubApiHelper.getDefaultBranch(settings.authToken, settings.repositoryOwner, settings.repositoryName, settings.githubServerUrl);
} }
@ -1926,31 +1974,60 @@ function getDefaultBranch(authToken, owner, repo, baseUrl) {
})); }));
}); });
} }
function tryGetRepositoryObjectFormat(authToken, owner, repo, baseUrl, commit) { function tryGetRepositoryObjectFormat(authToken, owner, repo, baseUrl, ref, commit) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
var _a; var _a;
try {
const commitFormat = getObjectFormat(commit); const commitFormat = getObjectFormat(commit);
if (commitFormat) { if (commitFormat) {
return { format: commitFormat, succeeded: true }; return { format: commitFormat, succeeded: true };
} }
try {
const octokit = github.getOctokit(authToken, { const octokit = github.getOctokit(authToken, {
baseUrl: (0, url_helper_1.getServerApiUrl)(baseUrl) baseUrl: (0, url_helper_1.getServerApiUrl)(baseUrl)
}); });
const response = yield octokit.request('GET /repos/{owner}/{repo}/hash-algorithm', { owner, repo }); let branchName = getBranchName(ref);
const hashAlgorithm = response.data.hash_algorithm; let defaultBranch = '';
if (hashAlgorithm === 'sha256' || hashAlgorithm === 'sha1') { if (!branchName) {
return { format: hashAlgorithm, succeeded: true }; const repository = yield octokit.rest.repos.get({ owner, repo });
defaultBranch = repository.data.default_branch;
assert.ok(defaultBranch, 'default_branch cannot be empty');
branchName = defaultBranch;
} }
core.debug('Unable to determine repository object format from hash-algorithm endpoint'); const branch = yield octokit.rest.repos.getBranch({
owner,
repo,
branch: branchName
});
const branchFormat = getObjectFormat(branch.data.commit.sha);
if (branchFormat) {
return {
defaultBranch: defaultBranch || undefined,
format: branchFormat,
succeeded: true
};
}
core.debug('Unable to determine repository object format from commit SHA');
return { format: '', succeeded: false }; return { format: '', succeeded: false };
} }
catch (err) { 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}`); core.debug(`Unable to determine repository object format: ${(_a = err === null || err === void 0 ? void 0 : err.message) !== null && _a !== void 0 ? _a : err}`);
return { format: '', succeeded: false }; return { format: '', succeeded: false };
} }
}); });
} }
function getBranchName(ref) {
if (!ref) {
return '';
}
const headsPrefix = 'refs/heads/';
if (ref.startsWith(headsPrefix)) {
return ref.substring(headsPrefix.length);
}
if (!ref.startsWith('refs/') && !getObjectFormat(ref)) {
return ref;
}
return '';
}
function getObjectFormat(sha) { function getObjectFormat(sha) {
if (/^[0-9a-fA-F]{64}$/.test(sha || '')) { if (/^[0-9a-fA-F]{64}$/.test(sha || '')) {
return 'sha256'; return 'sha256';

View File

@ -15,6 +15,11 @@ import {GitVersion} from './git-version'
export const MinimumGitVersion = new GitVersion('2.18') export const MinimumGitVersion = new GitVersion('2.18')
export const MinimumGitSparseCheckoutVersion = new GitVersion('2.28') export const MinimumGitSparseCheckoutVersion = new GitVersion('2.28')
export interface GitObjectFormatResult {
format: string
succeeded: boolean
}
export interface IGitCommandManager { export interface IGitCommandManager {
branchDelete(remote: boolean, branch: string): Promise<void> branchDelete(remote: boolean, branch: string): Promise<void>
branchExists(remote: boolean, pattern: string): Promise<boolean> branchExists(remote: boolean, pattern: string): Promise<boolean>
@ -68,6 +73,7 @@ export interface IGitCommandManager {
): Promise<boolean> ): Promise<boolean>
tryDisableAutomaticGarbageCollection(): Promise<boolean> tryDisableAutomaticGarbageCollection(): Promise<boolean>
tryGetFetchUrl(): Promise<string> tryGetFetchUrl(): Promise<string>
tryGetObjectFormat(repositoryUrl: string): Promise<GitObjectFormatResult>
tryGetConfigValues( tryGetConfigValues(
configKey: string, configKey: string,
globalConfig?: boolean, globalConfig?: boolean,
@ -542,6 +548,55 @@ class GitCommandManager {
return stdout return stdout
} }
async tryGetObjectFormat(
repositoryUrl: string
): Promise<GitObjectFormatResult> {
try {
const output = await this.execGit(
[
'-c',
'protocol.version=2',
'ls-remote',
'--quiet',
'--exit-code',
'--symref',
repositoryUrl,
'HEAD'
],
true,
true
)
if (output.exitCode !== 0) {
core.debug(
`Unable to determine repository object format: git ls-remote exited with ${output.exitCode}`
)
return {format: '', succeeded: false}
}
for (const line of output.stdout.trim().split('\n')) {
const [oid, ref] = line.split('\t')
if (ref !== 'HEAD') {
continue
}
if (/^[0-9a-fA-F]{64}$/.test(oid)) {
return {format: 'sha256', succeeded: true}
}
if (/^[0-9a-fA-F]{40}$/.test(oid)) {
return {format: 'sha1', succeeded: true}
}
}
core.debug('Unable to determine repository object format from HEAD')
return {format: '', succeeded: false}
} catch (err) {
core.debug(
`Unable to determine repository object format: ${(err as any)?.message ?? err}`
)
return {format: '', succeeded: false}
}
}
async tryGetConfigValues( async tryGetConfigValues(
configKey: string, configKey: string,
globalConfig?: boolean, globalConfig?: boolean,

View File

@ -105,22 +105,29 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
// Save state for POST action // Save state for POST action
stateHelper.setRepositoryPath(settings.repositoryPath) stateHelper.setRepositoryPath(settings.repositoryPath)
let defaultBranch = ''
// Initialize the repository // Initialize the repository
if ( if (
!fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git')) !fsHelper.directoryExistsSync(path.join(settings.repositoryPath, '.git'))
) { ) {
core.startGroup('Determining repository object format') core.startGroup('Determining repository object format')
const objectFormatResult = let objectFormatResult =
await githubApiHelper.tryGetRepositoryObjectFormat( await githubApiHelper.tryGetRepositoryObjectFormat(
settings.authToken, settings.authToken,
settings.repositoryOwner, settings.repositoryOwner,
settings.repositoryName, settings.repositoryName,
settings.githubServerUrl, settings.githubServerUrl,
settings.ref,
settings.commit settings.commit
) )
if (!objectFormatResult.succeeded) {
objectFormatResult = await git.tryGetObjectFormat(repositoryUrl)
}
const objectFormat = objectFormatResult.succeeded const objectFormat = objectFormatResult.succeeded
? objectFormatResult.format ? objectFormatResult.format
: '' : ''
defaultBranch = objectFormatResult.defaultBranch || ''
if (objectFormat === 'sha256') { if (objectFormat === 'sha256') {
core.info('Detected SHA-256 repository object format') core.info('Detected SHA-256 repository object format')
} }
@ -155,6 +162,9 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
core.startGroup('Determining the default branch') core.startGroup('Determining the default branch')
if (settings.sshKey) { if (settings.sshKey) {
settings.ref = await git.getDefaultBranch(repositoryUrl) settings.ref = await git.getDefaultBranch(repositoryUrl)
} else if (defaultBranch) {
core.info(`Default branch '${defaultBranch}'`)
settings.ref = `refs/heads/${defaultBranch}`
} else { } else {
settings.ref = await githubApiHelper.getDefaultBranch( settings.ref = await githubApiHelper.getDefaultBranch(
settings.authToken, settings.authToken,

View File

@ -133,38 +133,69 @@ export async function tryGetRepositoryObjectFormat(
owner: string, owner: string,
repo: string, repo: string,
baseUrl?: string, baseUrl?: string,
ref?: string,
commit?: string commit?: string
): Promise<RepositoryObjectFormatResult> { ): Promise<RepositoryObjectFormatResult> {
try {
const commitFormat = getObjectFormat(commit) const commitFormat = getObjectFormat(commit)
if (commitFormat) { if (commitFormat) {
return {format: commitFormat, succeeded: true} return {format: commitFormat, succeeded: true}
} }
try {
const octokit = github.getOctokit(authToken, { const octokit = github.getOctokit(authToken, {
baseUrl: getServerApiUrl(baseUrl) baseUrl: getServerApiUrl(baseUrl)
}) })
const response = await octokit.request(
'GET /repos/{owner}/{repo}/hash-algorithm', let branchName = getBranchName(ref)
{owner, repo} let defaultBranch = ''
) if (!branchName) {
const hashAlgorithm = response.data.hash_algorithm const repository = await octokit.rest.repos.get({owner, repo})
if (hashAlgorithm === 'sha256' || hashAlgorithm === 'sha1') { defaultBranch = repository.data.default_branch
return {format: hashAlgorithm, succeeded: true} assert.ok(defaultBranch, 'default_branch cannot be empty')
branchName = defaultBranch
} }
core.debug( const branch = await octokit.rest.repos.getBranch({
'Unable to determine repository object format from hash-algorithm endpoint' owner,
) repo,
branch: branchName
})
const branchFormat = getObjectFormat(branch.data.commit.sha)
if (branchFormat) {
return {
defaultBranch: defaultBranch || undefined,
format: branchFormat,
succeeded: true
}
}
core.debug('Unable to determine repository object format from commit SHA')
return {format: '', succeeded: false} return {format: '', succeeded: false}
} catch (err) { } catch (err) {
core.debug( core.debug(
`Unable to determine repository object format from hash-algorithm endpoint: ${(err as any)?.message ?? err}` `Unable to determine repository object format: ${(err as any)?.message ?? err}`
) )
return {format: '', succeeded: false} return {format: '', succeeded: false}
} }
} }
function getBranchName(ref?: string): string {
if (!ref) {
return ''
}
const headsPrefix = 'refs/heads/'
if (ref.startsWith(headsPrefix)) {
return ref.substring(headsPrefix.length)
}
if (!ref.startsWith('refs/') && !getObjectFormat(ref)) {
return ref
}
return ''
}
function getObjectFormat(sha?: string): string { function getObjectFormat(sha?: string): string {
if (/^[0-9a-fA-F]{64}$/.test(sha || '')) { if (/^[0-9a-fA-F]{64}$/.test(sha || '')) {
return 'sha256' return 'sha256'