Read-only mirror — the public repositories of the Forgejo instance, refreshed every 10 minutes. The live instance (fg.dieschoenewolke.de) is reachable only over the VPN; clone here with https://git.dieschoenewolke.de/git/<owner>/<repo>.git.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYashwanth Anantharaju <yaananth@users.noreply.github.com>2026-06-01 11:35:58 -0400
committerGitHub <noreply@github.com>2026-06-01 11:35:58 -0400
commiteb9dcad7e007cb96364b4e612799ce0ff736cb09 (patch)
tree2730243bfbc90a8bc802720dc13e2e7ce433e7f2
parent45c7dc79a6c1ac19a6a0981f08146cb419b30891 (diff)
Fix checkout init for SHA-256 repositories (#2439)
* Fix checkout init for SHA-256 repositories * Remove unused object format result field
-rw-r--r--__test__/git-command-manager.test.ts53
-rw-r--r--__test__/github-api-helper.test.ts98
-rw-r--r--dist/index.js55
-rw-r--r--src/git-command-manager.ts12
-rw-r--r--src/git-source-provider.ts19
-rw-r--r--src/github-api-helper.ts52
6 files changed, 282 insertions, 7 deletions
diff --git a/__test__/git-command-manager.test.ts b/__test__/git-command-manager.test.ts
index 8a97d82..47a0f8f 100644
--- a/__test__/git-command-manager.test.ts
+++ b/__test__/git-command-manager.test.ts
@@ -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', () => {
beforeEach(async () => {
jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn())
diff --git a/__test__/github-api-helper.test.ts b/__test__/github-api-helper.test.ts
new file mode 100644
index 0000000..6319e20
--- /dev/null
+++ b/__test__/github-api-helper.test.ts
@@ -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'
+ )
+ })
+})
diff --git a/dist/index.js b/dist/index.js
index 57729b2..906b59a 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -896,9 +896,14 @@ class GitCommandManager {
getWorkingDirectory() {
return this.workingDirectory;
}
- init() {
+ init(objectFormat) {
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() {
@@ -1486,8 +1491,17 @@ function getSource(settings) {
stateHelper.setRepositoryPath(settings.repositoryPath);
// Initialize the repository
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');
- yield git.init();
+ yield git.init(objectFormat);
yield git.remoteAdd('origin', repositoryUrl);
core.endGroup();
}
@@ -1810,6 +1824,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.downloadRepository = downloadRepository;
exports.getDefaultBranch = getDefaultBranch;
+exports.tryGetRepositoryObjectFormat = tryGetRepositoryObjectFormat;
const assert = __importStar(__nccwpck_require__(9491));
const core = __importStar(__nccwpck_require__(2186));
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) {
return __awaiter(this, void 0, void 0, function* () {
const octokit = github.getOctokit(authToken, {
diff --git a/src/git-command-manager.ts b/src/git-command-manager.ts
index f5ba40e..f1349ce 100644
--- a/src/git-command-manager.ts
+++ b/src/git-command-manager.ts
@@ -43,7 +43,7 @@ export interface IGitCommandManager {
getDefaultBranch(repositoryUrl: string): Promise<string>
getSubmoduleConfigPaths(recursive: boolean): Promise<string[]>
getWorkingDirectory(): string
- init(): Promise<void>
+ init(objectFormat?: string): Promise<void>
isDetached(): Promise<boolean>
lfsFetch(ref: string): Promise<void>
lfsInstall(): Promise<void>
@@ -364,8 +364,14 @@ class GitCommandManager {
return this.workingDirectory
}
- async init(): Promise<void> {
- await this.execGit(['init', this.workingDirectory])
+ async init(objectFormat?: string): Promise<void> {
+ const args = ['init']
+ if (objectFormat === 'sha256') {
+ args.push('--object-format=sha256')
+ }
+ args.push(this.workingDirectory)
+
+ await this.execGit(args)
}
async isDetached(): Promise<boolean> {
diff --git a/src/git-source-provider.ts b/src/git-source-provider.ts
index ec87178..452d44e 100644
--- a/src/git-source-provider.ts
+++ b/src/git-source-provider.ts
@@ -109,8 +109,25 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
if (
!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')
- await git.init()
+ await git.init(objectFormat)
await git.remoteAdd('origin', repositoryUrl)
core.endGroup()
}
diff --git a/src/github-api-helper.ts b/src/github-api-helper.ts
index 1ff27c2..bef31e6 100644
--- a/src/github-api-helper.ts
+++ b/src/github-api-helper.ts
@@ -11,6 +11,11 @@ import {getServerApiUrl} from './url-helper'
const IS_WINDOWS = process.platform === 'win32'
+export interface RepositoryObjectFormatResult {
+ format: string
+ succeeded: boolean
+}
+
export async function downloadRepository(
authToken: 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(
authToken: string,
owner: string,