Skip to content

Commit cf51dca

Browse files
authored
Merge pull request #3893 from github/henrymercer/sha256
Add support for SHA-256 Git object IDs
2 parents 67f4038 + 5b815f2 commit cf51dca

9 files changed

Lines changed: 123 additions & 123 deletions

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ See the [releases page](https://github.com/github/codeql-action/releases) for th
44

55
## [UNRELEASED]
66

7-
No user facing changes.
7+
- Add support for SHA-256 Git object IDs. [#3893](https://github.com/github/codeql-action/pull/3893)
88

99
## 4.35.5 - 15 May 2026
1010

lib/entry-points.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/upload-lib.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/diff-informed-analysis-utils.test.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ const testShouldPerformDiffInformedAnalysis = makeMacro({
7575
[Feature.DiffInformedQueries]: testCase.featureEnabled,
7676
});
7777

78-
const getGitHubVersionStub = sinon
78+
sinon
7979
.stub(apiClient, "getGitHubVersion")
8080
.resolves(testCase.gitHubVersion);
81-
const getPullRequestBranchesStub = sinon
81+
sinon
8282
.stub(actionsUtil, "getPullRequestBranches")
8383
.returns(testCase.pullRequestBranches);
8484

@@ -91,9 +91,6 @@ const testShouldPerformDiffInformedAnalysis = makeMacro({
9191
t.is(branches !== undefined, expectedResult);
9292

9393
delete process.env.CODEQL_ACTION_DIFF_INFORMED_QUERIES;
94-
95-
getGitHubVersionStub.restore();
96-
getPullRequestBranchesStub.restore();
9794
});
9895
},
9996
title: (title) => `getDiffInformedAnalysisBranches: ${title}`,

src/git-utils.test.ts

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ test.serial(
3333

3434
const actualRef = await gitUtils.getRef();
3535
t.deepEqual(actualRef, expectedRef);
36-
callback.restore();
3736
});
3837
},
3938
);
@@ -54,7 +53,6 @@ test.serial(
5453

5554
const actualRef = await gitUtils.getRef();
5655
t.deepEqual(actualRef, expectedRef);
57-
callback.restore();
5856
});
5957
},
6058
);
@@ -73,7 +71,6 @@ test.serial(
7371

7472
const actualRef = await gitUtils.getRef();
7573
t.deepEqual(actualRef, "refs/pull/1/head");
76-
callback.restore();
7774
});
7875
},
7976
);
@@ -100,8 +97,6 @@ test.serial(
10097

10198
const actualRef = await gitUtils.getRef();
10299
t.deepEqual(actualRef, "refs/pull/2/merge");
103-
callback.restore();
104-
getAdditionalInputStub.restore();
105100
});
106101
},
107102
);
@@ -161,7 +156,6 @@ test.serial(
161156
"Both 'ref' and 'sha' are required if one of them is provided.",
162157
},
163158
);
164-
getAdditionalInputStub.restore();
165159
});
166160
},
167161
);
@@ -188,7 +182,6 @@ test.serial(
188182
"Both 'ref' and 'sha' are required if one of them is provided.",
189183
},
190184
);
191-
getAdditionalInputStub.restore();
192185
});
193186
},
194187
);
@@ -242,7 +235,6 @@ test.serial("isAnalyzingDefaultBranch()", async (t) => {
242235
process.env["GITHUB_EVENT_NAME"] = "schedule";
243236
process.env["GITHUB_REF"] = "refs/heads/main";
244237
t.deepEqual(await gitUtils.isAnalyzingDefaultBranch(), false);
245-
getAdditionalInputStub.restore();
246238
});
247239
});
248240

@@ -254,8 +246,6 @@ test.serial("determineBaseBranchHeadCommitOid non-pullrequest", async (t) => {
254246
const result = await gitUtils.determineBaseBranchHeadCommitOid(__dirname);
255247
t.deepEqual(result, undefined);
256248
t.deepEqual(0, infoStub.callCount);
257-
258-
infoStub.restore();
259249
});
260250

261251
test.serial(
@@ -276,8 +266,6 @@ test.serial(
276266
"git call failed. Will calculate the base branch SHA on the server. Error: " +
277267
"The checkout path provided to the action does not appear to be a git repository.",
278268
);
279-
280-
infoStub.restore();
281269
},
282270
);
283271

