Move git version logging to config utils

This commit is contained in:
Henry Mercer
2025-12-17 12:06:41 +00:00
parent e052dbd57d
commit 3765106c90
17 changed files with 321 additions and 445 deletions
+2 -15
View File
@@ -4,7 +4,6 @@ import * as path from "path";
import * as github from "@actions/github";
import test, { ExecutionContext } from "ava";
import * as yaml from "js-yaml";
import * as semver from "semver";
import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
@@ -994,7 +993,7 @@ const defaultOverlayDatabaseModeTestSetup: OverlayDatabaseModeTestSetup = {
languages: [KnownLanguage.javascript],
codeqlVersion: CODEQL_OVERLAY_MINIMUM_VERSION,
gitRoot: "/some/git/root",
gitVersion: "2.40.0", // Default to a version that supports overlay analysis
gitVersion: gitUtils.GIT_MINIMUM_VERSION_FOR_OVERLAY,
codeScanningConfig: {},
diskUsage: {
numAvailableBytes: 50_000_000_000,
@@ -1060,19 +1059,6 @@ const getOverlayDatabaseModeMacro = test.macro({
sinon.stub(gitUtils, "getGitRoot").resolves(setup.gitRoot);
}
// Mock git version detection - stub gitVersionAtLeast directly
// since internal calls to getGitVersion won't be stubbed
if (setup.gitVersion !== undefined) {
sinon
.stub(gitUtils, "gitVersionAtLeast")
.callsFake(async (requiredVersion: string) => {
return semver.gte(setup.gitVersion!, requiredVersion);
});
} else {
// When git version is undefined, gitVersionAtLeast should return false
sinon.stub(gitUtils, "gitVersionAtLeast").resolves(false);
}
// Mock default branch detection
sinon
.stub(gitUtils, "isAnalyzingDefaultBranch")
@@ -1086,6 +1072,7 @@ const getOverlayDatabaseModeMacro = test.macro({
setup.buildMode,
undefined,
setup.codeScanningConfig,
setup.gitVersion,
logger,
);
+44 -2
View File
@@ -3,6 +3,7 @@ import * as path from "path";
import { performance } from "perf_hooks";
import * as yaml from "js-yaml";
import * as semver from "semver";
import { getActionVersion, isAnalyzingPullRequest } from "./actions-util";
import {
@@ -22,14 +23,15 @@ import {
parseUserConfig,
UserConfig,
} from "./config/db-config";
import { addDiagnostic, makeTelemetryDiagnostic } from "./diagnostics";
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
import * as errorMessages from "./error-messages";
import { Feature, FeatureEnablement } from "./feature-flags";
import { RepositoryProperties } from "./feature-flags/properties";
import {
getGitRoot,
getGitVersionOrThrow,
GIT_MINIMUM_VERSION_FOR_OVERLAY,
gitVersionAtLeast,
isAnalyzingDefaultBranch,
} from "./git-utils";
import { KnownLanguage, Language } from "./languages";
@@ -50,6 +52,7 @@ import {
isDefined,
checkDiskUsage,
getCodeQLMemoryLimit,
getErrorMessage,
} from "./util";
export * from "./config/db-config";
@@ -714,6 +717,7 @@ export async function getOverlayDatabaseMode(
buildMode: BuildMode | undefined,
ramInput: string | undefined,
codeScanningConfig: UserConfig,
gitVersion: string | undefined,
logger: Logger,
): Promise<{
overlayDatabaseMode: OverlayDatabaseMode;
@@ -816,7 +820,15 @@ export async function getOverlayDatabaseMode(
);
return nonOverlayAnalysis;
}
if (!(await gitVersionAtLeast(GIT_MINIMUM_VERSION_FOR_OVERLAY, logger))) {
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 (!semver.gte(gitVersion, 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}. ` +
@@ -916,6 +928,15 @@ export async function initConfig(
config.computedConfig["query-filters"] = [];
}
let gitVersion: string | undefined = undefined;
try {
gitVersion = await getGitVersionOrThrow();
logger.info(`Using Git version ${gitVersion}`);
await logGitVersionTelemetry(config, gitVersion);
} catch (e) {
logger.debug(`Could not determine Git version: ${getErrorMessage(e)}`);
}
// 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
// properties. So we need to calculate the overlay database mode after the
@@ -929,6 +950,7 @@ export async function initConfig(
config.buildMode,
inputs.ramInput,
config.computedConfig,
gitVersion,
logger,
);
logger.info(
@@ -1329,3 +1351,23 @@ export function getPrimaryAnalysisConfig(config: Config): AnalysisConfig {
? CodeScanning
: CodeQuality;
}
/** Logs the Git version as a telemetry diagnostic. */
async function logGitVersionTelemetry(
config: Config,
gitVersion: string,
): 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",
{ gitVersion },
),
);
}
}
+1 -69
View File
@@ -7,12 +7,7 @@ import * as sinon from "sinon";
import * as actionsUtil from "./actions-util";
import * as gitUtils from "./git-utils";
import {
getRecordingLogger,
LoggedMessage,
setupActionsVars,
setupTests,
} from "./testing-utils";
import { setupActionsVars, setupTests } from "./testing-utils";
import { withTmpDir } from "./util";
setupTests(test);
@@ -464,66 +459,3 @@ test("getGitVersionOrThrow throws when git command fails", async (t) => {
runGitCommandStub.restore();
}
});
test("gitVersionAtLeast returns true for version meeting requirement", async (t) => {
const runGitCommandStub = sinon
.stub(gitUtils as any, "runGitCommand")
.resolves("git version 2.40.0\n");
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
try {
const result = await gitUtils.gitVersionAtLeast("2.38.0", logger);
t.true(result);
t.true(
messages.some(
(m) =>
m.type === "debug" &&
m.message === "Installed Git version is 2.40.0.",
),
);
} finally {
runGitCommandStub.restore();
}
});
test("gitVersionAtLeast returns false for version not meeting requirement", async (t) => {
const runGitCommandStub = sinon
.stub(gitUtils as any, "runGitCommand")
.resolves("git version 2.30.0\n");
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
try {
const result = await gitUtils.gitVersionAtLeast("2.38.0", logger);
t.false(result);
} finally {
runGitCommandStub.restore();
}
});
test("gitVersionAtLeast returns false when version cannot be determined", async (t) => {
const runGitCommandStub = sinon
.stub(gitUtils as any, "runGitCommand")
.rejects(new Error("git not found"));
const messages: LoggedMessage[] = [];
const logger = getRecordingLogger(messages);
try {
const result = await gitUtils.gitVersionAtLeast("2.38.0", logger);
t.false(result);
t.true(
messages.some(
(m) =>
m.type === "debug" &&
typeof m.message === "string" &&
m.message.includes("Could not determine Git version"),
),
);
} finally {
runGitCommandStub.restore();
}
});
+1 -62
View File
@@ -1,21 +1,13 @@
import * as core from "@actions/core";
import * as toolrunner from "@actions/exec/lib/toolrunner";
import * as io from "@actions/io";
import * as semver from "semver";
import {
getOptionalInput,
getWorkflowEvent,
getWorkflowEventName,
} from "./actions-util";
import type { Config } from "./config-utils";
import { addDiagnostic, makeTelemetryDiagnostic } from "./diagnostics";
import { Logger } from "./logging";
import {
ConfigurationError,
getErrorMessage,
getRequiredEnvParam,
} from "./util";
import { ConfigurationError, getRequiredEnvParam } from "./util";
/**
* Minimum Git version required for overlay analysis. The `git ls-files --format`
@@ -45,59 +37,6 @@ export async function getGitVersionOrThrow(): Promise<string> {
throw new Error(`Could not parse Git version from output: ${stdout.trim()}`);
}
/**
* Logs the Git version as a telemetry diagnostic. Should be called once during
* initialization after the config is available.
*
* @param config The configuration that tells us where to store the diagnostic.
* @param logger A logger to use for logging errors.
*/
export async function logGitVersionTelemetry(
config: Config,
logger: Logger,
): Promise<void> {
try {
const version = await getGitVersionOrThrow();
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",
{ gitVersion: version },
),
);
}
} catch (e) {
logger.debug(`Could not determine Git version: ${getErrorMessage(e)}`);
}
}
/**
* Checks if the installed Git version is at least the given required version.
*
* @param requiredVersion The minimum required Git version.
* @param logger A logger to use for logging.
* @returns `true` if the installed Git version is at least the required version,
* `false` otherwise.
*/
export async function gitVersionAtLeast(
requiredVersion: string,
logger: Logger,
): Promise<boolean> {
try {
const version = await getGitVersionOrThrow();
logger.debug(`Installed Git version is ${version}.`);
return semver.gte(version, requiredVersion);
} catch (e) {
logger.debug(`Could not determine Git version: ${getErrorMessage(e)}`);
return false;
}
}
export const runGitCommand = async function (
workingDirectory: string | undefined,
args: string[],
-4
View File
@@ -38,7 +38,6 @@ import {
import { EnvVar } from "./environment";
import { Feature, Features } from "./feature-flags";
import { loadPropertiesFromApi } from "./feature-flags/properties";
import { logGitVersionTelemetry } from "./git-utils";
import {
checkInstallPython311,
checkPacksForOverlayCompatibility,
@@ -435,9 +434,6 @@ async function run() {
);
}
// Log Git version telemetry
await logGitVersionTelemetry(config, logger);
// Forward Go flags
const goFlags = process.env["GOFLAGS"];
if (goFlags) {