Compare commits

..

23 Commits

Author SHA1 Message Date
Henry Mercer
a2c3c8e3e2 Bump log level for failing to parse git version 2025-12-17 17:28:13 +00:00
Henry Mercer
a13b404670 Record both truncated and full git versions 2025-12-17 17:27:14 +00:00
Henry Mercer
056581e05b Update makeTelemetryDiagnostic doc 2025-12-17 12:15:37 +00:00
Henry Mercer
9c5588d006 Remove unnecessary stub restores 2025-12-17 12:12:04 +00:00
Henry Mercer
3765106c90 Move git version logging to config utils 2025-12-17 12:06:41 +00:00
Henry Mercer
e052dbd57d Remove caching mechanism 2025-12-17 11:56:23 +00:00
Henry Mercer
32795b3c52 Merge branch 'main' into copilot/update-overlay-git-version-check 2025-12-17 11:49:32 +00:00
Henry Mercer
b88acb2f6c Merge pull request #3359 from github/dependabot/npm_and_yarn/npm-minor-b2e0062778
Bump the npm-minor group with 3 updates
2025-12-17 11:04:55 +00:00
Henry Mercer
241948c698 Merge branch 'main' into dependabot/npm_and_yarn/npm-minor-b2e0062778 2025-12-17 10:38:55 +00:00
Henry Mercer
1fe89fe9cb Merge pull request #3368 from github/copilot/bump-actions-npm-packages
Bump @actions/* npm packages to latest versions
2025-12-17 09:59:27 +00:00
Henry Mercer
6dba00881c Merge pull request #3372 from github/mergeback/v4.31.9-to-main-5d4e8d1a
Mergeback v4.31.9 refs/heads/releases/v4 into main
2025-12-16 19:33:12 +00:00
github-actions[bot]
d4d47c0d3d Rebuild 2025-12-16 18:56:12 +00:00
github-actions[bot]
6c6e810910 Update changelog and version after v4.31.9 2025-12-16 18:32:18 +00:00
copilot-swe-agent[bot]
393c074965 Refactor existing telemetry diagnostics to use makeTelemetryDiagnostic
Refactored bundle-download-telemetry and zstd-availability diagnostics
in init-action.ts to use the new makeTelemetryDiagnostic helper function.
Also added guard for empty languages array in logGitVersionTelemetry.

Co-authored-by: henrymercer <14129055+henrymercer@users.noreply.github.com>
2025-12-16 17:24:57 +00:00
copilot-swe-agent[bot]
c3dc529aef Address feedback: cache git version, improve error handling, add telemetry
- Cache the git version to avoid recomputing on repeated calls
- Refactor getGitVersion to getGitVersionOrThrow with detailed errors
- Add getGitVersion that logs errors and handles caching
- Add makeTelemetryDiagnostic helper to diagnostics.ts
- Add logGitVersionTelemetry function to log git version telemetry
- Call logGitVersionTelemetry in init-action.ts
- Add resetCachedGitVersion for testing
- Update tests to work with new function signatures and caching

Co-authored-by: henrymercer <14129055+henrymercer@users.noreply.github.com>
2025-12-16 17:19:46 +00:00
copilot-swe-agent[bot]
fc2bbb041e Address code review feedback
- Add test for Windows-style git version format
- Add comment clarifying regex extracts major.minor.patch
- Replace dynamic import with static import for semver

Co-authored-by: henrymercer <14129055+henrymercer@users.noreply.github.com>
2025-12-16 16:27:41 +00:00
copilot-swe-agent[bot]
89753aa84b Add git version check for overlay analysis enablement
Overlay analysis depends on `getFileOidsUnderPath`, which uses
`git ls-files --format` option that requires Git 2.38.0+. This
change adds a check for the git version before enabling overlay
analysis.

Co-authored-by: henrymercer <14129055+henrymercer@users.noreply.github.com>
2025-12-16 16:22:23 +00:00
copilot-swe-agent[bot]
aff7998c4a Initial plan 2025-12-16 16:09:09 +00:00
Henry Mercer
7a5748cf0d Remove changelog note 2025-12-16 13:41:13 +00:00
copilot-swe-agent[bot]
db75d46248 Bump @actions/* npm packages to latest versions
Co-authored-by: henrymercer <14129055+henrymercer@users.noreply.github.com>
2025-12-16 13:34:51 +00:00
copilot-swe-agent[bot]
a0fc644617 Initial plan 2025-12-16 13:29:18 +00:00
github-actions[bot]
e1058e4d74 Rebuild 2025-12-15 17:03:33 +00:00
dependabot[bot]
d4f39b0766 Bump the npm-minor group with 3 updates
Bumps the npm-minor group with 3 updates: [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js), [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@eslint/js` from 9.39.1 to 9.39.2
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/commits/v9.39.2/packages/js)

Updates `@typescript-eslint/eslint-plugin` from 8.48.1 to 8.49.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.49.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.48.1 to 8.49.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.49.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@eslint/js"
  dependency-version: 9.39.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm-minor
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.49.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.49.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 17:01:55 +00:00
21 changed files with 272900 additions and 191340 deletions

View File

@@ -2,6 +2,10 @@
See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs. See the [releases page](https://github.com/github/codeql-action/releases) for the relevant changes to the CodeQL CLI and language packs.
## [UNRELEASED]
No user facing changes.
## 4.31.9 - 16 Dec 2025 ## 4.31.9 - 16 Dec 2025
No user facing changes. No user facing changes.

42654
lib/analyze-action-post.js generated

File diff suppressed because it is too large Load Diff

37486
lib/analyze-action.js generated

File diff suppressed because it is too large Load Diff

36151
lib/autobuild-action.js generated

File diff suppressed because it is too large Load Diff

42716
lib/init-action-post.js generated

File diff suppressed because it is too large Load Diff

37595
lib/init-action.js generated

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

36143
lib/setup-codeql-action.js generated

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

36478
lib/start-proxy-action.js generated

File diff suppressed because it is too large Load Diff

36187
lib/upload-lib.js generated

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

36161
lib/upload-sarif-action.js generated

File diff suppressed because it is too large Load Diff

984
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "codeql", "name": "codeql",
"version": "4.31.9", "version": "4.31.10",
"private": true, "private": true,
"description": "CodeQL action", "description": "CodeQL action",
"scripts": { "scripts": {
@@ -24,12 +24,12 @@
}, },
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/artifact": "^4.0.0", "@actions/artifact": "^5.0.1",
"@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2", "@actions/artifact-legacy": "npm:@actions/artifact@^1.1.2",
"@actions/cache": "^4.1.0", "@actions/cache": "^5.0.1",
"@actions/core": "^1.11.1", "@actions/core": "^2.0.1",
"@actions/exec": "^1.1.1", "@actions/exec": "^2.0.0",
"@actions/github": "^6.0.0", "@actions/github": "^6.0.1",
"@actions/glob": "^0.5.0", "@actions/glob": "^0.5.0",
"@actions/http-client": "^3.0.0", "@actions/http-client": "^3.0.0",
"@actions/io": "^2.0.0", "@actions/io": "^2.0.0",
@@ -51,7 +51,7 @@
"@ava/typescript": "6.0.0", "@ava/typescript": "6.0.0",
"@eslint/compat": "^2.0.0", "@eslint/compat": "^2.0.0",
"@eslint/eslintrc": "^3.3.3", "@eslint/eslintrc": "^3.3.3",
"@eslint/js": "^9.39.1", "@eslint/js": "^9.39.2",
"@microsoft/eslint-formatter-sarif": "^3.1.0", "@microsoft/eslint-formatter-sarif": "^3.1.0",
"@octokit/types": "^16.0.0", "@octokit/types": "^16.0.0",
"@types/archiver": "^7.0.0", "@types/archiver": "^7.0.0",
@@ -61,7 +61,7 @@
"@types/node-forge": "^1.3.14", "@types/node-forge": "^1.3.14",
"@types/semver": "^7.7.1", "@types/semver": "^7.7.1",
"@types/sinon": "^21.0.0", "@types/sinon": "^21.0.0",
"@typescript-eslint/eslint-plugin": "^8.48.1", "@typescript-eslint/eslint-plugin": "^8.49.0",
"@typescript-eslint/parser": "^8.48.0", "@typescript-eslint/parser": "^8.48.0",
"ava": "^6.4.1", "ava": "^6.4.1",
"esbuild": "^0.27.1", "esbuild": "^0.27.1",

View File

@@ -15,6 +15,7 @@ import * as configUtils from "./config-utils";
import * as errorMessages from "./error-messages"; import * as errorMessages from "./error-messages";
import { Feature } from "./feature-flags"; import { Feature } from "./feature-flags";
import * as gitUtils from "./git-utils"; import * as gitUtils from "./git-utils";
import { GitVersionInfo } from "./git-utils";
import { KnownLanguage, Language } from "./languages"; import { KnownLanguage, Language } from "./languages";
import { getRunnerLogger } from "./logging"; import { getRunnerLogger } from "./logging";
import { import {
@@ -978,6 +979,7 @@ interface OverlayDatabaseModeTestSetup {
languages: Language[]; languages: Language[];
codeqlVersion: string; codeqlVersion: string;
gitRoot: string | undefined; gitRoot: string | undefined;
gitVersion: GitVersionInfo | undefined;
codeScanningConfig: configUtils.UserConfig; codeScanningConfig: configUtils.UserConfig;
diskUsage: DiskUsage | undefined; diskUsage: DiskUsage | undefined;
memoryFlagValue: number; memoryFlagValue: number;
@@ -992,6 +994,10 @@ const defaultOverlayDatabaseModeTestSetup: OverlayDatabaseModeTestSetup = {
languages: [KnownLanguage.javascript], languages: [KnownLanguage.javascript],
codeqlVersion: CODEQL_OVERLAY_MINIMUM_VERSION, codeqlVersion: CODEQL_OVERLAY_MINIMUM_VERSION,
gitRoot: "/some/git/root", gitRoot: "/some/git/root",
gitVersion: new GitVersionInfo(
gitUtils.GIT_MINIMUM_VERSION_FOR_OVERLAY,
gitUtils.GIT_MINIMUM_VERSION_FOR_OVERLAY,
),
codeScanningConfig: {}, codeScanningConfig: {},
diskUsage: { diskUsage: {
numAvailableBytes: 50_000_000_000, numAvailableBytes: 50_000_000_000,
@@ -1070,6 +1076,7 @@ const getOverlayDatabaseModeMacro = test.macro({
setup.buildMode, setup.buildMode,
undefined, undefined,
setup.codeScanningConfig, setup.codeScanningConfig,
setup.gitVersion,
logger, logger,
); );
@@ -1773,6 +1780,32 @@ test(
}, },
); );
test(
getOverlayDatabaseModeMacro,
"Fallback due to old git version",
{
overlayDatabaseEnvVar: "overlay",
gitVersion: new GitVersionInfo("2.30.0", "2.30.0"), // Version below required 2.38.0
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
},
);
test(
getOverlayDatabaseModeMacro,
"Fallback when git version cannot be determined",
{
overlayDatabaseEnvVar: "overlay",
gitVersion: undefined,
},
{
overlayDatabaseMode: OverlayDatabaseMode.None,
useOverlayDatabaseCaching: false,
},
);
// Exercise language-specific overlay analysis features code paths // Exercise language-specific overlay analysis features code paths
for (const language in KnownLanguage) { for (const language in KnownLanguage) {
test( test(

View File

@@ -22,11 +22,18 @@ import {
parseUserConfig, parseUserConfig,
UserConfig, UserConfig,
} from "./config/db-config"; } from "./config/db-config";
import { addDiagnostic, makeTelemetryDiagnostic } from "./diagnostics";
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils"; import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
import * as errorMessages from "./error-messages"; import * as errorMessages from "./error-messages";
import { Feature, FeatureEnablement } from "./feature-flags"; import { Feature, FeatureEnablement } from "./feature-flags";
import { RepositoryProperties } from "./feature-flags/properties"; import { RepositoryProperties } from "./feature-flags/properties";
import { getGitRoot, isAnalyzingDefaultBranch } from "./git-utils"; import {
getGitRoot,
getGitVersionOrThrow,
GIT_MINIMUM_VERSION_FOR_OVERLAY,
GitVersionInfo,
isAnalyzingDefaultBranch,
} from "./git-utils";
import { KnownLanguage, Language } from "./languages"; import { KnownLanguage, Language } from "./languages";
import { Logger } from "./logging"; import { Logger } from "./logging";
import { import {
@@ -45,6 +52,7 @@ import {
isDefined, isDefined,
checkDiskUsage, checkDiskUsage,
getCodeQLMemoryLimit, getCodeQLMemoryLimit,
getErrorMessage,
} from "./util"; } from "./util";
export * from "./config/db-config"; export * from "./config/db-config";
@@ -709,6 +717,7 @@ export async function getOverlayDatabaseMode(
buildMode: BuildMode | undefined, buildMode: BuildMode | undefined,
ramInput: string | undefined, ramInput: string | undefined,
codeScanningConfig: UserConfig, codeScanningConfig: UserConfig,
gitVersion: GitVersionInfo | undefined,
logger: Logger, logger: Logger,
): Promise<{ ): Promise<{
overlayDatabaseMode: OverlayDatabaseMode; overlayDatabaseMode: OverlayDatabaseMode;
@@ -811,6 +820,22 @@ export async function getOverlayDatabaseMode(
); );
return nonOverlayAnalysis; return nonOverlayAnalysis;
} }
if (gitVersion === undefined) {
logger.warning(
`Cannot build an ${overlayDatabaseMode} database because ` +
"the Git version could not be determined. " +
"Falling back to creating a normal full database instead.",
);
return nonOverlayAnalysis;
}
if (!gitVersion.isAtLeast(GIT_MINIMUM_VERSION_FOR_OVERLAY)) {
logger.warning(
`Cannot build an ${overlayDatabaseMode} database because ` +
`the installed Git version is older than ${GIT_MINIMUM_VERSION_FOR_OVERLAY}. ` +
"Falling back to creating a normal full database instead.",
);
return nonOverlayAnalysis;
}
return { return {
overlayDatabaseMode, overlayDatabaseMode,
@@ -903,6 +928,15 @@ export async function initConfig(
config.computedConfig["query-filters"] = []; config.computedConfig["query-filters"] = [];
} }
let gitVersion: GitVersionInfo | undefined = undefined;
try {
gitVersion = await getGitVersionOrThrow();
logger.info(`Using Git version ${gitVersion.fullVersion}`);
await logGitVersionTelemetry(config, gitVersion);
} catch (e) {
logger.warning(`Could not determine Git version: ${getErrorMessage(e)}`);
}
// The choice of overlay database mode depends on the selection of languages // The choice of overlay database mode depends on the selection of languages
// and queries, which in turn depends on the user config and the augmentation // and queries, which in turn depends on the user config and the augmentation
// properties. So we need to calculate the overlay database mode after the // properties. So we need to calculate the overlay database mode after the
@@ -916,6 +950,7 @@ export async function initConfig(
config.buildMode, config.buildMode,
inputs.ramInput, inputs.ramInput,
config.computedConfig, config.computedConfig,
gitVersion,
logger, logger,
); );
logger.info( logger.info(
@@ -1316,3 +1351,26 @@ export function getPrimaryAnalysisConfig(config: Config): AnalysisConfig {
? CodeScanning ? CodeScanning
: CodeQuality; : CodeQuality;
} }
/** Logs the Git version as a telemetry diagnostic. */
async function logGitVersionTelemetry(
config: Config,
gitVersion: GitVersionInfo,
): Promise<void> {
if (config.languages.length > 0) {
addDiagnostic(
config,
// Arbitrarily choose the first language. We could also choose all languages, but that
// increases the risk of misinterpreting the data.
config.languages[0],
makeTelemetryDiagnostic(
"codeql-action/git-version-telemetry",
"Git version telemetry",
{
fullVersion: gitVersion.fullVersion,
truncatedVersion: gitVersion.truncatedVersion,
},
),
);
}
}