@@ -301,10 +289,27 @@ test.serial("determineBaseBranchHeadCommitOid other error", async (t) => {
301289
"The checkout path provided to the action does not appear to be a git repository.",
302290
),
303291
);
304-
305-
infoStub.restore();
306292
});
307293

294+
test.serial(
295+
"determineBaseBranchHeadCommitOid accepts SHA-256 OIDs",
296+
async (t) => {
297+
const mergeSha = "a".repeat(64);
298+
const baseOid = "b".repeat(64);
299+
const headOid = "c".repeat(64);
300+
301+
process.env["GITHUB_EVENT_NAME"] = "pull_request";
302+
process.env["GITHUB_SHA"] = mergeSha;
303+
304+
sinon
305+
.stub(gitUtils as any, "runGitCommand")
306+
.resolves(`commit ${mergeSha}\nparent ${baseOid}\nparent ${headOid}\n`);
307+
308+
const result = await gitUtils.determineBaseBranchHeadCommitOid(__dirname);
309+
t.deepEqual(result, baseOid);
310+
},
311+
);
312+
308313
test.serial("decodeGitFilePath unquoted strings", async (t) => {
309314
t.deepEqual(gitUtils.decodeGitFilePath("foo"), "foo");
310315
t.deepEqual(gitUtils.decodeGitFilePath("foo bar"), "foo bar");
@@ -436,6 +441,64 @@ test.serial("getFileOidsUnderPath handles quoted paths", async (t) => {
436441
});
437442
});
438443