View File

@@ -185,3 +185,27 @@ export function flushDiagnostics(config: Config) {
// Reset the unwritten diagnostics array. // Reset the unwritten diagnostics array.
unwrittenDiagnostics = []; unwrittenDiagnostics = [];
} }
/**
* Creates a telemetry-only diagnostic message. This is a convenience function
* for creating diagnostics that should only be sent to telemetry and not
* displayed on the status page or CLI summary table.
*
* @param id An identifier under which it makes sense to group this diagnostic message
* @param name Display name
* @param attributes Structured metadata
*/
export function makeTelemetryDiagnostic(
id: string,
name: string,
attributes: { [key: string]: any },
): DiagnosticMessage {
return makeDiagnostic(id, name, {
attributes,
visibility: {
cliSummaryTable: false,
statusPage: false,
telemetry: true,
},
});
}

View File

@@ -315,27 +315,23 @@ test("getFileOidsUnderPath returns correct file mapping", async (t) => {
"a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_src/git-utils.ts", "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_src/git-utils.ts",
); );
try { const result = await gitUtils.getFileOidsUnderPath("/fake/path");
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
t.deepEqual(result, { t.deepEqual(result, {
"lib/git-utils.js": "30d998ded095371488be3a729eb61d86ed721a18", "lib/git-utils.js": "30d998ded095371488be3a729eb61d86ed721a18",
"lib/git-utils.js.map": "d89514599a9a99f22b4085766d40af7b99974827", "lib/git-utils.js.map": "d89514599a9a99f22b4085766d40af7b99974827",
"src/git-utils.ts": "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96", "src/git-utils.ts": "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96",
}); });
t.deepEqual(runGitCommandStub.firstCall.args, [ t.deepEqual(runGitCommandStub.firstCall.args, [
"/fake/path", "/fake/path",
["ls-files", "--recurse-submodules", "--format=%(objectname)_%(path)"], ["ls-files", "--recurse-submodules", "--format=%(objectname)_%(path)"],
"Cannot list Git OIDs of tracked files.", "Cannot list Git OIDs of tracked files.",
]); ]);
} finally {
runGitCommandStub.restore();
}
}); });
test("getFileOidsUnderPath handles quoted paths", async (t) => { test("getFileOidsUnderPath handles quoted paths", async (t) => {
const runGitCommandStub = sinon sinon
.stub(gitUtils as any, "runGitCommand") .stub(gitUtils as any, "runGitCommand")
.resolves( .resolves(
"30d998ded095371488be3a729eb61d86ed721a18_lib/normal-file.js\n" + "30d998ded095371488be3a729eb61d86ed721a18_lib/normal-file.js\n" +
@@ -343,34 +339,24 @@ test("getFileOidsUnderPath handles quoted paths", async (t) => {
'a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_"lib/file\\twith\\ttabs.js"', 'a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_"lib/file\\twith\\ttabs.js"',
); );
try { const result = await gitUtils.getFileOidsUnderPath("/fake/path");
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
t.deepEqual(result, { t.deepEqual(result, {
"lib/normal-file.js": "30d998ded095371488be3a729eb61d86ed721a18", "lib/normal-file.js": "30d998ded095371488be3a729eb61d86ed721a18",
"lib/file with spaces.js": "d89514599a9a99f22b4085766d40af7b99974827", "lib/file with spaces.js": "d89514599a9a99f22b4085766d40af7b99974827",
"lib/file\twith\ttabs.js": "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96", "lib/file\twith\ttabs.js": "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96",
}); });
} finally {
runGitCommandStub.restore();
}
}); });
test("getFileOidsUnderPath handles empty output", async (t) => { test("getFileOidsUnderPath handles empty output", async (t) => {
const runGitCommandStub = sinon sinon.stub(gitUtils as any, "runGitCommand").resolves("");
.stub(gitUtils as any, "runGitCommand")
.resolves("");
try { const result = await gitUtils.getFileOidsUnderPath("/fake/path");
const result = await gitUtils.getFileOidsUnderPath("/fake/path"); t.deepEqual(result, {});
t.deepEqual(result, {});
} finally {
runGitCommandStub.restore();
}
}); });
test("getFileOidsUnderPath throws on unexpected output format", async (t) => { test("getFileOidsUnderPath throws on unexpected output format", async (t) => {
const runGitCommandStub = sinon sinon
.stub(gitUtils as any, "runGitCommand") .stub(gitUtils as any, "runGitCommand")
.resolves( .resolves(
"30d998ded095371488be3a729eb61d86ed721a18_lib/git-utils.js\n" + "30d998ded095371488be3a729eb61d86ed721a18_lib/git-utils.js\n" +
@@ -378,17 +364,71 @@ test("getFileOidsUnderPath throws on unexpected output format", async (t) => {
"a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_src/git-utils.ts", "a47c11f5bfdca7661942d2c8f1b7209fb0dfdf96_src/git-utils.ts",
); );
try { await t.throwsAsync(
await t.throwsAsync( async () => {
async () => { await gitUtils.getFileOidsUnderPath("/fake/path");
await gitUtils.getFileOidsUnderPath("/fake/path"); },
}, {
{ instanceOf: Error,
instanceOf: Error, message: 'Unexpected "git ls-files" output: invalid-line-format',
message: 'Unexpected "git ls-files" output: invalid-line-format', },
}, );
); });
} finally {
runGitCommandStub.restore(); test("getGitVersionOrThrow returns version for valid git output", async (t) => {
} sinon.stub(gitUtils as any, "runGitCommand").resolves("git version 2.40.0");
const version = await gitUtils.getGitVersionOrThrow();
t.is(version.truncatedVersion, "2.40.0");
t.is(version.fullVersion, "2.40.0");
});
test("getGitVersionOrThrow throws for invalid git output", async (t) => {
sinon.stub(gitUtils as any, "runGitCommand").resolves("invalid output");
await t.throwsAsync(
async () => {
await gitUtils.getGitVersionOrThrow();
},
{
instanceOf: Error,
message: "Could not parse Git version from output: invalid output",
},
);
});
test("getGitVersionOrThrow handles Windows-style git output", async (t) => {
sinon
.stub(gitUtils as any, "runGitCommand")
.resolves("git version 2.40.0.windows.1");
const version = await gitUtils.getGitVersionOrThrow();
// Should extract just the major.minor.patch portion
t.is(version.truncatedVersion, "2.40.0");
t.is(version.fullVersion, "2.40.0.windows.1");
});
test("getGitVersionOrThrow throws when git command fails", async (t) => {
sinon
.stub(gitUtils as any, "runGitCommand")
.rejects(new Error("git not found"));
await t.throwsAsync(
async () => {
await gitUtils.getGitVersionOrThrow();
},
{
instanceOf: Error,
message: "git not found",
},
);
});
test("GitVersionInfo.isAtLeast correctly compares versions", async (t) => {
const version = new gitUtils.GitVersionInfo("2.40.0", "2.40.0");
t.true(version.isAtLeast("2.38.0"));
t.true(version.isAtLeast("2.40.0"));
t.false(version.isAtLeast("2.41.0"));
t.false(version.isAtLeast("3.0.0"));
}); });

View File

@@ -1,6 +1,7 @@
import * as core from "@actions/core"; import * as core from "@actions/core";
import * as toolrunner from "@actions/exec/lib/toolrunner"; import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as io from "@actions/io"; import * as io from "@actions/io";
import * as semver from "semver";
import { import {
getOptionalInput, getOptionalInput,
@@ -9,6 +10,55 @@ import {
} from "./actions-util"; } from "./actions-util";
import { ConfigurationError, getRequiredEnvParam } from "./util"; import { ConfigurationError, getRequiredEnvParam } from "./util";
/**
* Minimum Git version required for overlay analysis. The `git ls-files --format`
* option, which is used by `getFileOidsUnderPath`, was introduced in Git 2.38.0.
*/
export const GIT_MINIMUM_VERSION_FOR_OVERLAY = "2.38.0";
/**
* Git version information
*
* The full version string as reported by `git --version` may not be
* semver-compatible (e.g., "2.40.0.windows.1"). This class captures both
* the full version string and a truncated semver-compatible version string
* (e.g., "2.40.0").
*/
export class GitVersionInfo {
constructor(
/** Truncated semver-compatible version */
public truncatedVersion: string,
/** Full version string as reported by `git --version` */
public fullVersion: string,
) {}
isAtLeast(minVersion: string): boolean {
return semver.gte(this.truncatedVersion, minVersion);
}
}
/**
* Gets the version of Git installed on the system and throws an error if
* the version cannot be determined.
*
* @returns The Git version string (e.g., "2.40.0").
* @throws {Error} if the version could not be determined.
*/
export async function getGitVersionOrThrow(): Promise<GitVersionInfo> {
const stdout = await runGitCommand(
undefined,
["--version"],
"Failed to get git version.",
);
// Git version output can vary: "git version 2.40.0" or "git version 2.40.0.windows.1"
// We capture just the major.minor.patch portion to ensure semver compatibility.
const match = stdout.match(/^git version ((\d+\.\d+\.\d+).*)$/);
if (match?.[1]) {
return new GitVersionInfo(match[2], match[1]);
}
throw new Error(`Could not parse Git version from output: ${stdout.trim()}`);
}
export const runGitCommand = async function ( export const runGitCommand = async function (
workingDirectory: string | undefined, workingDirectory: string | undefined,
args: string[], args: string[],

View File

@@ -33,6 +33,7 @@ import {
flushDiagnostics, flushDiagnostics,
logUnwrittenDiagnostics, logUnwrittenDiagnostics,
makeDiagnostic, makeDiagnostic,
makeTelemetryDiagnostic,
} from "./diagnostics"; } from "./diagnostics";
import { EnvVar } from "./environment"; import { EnvVar } from "./environment";
import { Feature, Features } from "./feature-flags"; import { Feature, Features } from "./feature-flags";
@@ -425,17 +426,10 @@ async function run() {
// Arbitrarily choose the first language. We could also choose all languages, but that // Arbitrarily choose the first language. We could also choose all languages, but that
// increases the risk of misinterpreting the data. // increases the risk of misinterpreting the data.
config.languages[0], config.languages[0],
makeDiagnostic( makeTelemetryDiagnostic(
"codeql-action/bundle-download-telemetry", "codeql-action/bundle-download-telemetry",
"CodeQL bundle download telemetry", "CodeQL bundle download telemetry",
{ toolsDownloadStatusReport,
attributes: toolsDownloadStatusReport,
visibility: {
cliSummaryTable: false,
statusPage: false,
telemetry: true,
},
},
), ),
); );
} }
@@ -794,17 +788,10 @@ async function recordZstdAvailability(
// Arbitrarily choose the first language. We could also choose all languages, but that // Arbitrarily choose the first language. We could also choose all languages, but that
// increases the risk of misinterpreting the data. // increases the risk of misinterpreting the data.
config.languages[0], config.languages[0],
makeDiagnostic( makeTelemetryDiagnostic(
"codeql-action/zstd-availability", "codeql-action/zstd-availability",
"Zstandard availability", "Zstandard availability",
{ zstdAvailability,
attributes: zstdAvailability,
visibility: {
cliSummaryTable: false,
statusPage: false,
telemetry: true,
},
},
), ),
); );
} }