444+
test.serial("getFileOidsUnderPath handles SHA-256 OIDs", async (t) => {
445+
await withTmpDir(async (tmpDir) => {
446+
const sha256OidA =
447+
"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2c0d4b7e8f9a1234567890ab";
448+
const sha256OidB =
449+
"aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899";
450+
451+
sinon
452+
.stub(gitUtils as any, "runGitCommand")
453+
.callsFake(async (_cwd: any, args: any) => {
454+
if (args[0] === "rev-parse") {
455+
return `${tmpDir}\n`;
456+
}
457+
return (
458+
`100644 ${sha256OidA} 0\tlib/sha256-file-a.js\n` +
459+
`100644 ${sha256OidB} 0\tsrc/sha256-file-b.ts`
460+
);
461+
});
462+
463+
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
464+
465+
t.deepEqual(result, {
466+
"lib/sha256-file-a.js": sha256OidA,
467+
"src/sha256-file-b.ts": sha256OidB,
468+
});
469+
});
470+
});
471+
472+
test.serial(
473+
"getFileOidsUnderPath rejects OIDs of unsupported length",
474+
async (t) => {
475+
await withTmpDir(async (tmpDir) => {
476+
// 50-char OID: not a valid SHA-1 (40) or SHA-256 (64) length. The regex
477+
// must not accept this even though every character is a valid hex digit.
478+
const invalidLine =
479+
"100644 30d998ded095371488be3a729eb61d86ed721a1830d998ded0 0\tlib/bad.js";
480+
sinon
481+
.stub(gitUtils as any, "runGitCommand")
482+
.callsFake(async (_cwd: any, args: any) => {
483+
if (args[0] === "rev-parse") {
484+
return `${tmpDir}\n`;
485+
}
486+
return invalidLine;
487+
});
488+
489+
await t.throwsAsync(
490+
async () => {
491+
await gitUtils.getFileOidsUnderPath("/fake/path");
492+
},
493+
{
494+
instanceOf: Error,
495+
message: `Unexpected "git ls-files" output: ${invalidLine}`,
496+
},
497+
);
498+
});
499+
},
500+
);
501+
439502
test.serial("getFileOidsUnderPath handles empty output", async (t) => {
440503
await withTmpDir(async (tmpDir) => {
441504
sinon

src/git-utils.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,12 @@ export const determineBaseBranchHeadCommitOid = async function (
163163
}
164164
}
165165

166-
// Let's confirm our assumptions: We had a merge commit and the parsed parent data looks correct
166+
// Let's confirm our assumptions: We had a merge commit and the parsed parent
167+
// data looks correct. OIDs are either 40 (SHA-1) or 64 (SHA-256) hex characters.
167168
if (
168169
commitOid === mergeSha &&
169-
headOid.length === 40 &&
170-
baseOid.length === 40
170+
(headOid.length === 40 || headOid.length === 64) &&
171+
(baseOid.length === 40 || baseOid.length === 64)
171172
) {
172173
return baseOid;
173174
}
@@ -296,7 +297,8 @@ export const getFileOidsUnderPath = async function (
296297
// 100644 4c51bc1d9e86cd86e01b0f340cb8ce095c33b283 0\tsrc/git-utils.test.ts
297298
// 100644 6b792ea543ce75d7a8a03df591e3c85311ecb64f 0\tsrc/git-utils.ts
298299
// The fields are: <mode> <oid> <stage>\t<path>
299-
const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/;
300+
// The OID is either 40 (SHA-1) or 64 (SHA-256) hex characters.
301+
const regex = /^[0-9]+ ([0-9a-f]{40}|[0-9a-f]{64}) [0-9]+\t(.+)$/;
300302
for (const line of stdout.split("\n")) {
301303
if (line) {
302304
const match = line.match(regex);

src/overlay/caching.test.ts

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -80,65 +80,46 @@ const testDownloadOverlayBaseDatabaseFromCache = makeMacro({
8080
await fs.promises.writeFile(baseDatabaseOidsFile, JSON.stringify({}));
8181
}
8282

83-
const stubs: sinon.SinonStub[] = [];
83+
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
8484

85-
const getAutomationIDStub = sinon
86-
.stub(apiClient, "getAutomationID")
87-
.resolves("test-automation-id/");
88-
stubs.push(getAutomationIDStub);
89-
90-
const isInTestModeStub = sinon
91-
.stub(utils, "isInTestMode")
92-
.returns(testCase.isInTestMode);
93-
stubs.push(isInTestModeStub);
85+
sinon.stub(utils, "isInTestMode").returns(testCase.isInTestMode);
9486

9587
if (testCase.restoreCacheResult instanceof Error) {
96-
const restoreCacheStub = sinon
88+
sinon
9789
.stub(actionsCache, "restoreCache")
9890
.rejects(testCase.restoreCacheResult);
99-
stubs.push(restoreCacheStub);
10091
} else {
101-
const restoreCacheStub = sinon
92+
sinon
10293
.stub(actionsCache, "restoreCache")
10394
.resolves(testCase.restoreCacheResult);
104-
stubs.push(restoreCacheStub);
10595
}
10696

107-
const tryGetFolderBytesStub = sinon
97+
sinon
10898
.stub(utils, "tryGetFolderBytes")
10999
.resolves(testCase.tryGetFolderBytesSucceeds ? 1024 * 1024 : undefined);
110-
stubs.push(tryGetFolderBytesStub);
111100

112101
const codeql = mockCodeQLVersion(testCase.codeQLVersion);
113102

114103
if (testCase.resolveDatabaseOutput instanceof Error) {
115-
const resolveDatabaseStub = sinon
104+
sinon
116105
.stub(codeql, "resolveDatabase")
117106
.rejects(testCase.resolveDatabaseOutput);
118-
stubs.push(resolveDatabaseStub);
119107
} else {
120-
const resolveDatabaseStub = sinon
108+
sinon
121109
.stub(codeql, "resolveDatabase")
122110
.resolves(testCase.resolveDatabaseOutput);
123-
stubs.push(resolveDatabaseStub);
124111
}
125112

126-
try {
127-
const result = await downloadOverlayBaseDatabaseFromCache(
128-
codeql,
129-
config,
130-
logger,
131-
);
113+
const result = await downloadOverlayBaseDatabaseFromCache(
114+
codeql,
115+
config,
116+
logger,
117+
);
132118

133-
if (expectDownloadSuccess) {
134-
t.truthy(result);
135-
} else {
136-
t.is(result, undefined);
137-
}
138-
} finally {
139-
for (const stub of stubs) {
140-
stub.restore();
141-
}
119+
if (expectDownloadSuccess) {
120+
t.truthy(result);
121+
} else {
122+
t.is(result, undefined);
142123
}
143124
});
144125
},

0 commit comments

Comments
 (0)