mirror of
https://github.com/github/codeql-action.git
synced 2026-05-21 21:08:32 +00:00
Merge branch 'main' into henrymercer/require-codeql-2.19.4
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
"use strict";
|
||||
|
||||
const import_entry_points = require("./entry-points");
|
||||
void (0, import_entry_points.run__ACTION__)();
|
||||
+54
-9
@@ -16,7 +16,7 @@ import {
|
||||
} from "./analyses";
|
||||
import { EnvVar } from "./environment";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { setupTests } from "./testing-utils";
|
||||
import { createFeatures, RecordingLogger, setupTests } from "./testing-utils";
|
||||
import { AssessmentPayload } from "./upload-lib/types";
|
||||
import { ConfigurationError } from "./util";
|
||||
|
||||
@@ -53,24 +53,56 @@ test("Parsing analysis kinds requires at least one analysis kind", async (t) =>
|
||||
test.serial(
|
||||
"getAnalysisKinds - returns expected analysis kinds for `analysis-kinds` input",
|
||||
async (t) => {
|
||||
process.env[EnvVar.TEST_MODE] = "true";
|
||||
const features = createFeatures([]);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub
|
||||
.withArgs("analysis-kinds")
|
||||
.returns("code-scanning,code-quality");
|
||||
const result = await getAnalysisKinds(getRunnerLogger(true), true);
|
||||
const result = await getAnalysisKinds(
|
||||
getRunnerLogger(true),
|
||||
features,
|
||||
true,
|
||||
);
|
||||
t.assert(result.includes(AnalysisKind.CodeScanning));
|
||||
t.assert(result.includes(AnalysisKind.CodeQuality));
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"getAnalysisKinds - only use `code-scanning` for multiple analysis kinds outside of test mode",
|
||||
async (t) => {
|
||||
process.env[EnvVar.TEST_MODE] = "false";
|
||||
const features = createFeatures([]);
|
||||
const logger = new RecordingLogger();
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub
|
||||
.withArgs("analysis-kinds")
|
||||
.returns("code-scanning,code-quality");
|
||||
const result = await getAnalysisKinds(logger, features, true);
|
||||
t.deepEqual(result, [AnalysisKind.CodeScanning]);
|
||||
t.assert(
|
||||
logger.hasMessage(
|
||||
"Continuing with only `analysis-kinds: code-scanning`.",
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"getAnalysisKinds - includes `code-quality` when deprecated `quality-queries` input is used",
|
||||
async (t) => {
|
||||
process.env[EnvVar.TEST_MODE] = "true";
|
||||
const features = createFeatures([]);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub.withArgs("analysis-kinds").returns("code-scanning");
|
||||
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
|
||||
optionalInputStub.withArgs("quality-queries").returns("code-quality");
|
||||
const result = await getAnalysisKinds(getRunnerLogger(true), true);
|
||||
const result = await getAnalysisKinds(
|
||||
getRunnerLogger(true),
|
||||
features,
|
||||
true,
|
||||
);
|
||||
t.assert(result.includes(AnalysisKind.CodeScanning));
|
||||
t.assert(result.includes(AnalysisKind.CodeQuality));
|
||||
},
|
||||
@@ -79,9 +111,12 @@ test.serial(
|
||||
test.serial(
|
||||
"getAnalysisKinds - throws if `analysis-kinds` input is invalid",
|
||||
async (t) => {
|
||||
const features = createFeatures([]);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub.withArgs("analysis-kinds").returns("no-such-thing");
|
||||
await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true));
|
||||
await t.throwsAsync(
|
||||
getAnalysisKinds(getRunnerLogger(true), features, true),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -98,11 +133,17 @@ for (let i = 0; i < analysisKinds.length; i++) {
|
||||
test.serial(
|
||||
`getAnalysisKinds - allows ${analysisKind} with ${otherAnalysis}`,
|
||||
async (t) => {
|
||||
process.env[EnvVar.TEST_MODE] = "true";
|
||||
const features = createFeatures([]);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub
|
||||
.withArgs("analysis-kinds")
|
||||
.returns([analysisKind, otherAnalysis].join(","));
|
||||
const result = await getAnalysisKinds(getRunnerLogger(true), true);
|
||||
const result = await getAnalysisKinds(
|
||||
getRunnerLogger(true),
|
||||
features,
|
||||
true,
|
||||
);
|
||||
t.is(result.length, 2);
|
||||
},
|
||||
);
|
||||
@@ -110,14 +151,18 @@ for (let i = 0; i < analysisKinds.length; i++) {
|
||||
test.serial(
|
||||
`getAnalysisKinds - throws if ${analysisKind} is enabled with ${otherAnalysis}`,
|
||||
async (t) => {
|
||||
const features = createFeatures([]);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub
|
||||
.withArgs("analysis-kinds")
|
||||
.returns([analysisKind, otherAnalysis].join(","));
|
||||
await t.throwsAsync(getAnalysisKinds(getRunnerLogger(true), true), {
|
||||
instanceOf: ConfigurationError,
|
||||
message: `${analysisKind} and ${otherAnalysis} cannot be enabled at the same time`,
|
||||
});
|
||||
await t.throwsAsync(
|
||||
getAnalysisKinds(getRunnerLogger(true), features, true),
|
||||
{
|
||||
instanceOf: ConfigurationError,
|
||||
message: `${analysisKind} and ${otherAnalysis} cannot be enabled at the same time`,
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
+22
-1
@@ -4,13 +4,14 @@ import {
|
||||
getRequiredInput,
|
||||
} from "./actions-util";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { Logger } from "./logging";
|
||||
import {
|
||||
AssessmentPayload,
|
||||
BasePayload,
|
||||
UploadPayload,
|
||||
} from "./upload-lib/types";
|
||||
import { ConfigurationError, getRequiredEnvParam } from "./util";
|
||||
import { ConfigurationError, getRequiredEnvParam, isInTestMode } from "./util";
|
||||
|
||||
export enum AnalysisKind {
|
||||
CodeScanning = "code-scanning",
|
||||
@@ -77,6 +78,7 @@ let cachedAnalysisKinds: AnalysisKind[] | undefined;
|
||||
*/
|
||||
export async function getAnalysisKinds(
|
||||
logger: Logger,
|
||||
features: FeatureEnablement,
|
||||
skipCache: boolean = false,
|
||||
): Promise<AnalysisKind[]> {
|
||||
if (!skipCache && cachedAnalysisKinds !== undefined) {
|
||||
@@ -120,6 +122,25 @@ export async function getAnalysisKinds(
|
||||
}
|
||||
}
|
||||
|
||||
// Log an error if we have multiple inputs for `analysis-kinds` outside of test mode,
|
||||
// and enable only `code-scanning`.
|
||||
if (
|
||||
!isInTestMode() &&
|
||||
analysisKinds.length > 1 &&
|
||||
!(await features.getValue(Feature.AllowMultipleAnalysisKinds))
|
||||
) {
|
||||
logger.error(
|
||||
"The `analysis-kinds` input is experimental and for GitHub-internal use only. " +
|
||||
"Its behaviour may change at any time or be removed entirely. " +
|
||||
"Specifying multiple values as input is no longer supported. " +
|
||||
"Continuing with only `analysis-kinds: code-scanning`.",
|
||||
);
|
||||
|
||||
// Only enable Code Scanning.
|
||||
cachedAnalysisKinds = [AnalysisKind.CodeScanning];
|
||||
return cachedAnalysisKinds;
|
||||
}
|
||||
|
||||
// Cache the analysis kinds and return them.
|
||||
cachedAnalysisKinds = analysisKinds;
|
||||
return cachedAnalysisKinds;
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as analyze from "./analyze";
|
||||
import * as api from "./api-client";
|
||||
import * as configUtils from "./config-utils";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import * as statusReport from "./status-report";
|
||||
import {
|
||||
setupTests,
|
||||
setupActionsVars,
|
||||
mockFeatureFlagApiEndpoint,
|
||||
} from "./testing-utils";
|
||||
import * as util from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
// This test needs to be in its own file so that ava would run it in its own
|
||||
// nodejs process. The code being tested is in analyze-action.ts, which runs
|
||||
// immediately on load. So the file needs to be loaded during part of the test,
|
||||
// and that can happen only once per nodejs process. If multiple such tests are
|
||||
// in the same test file, ava would run them in the same nodejs process, and all
|
||||
// but the first test would fail.
|
||||
|
||||
test("analyze action with RAM & threads from environment variables", async (t) => {
|
||||
// This test frequently times out on Windows with the default timeout, so we bump
|
||||
// it a bit to 20s.
|
||||
t.timeout(1000 * 20);
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
sinon
|
||||
.stub(statusReport, "createStatusReportBase")
|
||||
.resolves({} as statusReport.StatusReportBase);
|
||||
sinon.stub(statusReport, "sendStatusReport").resolves();
|
||||
sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true);
|
||||
|
||||
const gitHubVersion: util.GitHubVersion = {
|
||||
type: util.GitHubVariant.DOTCOM,
|
||||
};
|
||||
sinon.stub(configUtils, "getConfig").resolves({
|
||||
gitHubVersion,
|
||||
augmentationProperties: {},
|
||||
languages: [],
|
||||
packs: [],
|
||||
trapCaches: {},
|
||||
} as unknown as configUtils.Config);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub.withArgs("token").returns("fake-token");
|
||||
requiredInputStub.withArgs("upload-database").returns("false");
|
||||
requiredInputStub.withArgs("output").returns("out");
|
||||
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
|
||||
optionalInputStub.withArgs("expect-error").returns("false");
|
||||
sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion);
|
||||
mockFeatureFlagApiEndpoint(200, {});
|
||||
|
||||
// When there are no action inputs for RAM and threads, the action uses
|
||||
// environment variables (passed down from the init action) to set RAM and
|
||||
// threads usage.
|
||||
process.env["CODEQL_THREADS"] = "-1";
|
||||
process.env["CODEQL_RAM"] = "4992";
|
||||
|
||||
const runFinalizeStub = sinon.stub(analyze, "runFinalize");
|
||||
const runQueriesStub = sinon.stub(analyze, "runQueries");
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const analyzeAction = require("./analyze-action");
|
||||
|
||||
// When analyze-action.ts loads, it runs an async function from the top
|
||||
// level but does not wait for it to finish. To ensure that calls to
|
||||
// runFinalize and runQueries are correctly captured by spies, we explicitly
|
||||
// wait for the action promise to complete before starting verification.
|
||||
await analyzeAction.runPromise;
|
||||
|
||||
t.assert(
|
||||
runFinalizeStub.calledOnceWith(
|
||||
sinon.match.any,
|
||||
sinon.match.any,
|
||||
"--threads=-1",
|
||||
"--ram=4992",
|
||||
),
|
||||
);
|
||||
t.assert(
|
||||
runQueriesStub.calledOnceWith(
|
||||
sinon.match.any,
|
||||
"--ram=4992",
|
||||
"--threads=-1",
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,88 +0,0 @@
|
||||
import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as analyze from "./analyze";
|
||||
import * as api from "./api-client";
|
||||
import * as configUtils from "./config-utils";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import * as statusReport from "./status-report";
|
||||
import {
|
||||
setupTests,
|
||||
setupActionsVars,
|
||||
mockFeatureFlagApiEndpoint,
|
||||
} from "./testing-utils";
|
||||
import * as util from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
// This test needs to be in its own file so that ava would run it in its own
|
||||
// nodejs process. The code being tested is in analyze-action.ts, which runs
|
||||
// immediately on load. So the file needs to be loaded during part of the test,
|
||||
// and that can happen only once per nodejs process. If multiple such tests are
|
||||
// in the same test file, ava would run them in the same nodejs process, and all
|
||||
// but the first test would fail.
|
||||
|
||||
test("analyze action with RAM & threads from action inputs", async (t) => {
|
||||
t.timeout(1000 * 20);
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
sinon
|
||||
.stub(statusReport, "createStatusReportBase")
|
||||
.resolves({} as statusReport.StatusReportBase);
|
||||
sinon.stub(statusReport, "sendStatusReport").resolves();
|
||||
const gitHubVersion: util.GitHubVersion = {
|
||||
type: util.GitHubVariant.DOTCOM,
|
||||
};
|
||||
sinon.stub(configUtils, "getConfig").resolves({
|
||||
gitHubVersion,
|
||||
augmentationProperties: {},
|
||||
languages: [],
|
||||
packs: [],
|
||||
trapCaches: {},
|
||||
} as unknown as configUtils.Config);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub.withArgs("token").returns("fake-token");
|
||||
requiredInputStub.withArgs("upload-database").returns("false");
|
||||
requiredInputStub.withArgs("output").returns("out");
|
||||
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
|
||||
optionalInputStub.withArgs("expect-error").returns("false");
|
||||
sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion);
|
||||
sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true);
|
||||
mockFeatureFlagApiEndpoint(200, {});
|
||||
|
||||
process.env["CODEQL_THREADS"] = "1";
|
||||
process.env["CODEQL_RAM"] = "4992";
|
||||
|
||||
// Action inputs have precedence over environment variables.
|
||||
optionalInputStub.withArgs("threads").returns("-1");
|
||||
optionalInputStub.withArgs("ram").returns("3012");
|
||||
|
||||
const runFinalizeStub = sinon.stub(analyze, "runFinalize");
|
||||
const runQueriesStub = sinon.stub(analyze, "runQueries");
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
const analyzeAction = require("./analyze-action");
|
||||
|
||||
// When analyze-action.ts loads, it runs an async function from the top
|
||||
// level but does not wait for it to finish. To ensure that calls to
|
||||
// runFinalize and runQueries are correctly captured by spies, we explicitly
|
||||
// wait for the action promise to complete before starting verification.
|
||||
await analyzeAction.runPromise;
|
||||
|
||||
t.assert(
|
||||
runFinalizeStub.calledOnceWith(
|
||||
sinon.match.any,
|
||||
sinon.match.any,
|
||||
"--threads=-1",
|
||||
"--ram=3012",
|
||||
),
|
||||
);
|
||||
t.assert(
|
||||
runQueriesStub.calledOnceWith(
|
||||
sinon.match.any,
|
||||
"--ram=3012",
|
||||
"--threads=-1",
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -20,7 +20,7 @@ import { EnvVar } from "./environment";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { checkGitHubVersionInRange, getErrorMessage } from "./util";
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
// To capture errors appropriately, keep as much code within the try-catch as
|
||||
// possible, and only use safe functions outside.
|
||||
|
||||
@@ -72,5 +72,3 @@ async function runWrapper() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
import test from "ava";
|
||||
import * as sinon from "sinon";
|
||||
|
||||
import * as actionsUtil from "./actions-util";
|
||||
import * as analyze from "./analyze";
|
||||
import { runWrapper } from "./analyze-action";
|
||||
import * as api from "./api-client";
|
||||
import * as configUtils from "./config-utils";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import * as statusReport from "./status-report";
|
||||
import {
|
||||
setupTests,
|
||||
setupActionsVars,
|
||||
mockFeatureFlagApiEndpoint,
|
||||
} from "./testing-utils";
|
||||
import * as util from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
test.serial(
|
||||
"analyze action with RAM & threads from environment variables",
|
||||
async (t) => {
|
||||
// This test frequently times out on Windows with the default timeout, so we bump
|
||||
// it a bit to 20s.
|
||||
t.timeout(1000 * 20);
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
sinon
|
||||
.stub(statusReport, "createStatusReportBase")
|
||||
.resolves({} as statusReport.StatusReportBase);
|
||||
sinon.stub(statusReport, "sendStatusReport").resolves();
|
||||
sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true);
|
||||
|
||||
const gitHubVersion: util.GitHubVersion = {
|
||||
type: util.GitHubVariant.DOTCOM,
|
||||
};
|
||||
sinon.stub(configUtils, "getConfig").resolves({
|
||||
gitHubVersion,
|
||||
augmentationProperties: {},
|
||||
languages: [],
|
||||
packs: [],
|
||||
trapCaches: {},
|
||||
} as unknown as configUtils.Config);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub.withArgs("token").returns("fake-token");
|
||||
requiredInputStub.withArgs("upload-database").returns("false");
|
||||
requiredInputStub.withArgs("output").returns("out");
|
||||
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
|
||||
optionalInputStub.withArgs("expect-error").returns("false");
|
||||
sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion);
|
||||
mockFeatureFlagApiEndpoint(200, {});
|
||||
|
||||
// When there are no action inputs for RAM and threads, the action uses
|
||||
// environment variables (passed down from the init action) to set RAM and
|
||||
// threads usage.
|
||||
process.env["CODEQL_THREADS"] = "-1";
|
||||
process.env["CODEQL_RAM"] = "4992";
|
||||
|
||||
const runFinalizeStub = sinon.stub(analyze, "runFinalize");
|
||||
const runQueriesStub = sinon.stub(analyze, "runQueries");
|
||||
|
||||
await runWrapper();
|
||||
|
||||
t.assert(
|
||||
runFinalizeStub.calledOnceWith(
|
||||
sinon.match.any,
|
||||
sinon.match.any,
|
||||
"--threads=-1",
|
||||
"--ram=4992",
|
||||
),
|
||||
);
|
||||
t.assert(
|
||||
runQueriesStub.calledOnceWith(
|
||||
sinon.match.any,
|
||||
"--ram=4992",
|
||||
"--threads=-1",
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"analyze action with RAM & threads from action inputs",
|
||||
async (t) => {
|
||||
t.timeout(1000 * 20);
|
||||
await util.withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
sinon
|
||||
.stub(statusReport, "createStatusReportBase")
|
||||
.resolves({} as statusReport.StatusReportBase);
|
||||
sinon.stub(statusReport, "sendStatusReport").resolves();
|
||||
const gitHubVersion: util.GitHubVersion = {
|
||||
type: util.GitHubVariant.DOTCOM,
|
||||
};
|
||||
sinon.stub(configUtils, "getConfig").resolves({
|
||||
gitHubVersion,
|
||||
augmentationProperties: {},
|
||||
languages: [],
|
||||
packs: [],
|
||||
trapCaches: {},
|
||||
} as unknown as configUtils.Config);
|
||||
const requiredInputStub = sinon.stub(actionsUtil, "getRequiredInput");
|
||||
requiredInputStub.withArgs("token").returns("fake-token");
|
||||
requiredInputStub.withArgs("upload-database").returns("false");
|
||||
requiredInputStub.withArgs("output").returns("out");
|
||||
const optionalInputStub = sinon.stub(actionsUtil, "getOptionalInput");
|
||||
optionalInputStub.withArgs("expect-error").returns("false");
|
||||
sinon.stub(api, "getGitHubVersion").resolves(gitHubVersion);
|
||||
sinon.stub(gitUtils, "isAnalyzingDefaultBranch").resolves(true);
|
||||
mockFeatureFlagApiEndpoint(200, {});
|
||||
|
||||
process.env["CODEQL_THREADS"] = "1";
|
||||
process.env["CODEQL_RAM"] = "4992";
|
||||
|
||||
// Action inputs have precedence over environment variables.
|
||||
optionalInputStub.withArgs("threads").returns("-1");
|
||||
optionalInputStub.withArgs("ram").returns("3012");
|
||||
|
||||
const runFinalizeStub = sinon.stub(analyze, "runFinalize");
|
||||
const runQueriesStub = sinon.stub(analyze, "runQueries");
|
||||
|
||||
await runWrapper();
|
||||
|
||||
t.assert(
|
||||
runFinalizeStub.calledOnceWith(
|
||||
sinon.match.any,
|
||||
sinon.match.any,
|
||||
"--threads=-1",
|
||||
"--ram=3012",
|
||||
),
|
||||
);
|
||||
t.assert(
|
||||
runQueriesStub.calledOnceWith(
|
||||
sinon.match.any,
|
||||
"--ram=3012",
|
||||
"--threads=-1",
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -523,14 +523,11 @@ async function run(startedAt: Date) {
|
||||
}
|
||||
}
|
||||
|
||||
// Module-level startedAt so it can be accessed by runWrapper for error reporting
|
||||
const startedAt = new Date();
|
||||
export const runPromise = run(startedAt);
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
await runPromise;
|
||||
await run(startedAt);
|
||||
} catch (error) {
|
||||
core.setFailed(`analyze action failed: ${util.getErrorMessage(error)}`);
|
||||
await sendUnhandledErrorStatusReport(
|
||||
@@ -542,5 +539,3 @@ async function runWrapper() {
|
||||
}
|
||||
await util.checkForTimeout();
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
@@ -141,14 +141,9 @@ test("scanArtifactsForTokens handles files without tokens", async (t) => {
|
||||
}
|
||||
});
|
||||
|
||||
// This test is slow (extracts and scans a zip artifact), so by default we only run it in CI. Set
|
||||
// RUN_SLOW_TESTS=1 to run it locally.
|
||||
if (
|
||||
os.platform() !== "win32" &&
|
||||
(process.env.CI === "true" || process.env.RUN_SLOW_TESTS === "1")
|
||||
) {
|
||||
// `scanArchiveFile` does not support Windows, so we skip this test there.
|
||||
if (os.platform() !== "win32") {
|
||||
test("scanArtifactsForTokens finds token in debug artifacts", async (t) => {
|
||||
t.timeout(15000); // 15 seconds
|
||||
const messages: LoggedMessage[] = [];
|
||||
const logger = getRecordingLogger(messages, { logToConsole: false });
|
||||
// The zip here is a regression test based on
|
||||
|
||||
@@ -142,7 +142,7 @@ async function run(startedAt: Date) {
|
||||
await sendCompletedStatusReport(config, logger, startedAt, languages ?? []);
|
||||
}
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
@@ -157,5 +157,3 @@ async function runWrapper() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
@@ -21,6 +21,7 @@ import { GitVersionInfo } from "./git-utils";
|
||||
import { BuiltInLanguage, Language } from "./languages";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { CODEQL_OVERLAY_MINIMUM_VERSION } from "./overlay";
|
||||
import * as overlayDiagnostics from "./overlay/diagnostics";
|
||||
import { OverlayDisabledReason } from "./overlay/diagnostics";
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
import * as overlayStatus from "./overlay/status";
|
||||
@@ -2143,3 +2144,87 @@ test.serial(
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test("applyIncrementalAnalysisSettings: no-op when mode is not Overlay and diff ranges are unavailable", async (t) => {
|
||||
const config = createTestConfig({});
|
||||
config.overlayDatabaseMode = OverlayDatabaseMode.None;
|
||||
const codeql = createStubCodeQL({});
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
await configUtils.applyIncrementalAnalysisSettings(
|
||||
config,
|
||||
false,
|
||||
codeql,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
|
||||
t.deepEqual(config.extraQueryExclusions, []);
|
||||
});
|
||||
|
||||
test("applyIncrementalAnalysisSettings: keeps overlay mode and adds exclusions when diff ranges are available", async (t) => {
|
||||
const config = createTestConfig({
|
||||
overlayDatabaseMode: OverlayDatabaseMode.Overlay,
|
||||
});
|
||||
const codeql = createStubCodeQL({});
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
await configUtils.applyIncrementalAnalysisSettings(
|
||||
config,
|
||||
true,
|
||||
codeql,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.Overlay);
|
||||
t.deepEqual(config.extraQueryExclusions, [
|
||||
{ exclude: { tags: "exclude-from-incremental" } },
|
||||
]);
|
||||
});
|
||||
|
||||
test("applyIncrementalAnalysisSettings: disables overlay analysis when diff ranges are unavailable", async (t) => {
|
||||
const config = createTestConfig({
|
||||
overlayDatabaseMode: OverlayDatabaseMode.Overlay,
|
||||
});
|
||||
config.useOverlayDatabaseCaching = true;
|
||||
const codeql = createStubCodeQL({});
|
||||
const logger = getRunnerLogger(true);
|
||||
const addDiagnosticsStub = sinon
|
||||
.stub(overlayDiagnostics, "addOverlayDisablementDiagnostics")
|
||||
.resolves();
|
||||
|
||||
await configUtils.applyIncrementalAnalysisSettings(
|
||||
config,
|
||||
false,
|
||||
codeql,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
|
||||
t.is(config.useOverlayDatabaseCaching, false);
|
||||
t.deepEqual(config.extraQueryExclusions, []);
|
||||
t.true(addDiagnosticsStub.calledOnce);
|
||||
t.is(
|
||||
addDiagnosticsStub.firstCall.args[2],
|
||||
OverlayDisabledReason.DiffInformedAnalysisNotEnabled,
|
||||
);
|
||||
});
|
||||
|
||||
test("applyIncrementalAnalysisSettings: adds exclusions for diff-informed-only runs", async (t) => {
|
||||
const config = createTestConfig({});
|
||||
config.overlayDatabaseMode = OverlayDatabaseMode.None;
|
||||
const codeql = createStubCodeQL({});
|
||||
const logger = getRunnerLogger(true);
|
||||
|
||||
await configUtils.applyIncrementalAnalysisSettings(
|
||||
config,
|
||||
true,
|
||||
codeql,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(config.overlayDatabaseMode, OverlayDatabaseMode.None);
|
||||
t.deepEqual(config.extraQueryExclusions, [
|
||||
{ exclude: { tags: "exclude-from-incremental" } },
|
||||
]);
|
||||
});
|
||||
|
||||
+55
-13
@@ -31,7 +31,7 @@ import {
|
||||
addNoLanguageDiagnostic,
|
||||
makeTelemetryDiagnostic,
|
||||
} from "./diagnostics";
|
||||
import { shouldPerformDiffInformedAnalysis } from "./diff-informed-analysis-utils";
|
||||
import { prepareDiffInformedAnalysis } from "./diff-informed-analysis-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import * as errorMessages from "./error-messages";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
@@ -1077,6 +1077,48 @@ function hasQueryCustomisation(userConfig: UserConfig): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize the incremental-analysis configuration for this run.
|
||||
*
|
||||
* Overlay analysis has only been validated in combination with diff-informed
|
||||
* analysis, so if `Overlay` mode was selected for a pull request but the diff
|
||||
* ranges could not be computed, fall back to a full non-overlay analysis.
|
||||
*
|
||||
* Query exclusions for incremental-only queries are then applied whenever the
|
||||
* diff ranges are available — which, after the fallback above, is exactly the
|
||||
* set of runs where any kind of incremental analysis (overlay or
|
||||
* diff-informed) is in effect.
|
||||
*/
|
||||
export async function applyIncrementalAnalysisSettings(
|
||||
config: Config,
|
||||
hasDiffRanges: boolean,
|
||||
codeql: CodeQL,
|
||||
logger: Logger,
|
||||
): Promise<void> {
|
||||
if (
|
||||
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay &&
|
||||
!hasDiffRanges
|
||||
) {
|
||||
logger.info(
|
||||
`Reverting overlay database mode to ${OverlayDatabaseMode.None} ` +
|
||||
"because the PR diff ranges could not be computed.",
|
||||
);
|
||||
config.overlayDatabaseMode = OverlayDatabaseMode.None;
|
||||
config.useOverlayDatabaseCaching = false;
|
||||
await addOverlayDisablementDiagnostics(
|
||||
config,
|
||||
codeql,
|
||||
OverlayDisabledReason.DiffInformedAnalysisNotEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
if (hasDiffRanges) {
|
||||
config.extraQueryExclusions.push({
|
||||
exclude: { tags: "exclude-from-incremental" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and return the config.
|
||||
*
|
||||
@@ -1231,18 +1273,18 @@ export async function initConfig(
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
config.overlayDatabaseMode === OverlayDatabaseMode.Overlay ||
|
||||
(await shouldPerformDiffInformedAnalysis(
|
||||
inputs.codeql,
|
||||
inputs.features,
|
||||
logger,
|
||||
))
|
||||
) {
|
||||
config.extraQueryExclusions.push({
|
||||
exclude: { tags: "exclude-from-incremental" },
|
||||
});
|
||||
}
|
||||
const hasDiffRanges = await prepareDiffInformedAnalysis(
|
||||
inputs.codeql,
|
||||
inputs.features,
|
||||
logger,
|
||||
);
|
||||
|
||||
await applyIncrementalAnalysisSettings(
|
||||
config,
|
||||
hasDiffRanges,
|
||||
inputs.codeql,
|
||||
logger,
|
||||
);
|
||||
|
||||
if (await isTrapCachingEnabled(features, config.overlayDatabaseMode)) {
|
||||
const { trapCaches, trapCacheDownloadTime } = await downloadCacheWithTime(
|
||||
|
||||
@@ -5,14 +5,16 @@ import * as actionsUtil from "./actions-util";
|
||||
import type { PullRequestBranches } from "./actions-util";
|
||||
import * as apiClient from "./api-client";
|
||||
import {
|
||||
shouldPerformDiffInformedAnalysis,
|
||||
getDiffInformedAnalysisBranches,
|
||||
prepareDiffInformedAnalysis,
|
||||
exportedForTesting,
|
||||
} from "./diff-informed-analysis-utils";
|
||||
import { Feature, initFeatures } from "./feature-flags";
|
||||
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import {
|
||||
setupTests,
|
||||
createFeatures,
|
||||
mockCodeQLVersion,
|
||||
mockFeatureFlagApiEndpoint,
|
||||
setupActionsVars,
|
||||
@@ -73,28 +75,25 @@ const testShouldPerformDiffInformedAnalysis = makeMacro({
|
||||
[Feature.DiffInformedQueries]: testCase.featureEnabled,
|
||||
});
|
||||
|
||||
const getGitHubVersionStub = sinon
|
||||
sinon
|
||||
.stub(apiClient, "getGitHubVersion")
|
||||
.resolves(testCase.gitHubVersion);
|
||||
const getPullRequestBranchesStub = sinon
|
||||
sinon
|
||||
.stub(actionsUtil, "getPullRequestBranches")
|
||||
.returns(testCase.pullRequestBranches);
|
||||
|
||||
const result = await shouldPerformDiffInformedAnalysis(
|
||||
const branches = await getDiffInformedAnalysisBranches(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.is(result, expectedResult);
|
||||
t.is(branches !== undefined, expectedResult);
|
||||
|
||||
delete process.env.CODEQL_ACTION_DIFF_INFORMED_QUERIES;
|
||||
|
||||
getGitHubVersionStub.restore();
|
||||
getPullRequestBranchesStub.restore();
|
||||
});
|
||||
},
|
||||
title: (title) => `shouldPerformDiffInformedAnalysis: ${title}`,
|
||||
title: (title) => `getDiffInformedAnalysisBranches: ${title}`,
|
||||
});
|
||||
|
||||
testShouldPerformDiffInformedAnalysis.serial(
|
||||
@@ -178,6 +177,135 @@ testShouldPerformDiffInformedAnalysis.serial(
|
||||
false,
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"prepareDiffInformedAnalysis: returns false when not a pull request",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
const logger = getRunnerLogger(true);
|
||||
const codeql = mockCodeQLVersion("2.21.0");
|
||||
const features = createFeatures([Feature.DiffInformedQueries]);
|
||||
|
||||
sinon.stub(actionsUtil, "getPullRequestBranches").returns(undefined);
|
||||
sinon
|
||||
.stub(apiClient, "getGitHubVersion")
|
||||
.resolves({ type: GitHubVariant.DOTCOM });
|
||||
|
||||
const result = await prepareDiffInformedAnalysis(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.false(result);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"prepareDiffInformedAnalysis: returns false when applicability check throws",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
const logger = getRunnerLogger(true);
|
||||
const codeql = mockCodeQLVersion("2.21.0");
|
||||
// A features implementation whose getValue rejects, simulating an
|
||||
// unexpected failure when determining whether diff-informed analysis
|
||||
// should run.
|
||||
const features: FeatureEnablement = {
|
||||
getEnabledDefaultCliVersions: async () => {
|
||||
throw new Error("not implemented");
|
||||
},
|
||||
getValue: async () => {
|
||||
throw new Error("feature flag lookup failed");
|
||||
},
|
||||
};
|
||||
|
||||
const result = await prepareDiffInformedAnalysis(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.false(result);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"prepareDiffInformedAnalysis: returns true when the diff is fetched successfully",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
const logger = getRunnerLogger(true);
|
||||
const codeql = mockCodeQLVersion("2.21.0");
|
||||
const features = createFeatures([Feature.DiffInformedQueries]);
|
||||
|
||||
sinon
|
||||
.stub(actionsUtil, "getPullRequestBranches")
|
||||
.returns({ base: "main", head: "feature" });
|
||||
sinon
|
||||
.stub(apiClient, "getGitHubVersion")
|
||||
.resolves({ type: GitHubVariant.DOTCOM });
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
sinon.stub(apiClient, "getApiClient").returns({
|
||||
rest: {
|
||||
repos: {
|
||||
compareCommitsWithBasehead: sinon
|
||||
.stub()
|
||||
.resolves({ data: { files: [] } }),
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
|
||||
const result = await prepareDiffInformedAnalysis(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.true(result);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"prepareDiffInformedAnalysis: returns false when the diff API call fails",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
setupActionsVars(tmpDir, tmpDir);
|
||||
const logger = getRunnerLogger(true);
|
||||
const codeql = mockCodeQLVersion("2.21.0");
|
||||
const features = createFeatures([Feature.DiffInformedQueries]);
|
||||
|
||||
sinon
|
||||
.stub(actionsUtil, "getPullRequestBranches")
|
||||
.returns({ base: "main", head: "feature" });
|
||||
sinon
|
||||
.stub(apiClient, "getGitHubVersion")
|
||||
.resolves({ type: GitHubVariant.DOTCOM });
|
||||
const notFoundError: any = new Error("Not Found");
|
||||
notFoundError.status = 404;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
sinon.stub(apiClient, "getApiClient").returns({
|
||||
rest: {
|
||||
repos: {
|
||||
compareCommitsWithBasehead: sinon.stub().rejects(notFoundError),
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
|
||||
const result = await prepareDiffInformedAnalysis(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
|
||||
t.false(result);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
function runGetDiffRanges(changes: number, patch: string[] | undefined): any {
|
||||
return exportedForTesting.getDiffRanges(
|
||||
{
|
||||
|
||||
@@ -5,9 +5,9 @@ import type { PullRequestBranches } from "./actions-util";
|
||||
import { getApiClient, getGitHubVersion } from "./api-client";
|
||||
import type { CodeQL } from "./codeql";
|
||||
import { Feature, FeatureEnablement } from "./feature-flags";
|
||||
import { Logger } from "./logging";
|
||||
import { Logger, withGroupAsync } from "./logging";
|
||||
import { getRepositoryNwoFromEnv } from "./repository";
|
||||
import { GitHubVariant, satisfiesGHESVersion } from "./util";
|
||||
import { getErrorMessage, GitHubVariant, satisfiesGHESVersion } from "./util";
|
||||
|
||||
/**
|
||||
* This interface is an abbreviated version of the file diff object returned by
|
||||
@@ -21,20 +21,6 @@ interface FileDiff {
|
||||
patch?: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the action should perform diff-informed analysis.
|
||||
*/
|
||||
export async function shouldPerformDiffInformedAnalysis(
|
||||
codeql: CodeQL,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
): Promise<boolean> {
|
||||
return (
|
||||
(await getDiffInformedAnalysisBranches(codeql, features, logger)) !==
|
||||
undefined
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the branches to use for diff-informed analysis.
|
||||
*
|
||||
@@ -69,6 +55,46 @@ export async function getDiffInformedAnalysisBranches(
|
||||
return branches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the diff ranges needed for diff-informed analysis for the current
|
||||
* run.
|
||||
*
|
||||
* @returns `true` if the diff ranges were successfully computed and persisted
|
||||
* and are therefore available for use, `false` otherwise.
|
||||
*/
|
||||
export async function prepareDiffInformedAnalysis(
|
||||
codeql: CodeQL,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
): Promise<boolean> {
|
||||
let branches: PullRequestBranches | undefined;
|
||||
try {
|
||||
branches = await getDiffInformedAnalysisBranches(codeql, features, logger);
|
||||
} catch (e) {
|
||||
// If we cannot determine whether diff-informed analysis applies (for
|
||||
// example, because a feature-flag lookup failed), treat it as not
|
||||
// applicable rather than triggering the overlay fallback.
|
||||
logger.warning(
|
||||
`Failed to determine branch information for diff-informed analysis: ${getErrorMessage(e)}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!branches) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return await withGroupAsync("Computing PR diff ranges", async () => {
|
||||
try {
|
||||
return await computeAndPersistDiffRanges(branches, logger);
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
`Failed to compute diff-informed analysis ranges: ${getErrorMessage(e)}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export interface DiffThunkRange {
|
||||
/** Relative path from the repository root, using forward slashes as separators. */
|
||||
path: string;
|
||||
@@ -151,6 +177,33 @@ export async function getPullRequestEditedDiffRanges(
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and persist the diff ranges for a pull request. This fetches the
|
||||
* diff from the GitHub API and writes it to the diff ranges JSON file so that
|
||||
* CodeQL can use it for diff-informed analysis.
|
||||
*
|
||||
* @param branches The base and head branches of the pull request, as returned
|
||||
* by `getDiffInformedAnalysisBranches`.
|
||||
* @param logger
|
||||
* @returns `true` if the diff ranges were successfully computed and persisted,
|
||||
* otherwise `false`.
|
||||
*/
|
||||
export async function computeAndPersistDiffRanges(
|
||||
branches: PullRequestBranches,
|
||||
logger: Logger,
|
||||
): Promise<boolean> {
|
||||
const ranges = await getPullRequestEditedDiffRanges(branches, logger);
|
||||
if (ranges === undefined) {
|
||||
return false;
|
||||
}
|
||||
writeDiffRangesJsonFile(logger, ranges);
|
||||
const distinctFiles = new Set(ranges.map((r) => r.path)).size;
|
||||
logger.info(
|
||||
`Persisted ${ranges.length} diff range(s) across ${distinctFiles} file(s).`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function getFileDiffsWithBasehead(
|
||||
branches: PullRequestBranches,
|
||||
logger: Logger,
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export async function run__ACTION__() {
|
||||
return await __ACTION__.runWrapper();
|
||||
}
|
||||
@@ -70,6 +70,8 @@ export interface CodeQLDefaultVersionInfo {
|
||||
* Legacy features should end with `_enabled`.
|
||||
*/
|
||||
export enum Feature {
|
||||
/** Controls whether we allow multiple values for the `analysis-kinds` input. */
|
||||
AllowMultipleAnalysisKinds = "allow_multiple_analysis_kinds",
|
||||
AllowToolcacheInput = "allow_toolcache_input",
|
||||
CleanupTrapCaches = "cleanup_trap_caches",
|
||||
CppDependencyInstallation = "cpp_dependency_installation_enabled",
|
||||
@@ -163,6 +165,11 @@ export type FeatureConfig = {
|
||||
};
|
||||
|
||||
export const featureConfig = {
|
||||
[Feature.AllowMultipleAnalysisKinds]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_ALLOW_MULTIPLE_ANALYSIS_KINDS",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.AllowToolcacheInput]: {
|
||||
defaultValue: false,
|
||||
envVar: "CODEQL_ACTION_ALLOW_TOOLCACHE_INPUT",
|
||||
|
||||
+77
-14
@@ -33,7 +33,6 @@ test.serial(
|
||||
|
||||
const actualRef = await gitUtils.getRef();
|
||||
t.deepEqual(actualRef, expectedRef);
|
||||
callback.restore();
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -54,7 +53,6 @@ test.serial(
|
||||
|
||||
const actualRef = await gitUtils.getRef();
|
||||
t.deepEqual(actualRef, expectedRef);
|
||||
callback.restore();
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -73,7 +71,6 @@ test.serial(
|
||||
|
||||
const actualRef = await gitUtils.getRef();
|
||||
t.deepEqual(actualRef, "refs/pull/1/head");
|
||||
callback.restore();
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -100,8 +97,6 @@ test.serial(
|
||||
|
||||
const actualRef = await gitUtils.getRef();
|
||||
t.deepEqual(actualRef, "refs/pull/2/merge");
|
||||
callback.restore();
|
||||
getAdditionalInputStub.restore();
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -161,7 +156,6 @@ test.serial(
|
||||
"Both 'ref' and 'sha' are required if one of them is provided.",
|
||||
},
|
||||
);
|
||||
getAdditionalInputStub.restore();
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -188,7 +182,6 @@ test.serial(
|
||||
"Both 'ref' and 'sha' are required if one of them is provided.",
|
||||
},
|
||||
);
|
||||
getAdditionalInputStub.restore();
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -242,7 +235,6 @@ test.serial("isAnalyzingDefaultBranch()", async (t) => {
|
||||
process.env["GITHUB_EVENT_NAME"] = "schedule";
|
||||
process.env["GITHUB_REF"] = "refs/heads/main";
|
||||
t.deepEqual(await gitUtils.isAnalyzingDefaultBranch(), false);
|
||||
getAdditionalInputStub.restore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -254,8 +246,6 @@ test.serial("determineBaseBranchHeadCommitOid non-pullrequest", async (t) => {
|
||||
const result = await gitUtils.determineBaseBranchHeadCommitOid(__dirname);
|
||||
t.deepEqual(result, undefined);
|
||||
t.deepEqual(0, infoStub.callCount);
|
||||
|
||||
infoStub.restore();
|
||||
});
|
||||
|
||||
test.serial(
|
||||
@@ -276,8 +266,6 @@ test.serial(
|
||||
"git call failed. Will calculate the base branch SHA on the server. Error: " +
|
||||
"The checkout path provided to the action does not appear to be a git repository.",
|
||||
);
|
||||
|
||||
infoStub.restore();
|
||||
},
|
||||
);
|
||||
|
||||
@@ -301,10 +289,27 @@ test.serial("determineBaseBranchHeadCommitOid other error", async (t) => {
|
||||
"The checkout path provided to the action does not appear to be a git repository.",
|
||||
),
|
||||
);
|
||||
|
||||
infoStub.restore();
|
||||
});
|
||||
|
||||
test.serial(
|
||||
"determineBaseBranchHeadCommitOid accepts SHA-256 OIDs",
|
||||
async (t) => {
|
||||
const mergeSha = "a".repeat(64);
|
||||
const baseOid = "b".repeat(64);
|
||||
const headOid = "c".repeat(64);
|
||||
|
||||
process.env["GITHUB_EVENT_NAME"] = "pull_request";
|
||||
process.env["GITHUB_SHA"] = mergeSha;
|
||||
|
||||
sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.resolves(`commit ${mergeSha}\nparent ${baseOid}\nparent ${headOid}\n`);
|
||||
|
||||
const result = await gitUtils.determineBaseBranchHeadCommitOid(__dirname);
|
||||
t.deepEqual(result, baseOid);
|
||||
},
|
||||
);
|
||||
|
||||
test.serial("decodeGitFilePath unquoted strings", async (t) => {
|
||||
t.deepEqual(gitUtils.decodeGitFilePath("foo"), "foo");
|
||||
t.deepEqual(gitUtils.decodeGitFilePath("foo bar"), "foo bar");
|
||||
@@ -436,6 +441,64 @@ test.serial("getFileOidsUnderPath handles quoted paths", async (t) => {
|
||||
});
|
||||
});
|
||||
|
||||
test.serial("getFileOidsUnderPath handles SHA-256 OIDs", async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
const sha256OidA =
|
||||
"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2c0d4b7e8f9a1234567890ab";
|
||||
const sha256OidB =
|
||||
"aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899";
|
||||
|
||||
sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.callsFake(async (_cwd: any, args: any) => {
|
||||
if (args[0] === "rev-parse") {
|
||||
return `${tmpDir}\n`;
|
||||
}
|
||||
return (
|
||||
`100644 ${sha256OidA} 0\tlib/sha256-file-a.js\n` +
|
||||
`100644 ${sha256OidB} 0\tsrc/sha256-file-b.ts`
|
||||
);
|
||||
});
|
||||
|
||||
const result = await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
|
||||
t.deepEqual(result, {
|
||||
"lib/sha256-file-a.js": sha256OidA,
|
||||
"src/sha256-file-b.ts": sha256OidB,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.serial(
|
||||
"getFileOidsUnderPath rejects OIDs of unsupported length",
|
||||
async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
// 50-char OID: not a valid SHA-1 (40) or SHA-256 (64) length. The regex
|
||||
// must not accept this even though every character is a valid hex digit.
|
||||
const invalidLine =
|
||||
"100644 30d998ded095371488be3a729eb61d86ed721a1830d998ded0 0\tlib/bad.js";
|
||||
sinon
|
||||
.stub(gitUtils as any, "runGitCommand")
|
||||
.callsFake(async (_cwd: any, args: any) => {
|
||||
if (args[0] === "rev-parse") {
|
||||
return `${tmpDir}\n`;
|
||||
}
|
||||
return invalidLine;
|
||||
});
|
||||
|
||||
await t.throwsAsync(
|
||||
async () => {
|
||||
await gitUtils.getFileOidsUnderPath("/fake/path");
|
||||
},
|
||||
{
|
||||
instanceOf: Error,
|
||||
message: `Unexpected "git ls-files" output: ${invalidLine}`,
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
test.serial("getFileOidsUnderPath handles empty output", async (t) => {
|
||||
await withTmpDir(async (tmpDir) => {
|
||||
sinon
|
||||
|
||||
+6
-4
@@ -163,11 +163,12 @@ export const determineBaseBranchHeadCommitOid = async function (
|
||||
}
|
||||
}
|
||||
|
||||
// Let's confirm our assumptions: We had a merge commit and the parsed parent data looks correct
|
||||
// Let's confirm our assumptions: We had a merge commit and the parsed parent
|
||||
// data looks correct. OIDs are either 40 (SHA-1) or 64 (SHA-256) hex characters.
|
||||
if (
|
||||
commitOid === mergeSha &&
|
||||
headOid.length === 40 &&
|
||||
baseOid.length === 40
|
||||
(headOid.length === 40 || headOid.length === 64) &&
|
||||
(baseOid.length === 40 || baseOid.length === 64)
|
||||
) {
|
||||
return baseOid;
|
||||
}
|
||||
@@ -296,7 +297,8 @@ export const getFileOidsUnderPath = async function (
|
||||
// 100644 4c51bc1d9e86cd86e01b0f340cb8ce095c33b283 0\tsrc/git-utils.test.ts
|
||||
// 100644 6b792ea543ce75d7a8a03df591e3c85311ecb64f 0\tsrc/git-utils.ts
|
||||
// The fields are: <mode> <oid> <stage>\t<path>
|
||||
const regex = /^[0-9]+ ([0-9a-f]{40}) [0-9]+\t(.+)$/;
|
||||
// The OID is either 40 (SHA-1) or 64 (SHA-256) hex characters.
|
||||
const regex = /^[0-9]+ ([0-9a-f]{40}|[0-9a-f]{64}) [0-9]+\t(.+)$/;
|
||||
for (const line of stdout.split("\n")) {
|
||||
if (line) {
|
||||
const match = line.match(regex);
|
||||
|
||||
@@ -207,7 +207,7 @@ function getJobStatusFromEnvironment(): JobStatus | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
@@ -222,5 +222,3 @@ async function runWrapper() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
+3
-47
@@ -37,11 +37,6 @@ import {
|
||||
makeDiagnostic,
|
||||
makeTelemetryDiagnostic,
|
||||
} from "./diagnostics";
|
||||
import {
|
||||
getDiffInformedAnalysisBranches,
|
||||
getPullRequestEditedDiffRanges,
|
||||
writeDiffRangesJsonFile,
|
||||
} from "./diff-informed-analysis-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, FeatureEnablement, initFeatures } from "./feature-flags";
|
||||
import {
|
||||
@@ -281,7 +276,7 @@ async function run(startedAt: Date) {
|
||||
// successful, the results are cached so that we don't duplicate the work in normal runs.
|
||||
let analysisKinds: AnalysisKind[] | undefined;
|
||||
try {
|
||||
analysisKinds = await getAnalysisKinds(logger);
|
||||
analysisKinds = await getAnalysisKinds(logger, features);
|
||||
} catch (err) {
|
||||
logger.debug(
|
||||
`Failed to parse analysis kinds for 'starting' status report: ${getErrorMessage(err)}`,
|
||||
@@ -353,7 +348,7 @@ async function run(startedAt: Date) {
|
||||
}
|
||||
}
|
||||
|
||||
analysisKinds = await getAnalysisKinds(logger);
|
||||
analysisKinds = await getAnalysisKinds(logger, features);
|
||||
const debugMode = getOptionalInput("debug") === "true" || core.isDebug();
|
||||
const repositoryProperties = repositoryPropertiesResult.orElse({});
|
||||
const fileCoverageResult = await getFileCoverageInformationEnabled(
|
||||
@@ -434,7 +429,6 @@ async function run(startedAt: Date) {
|
||||
}
|
||||
|
||||
await checkInstallPython311(config.languages, codeql);
|
||||
await computeAndPersistDiffRanges(codeql, features, logger);
|
||||
} catch (unwrappedError) {
|
||||
const error = wrapError(unwrappedError);
|
||||
core.setFailed(error.message);
|
||||
@@ -830,42 +824,6 @@ async function loadRepositoryProperties(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and persist diff ranges when diff-informed analysis is enabled
|
||||
* (feature flag + PR context). This writes the standard pr-diff-range.json
|
||||
* file for later reuse in the analyze step. Failures are logged but non-fatal.
|
||||
*/
|
||||
async function computeAndPersistDiffRanges(
|
||||
codeql: CodeQL,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
): Promise<void> {
|
||||
await withGroupAsync("Computing PR diff ranges", async () => {
|
||||
try {
|
||||
const branches = await getDiffInformedAnalysisBranches(
|
||||
codeql,
|
||||
features,
|
||||
logger,
|
||||
);
|
||||
if (!branches) {
|
||||
return;
|
||||
}
|
||||
const ranges = await getPullRequestEditedDiffRanges(branches, logger);
|
||||
if (ranges === undefined) {
|
||||
return;
|
||||
}
|
||||
writeDiffRangesJsonFile(logger, ranges);
|
||||
const distinctFiles = new Set(ranges.map((r) => r.path)).size;
|
||||
logger.info(
|
||||
`Persisted ${ranges.length} diff range(s) across ${distinctFiles} file(s).`,
|
||||
);
|
||||
} catch (e) {
|
||||
logger.warning(
|
||||
`Failed to compute and persist PR diff ranges: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
async function recordZstdAvailability(
|
||||
config: configUtils.Config,
|
||||
zstdAvailability: ZstdAvailability,
|
||||
@@ -880,7 +838,7 @@ async function recordZstdAvailability(
|
||||
);
|
||||
}
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
@@ -896,5 +854,3 @@ async function runWrapper() {
|
||||
}
|
||||
await checkForTimeout();
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
+16
-35
@@ -80,65 +80,46 @@ const testDownloadOverlayBaseDatabaseFromCache = makeMacro({
|
||||
await fs.promises.writeFile(baseDatabaseOidsFile, JSON.stringify({}));
|
||||
}
|
||||
|
||||
const stubs: sinon.SinonStub[] = [];
|
||||
sinon.stub(apiClient, "getAutomationID").resolves("test-automation-id/");
|
||||
|
||||
const getAutomationIDStub = sinon
|
||||
.stub(apiClient, "getAutomationID")
|
||||
.resolves("test-automation-id/");
|
||||
stubs.push(getAutomationIDStub);
|
||||
|
||||
const isInTestModeStub = sinon
|
||||
.stub(utils, "isInTestMode")
|
||||
.returns(testCase.isInTestMode);
|
||||
stubs.push(isInTestModeStub);
|
||||
sinon.stub(utils, "isInTestMode").returns(testCase.isInTestMode);
|
||||
|
||||
if (testCase.restoreCacheResult instanceof Error) {
|
||||
const restoreCacheStub = sinon
|
||||
sinon
|
||||
.stub(actionsCache, "restoreCache")
|
||||
.rejects(testCase.restoreCacheResult);
|
||||
stubs.push(restoreCacheStub);
|
||||
} else {
|
||||
const restoreCacheStub = sinon
|
||||
sinon
|
||||
.stub(actionsCache, "restoreCache")
|
||||
.resolves(testCase.restoreCacheResult);
|
||||
stubs.push(restoreCacheStub);
|
||||
}
|
||||
|
||||
const tryGetFolderBytesStub = sinon
|
||||
sinon
|
||||
.stub(utils, "tryGetFolderBytes")
|
||||
.resolves(testCase.tryGetFolderBytesSucceeds ? 1024 * 1024 : undefined);
|
||||
stubs.push(tryGetFolderBytesStub);
|
||||
|
||||
const codeql = mockCodeQLVersion(testCase.codeQLVersion);
|
||||
|
||||
if (testCase.resolveDatabaseOutput instanceof Error) {
|
||||
const resolveDatabaseStub = sinon
|
||||
sinon
|
||||
.stub(codeql, "resolveDatabase")
|
||||
.rejects(testCase.resolveDatabaseOutput);
|
||||
stubs.push(resolveDatabaseStub);
|
||||
} else {
|
||||
const resolveDatabaseStub = sinon
|
||||
sinon
|
||||
.stub(codeql, "resolveDatabase")
|
||||
.resolves(testCase.resolveDatabaseOutput);
|
||||
stubs.push(resolveDatabaseStub);
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await downloadOverlayBaseDatabaseFromCache(
|
||||
codeql,
|
||||
config,
|
||||
logger,
|
||||
);
|
||||
const result = await downloadOverlayBaseDatabaseFromCache(
|
||||
codeql,
|
||||
config,
|
||||
logger,
|
||||
);
|
||||
|
||||
if (expectDownloadSuccess) {
|
||||
t.truthy(result);
|
||||
} else {
|
||||
t.is(result, undefined);
|
||||
}
|
||||
} finally {
|
||||
for (const stub of stubs) {
|
||||
stub.restore();
|
||||
}
|
||||
if (expectDownloadSuccess) {
|
||||
t.truthy(result);
|
||||
} else {
|
||||
t.is(result, undefined);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -39,6 +39,15 @@ export enum OverlayDisabledReason {
|
||||
NotPullRequestOrDefaultBranch = "not-pull-request-or-default-branch",
|
||||
/** The top-level overlay analysis feature flag is not enabled. */
|
||||
OverallFeatureNotEnabled = "overall-feature-not-enabled",
|
||||
/**
|
||||
* Overlay analysis was selected for a pull request, but diff-informed
|
||||
* analysis was not enabled for the run (for example, because the
|
||||
* `DiffInformedQueries` feature flag is off, the GHES version is too old,
|
||||
* or the PR diff ranges could not be computed). Overlay analysis has only
|
||||
* been validated in combination with diff-informed analysis, so we fall
|
||||
* back to a non-overlay analysis in this case.
|
||||
*/
|
||||
DiffInformedAnalysisNotEnabled = "diff-informed-analysis-not-enabled",
|
||||
/** Overlay analysis was skipped because it previously failed with similar hardware resources. */
|
||||
SkippedDueToCachedStatus = "skipped-due-to-cached-status",
|
||||
/** Disk usage could not be determined during the overlay status check. */
|
||||
|
||||
+16
-56
@@ -50,31 +50,21 @@ test.serial(
|
||||
"modified.js": "ddd444", // Changed OID
|
||||
"added.js": "eee555", // New file
|
||||
};
|
||||
const getFileOidsStubForOverlay = sinon
|
||||
.stub(gitUtils, "getFileOidsUnderPath")
|
||||
.resolves(currentOids);
|
||||
sinon.stub(gitUtils, "getFileOidsUnderPath").resolves(currentOids);
|
||||
|
||||
// Write the overlay changes file, which uses the mocked overlay OIDs
|
||||
// and the base database OIDs file
|
||||
const diffRangeFilePath = path.join(tempDir, "pr-diff-range.json");
|
||||
const getTempDirStub = sinon
|
||||
.stub(actionsUtil, "getTemporaryDirectory")
|
||||
.returns(tempDir);
|
||||
const getDiffRangesStub = sinon
|
||||
sinon.stub(actionsUtil, "getTemporaryDirectory").returns(tempDir);
|
||||
sinon
|
||||
.stub(actionsUtil, "getDiffRangesJsonFilePath")
|
||||
.returns(diffRangeFilePath);
|
||||
const getGitRootStub = sinon
|
||||
.stub(gitUtils, "getGitRoot")
|
||||
.resolves(sourceRoot);
|
||||
sinon.stub(gitUtils, "getGitRoot").resolves(sourceRoot);
|
||||
const changesFilePath = await writeOverlayChangesFile(
|
||||
config,
|
||||
sourceRoot,
|
||||
logger,
|
||||
);
|
||||
getFileOidsStubForOverlay.restore();
|
||||
getTempDirStub.restore();
|
||||
getDiffRangesStub.restore();
|
||||
getGitRootStub.restore();
|
||||
|
||||
const fileContent = await fs.promises.readFile(changesFilePath, "utf-8");
|
||||
const parsedContent = JSON.parse(fileContent) as { changes: string[] };
|
||||
@@ -128,20 +118,14 @@ test.serial(
|
||||
"modified.js": "ddd444", // Changed OID
|
||||
"reverted.js": "eee555", // Same OID as base -- not detected by OID comparison
|
||||
};
|
||||
const getFileOidsStubForOverlay = sinon
|
||||
.stub(gitUtils, "getFileOidsUnderPath")
|
||||
.resolves(currentOids);
|
||||
sinon.stub(gitUtils, "getFileOidsUnderPath").resolves(currentOids);
|
||||
|
||||
const diffRangeFilePath = path.join(tempDir, "pr-diff-range.json");
|
||||
const getTempDirStub = sinon
|
||||
.stub(actionsUtil, "getTemporaryDirectory")
|
||||
.returns(tempDir);
|
||||
const getDiffRangesStub = sinon
|
||||
sinon.stub(actionsUtil, "getTemporaryDirectory").returns(tempDir);
|
||||
sinon
|
||||
.stub(actionsUtil, "getDiffRangesJsonFilePath")
|
||||
.returns(diffRangeFilePath);
|
||||
const getGitRootStub = sinon
|
||||
.stub(gitUtils, "getGitRoot")
|
||||
.resolves(sourceRoot);
|
||||
sinon.stub(gitUtils, "getGitRoot").resolves(sourceRoot);
|
||||
|
||||
// Write a pr-diff-range.json file with diff ranges including
|
||||
// "reverted.js" (unchanged OIDs) and "modified.js" (already in OID changes)
|
||||
@@ -159,10 +143,6 @@ test.serial(
|
||||
sourceRoot,
|
||||
logger,
|
||||
);
|
||||
getFileOidsStubForOverlay.restore();
|
||||
getTempDirStub.restore();
|
||||
getDiffRangesStub.restore();
|
||||
getGitRootStub.restore();
|
||||
|
||||
const fileContent = await fs.promises.readFile(changesFilePath, "utf-8");
|
||||
const parsedContent = JSON.parse(fileContent) as { changes: string[] };
|
||||
@@ -208,20 +188,14 @@ test.serial(
|
||||
"unchanged.js": "aaa111",
|
||||
"modified.js": "ddd444",
|
||||
};
|
||||
const getFileOidsStubForOverlay = sinon
|
||||
.stub(gitUtils, "getFileOidsUnderPath")
|
||||
.resolves(currentOids);
|
||||
sinon.stub(gitUtils, "getFileOidsUnderPath").resolves(currentOids);
|
||||
|
||||
const diffRangeFilePath = path.join(tempDir, "pr-diff-range.json");
|
||||
const getTempDirStub = sinon
|
||||
.stub(actionsUtil, "getTemporaryDirectory")
|
||||
.returns(tempDir);
|
||||
const getDiffRangesStub = sinon
|
||||
sinon.stub(actionsUtil, "getTemporaryDirectory").returns(tempDir);
|
||||
sinon
|
||||
.stub(actionsUtil, "getDiffRangesJsonFilePath")
|
||||
.returns(diffRangeFilePath);
|
||||
const getGitRootStub = sinon
|
||||
.stub(gitUtils, "getGitRoot")
|
||||
.resolves(sourceRoot);
|
||||
sinon.stub(gitUtils, "getGitRoot").resolves(sourceRoot);
|
||||
|
||||
// No pr-diff-range.json file exists - should work the same as before
|
||||
const changesFilePath = await writeOverlayChangesFile(
|
||||
@@ -229,10 +203,6 @@ test.serial(
|
||||
sourceRoot,
|
||||
logger,
|
||||
);
|
||||
getFileOidsStubForOverlay.restore();
|
||||
getTempDirStub.restore();
|
||||
getDiffRangesStub.restore();
|
||||
getGitRootStub.restore();
|
||||
|
||||
const fileContent = await fs.promises.readFile(changesFilePath, "utf-8");
|
||||
const parsedContent = JSON.parse(fileContent) as { changes: string[] };
|
||||
@@ -281,21 +251,15 @@ test.serial(
|
||||
"app.js": "aaa111",
|
||||
"lib/util.js": "bbb222",
|
||||
};
|
||||
const getFileOidsStubForOverlay = sinon
|
||||
.stub(gitUtils, "getFileOidsUnderPath")
|
||||
.resolves(currentOids);
|
||||
sinon.stub(gitUtils, "getFileOidsUnderPath").resolves(currentOids);
|
||||
|
||||
const diffRangeFilePath = path.join(tempDir, "pr-diff-range.json");
|
||||
const getTempDirStub = sinon
|
||||
.stub(actionsUtil, "getTemporaryDirectory")
|
||||
.returns(tempDir);
|
||||
const getDiffRangesStub = sinon
|
||||
sinon.stub(actionsUtil, "getTemporaryDirectory").returns(tempDir);
|
||||
sinon
|
||||
.stub(actionsUtil, "getDiffRangesJsonFilePath")
|
||||
.returns(diffRangeFilePath);
|
||||
// getGitRoot returns the repo root (parent of sourceRoot)
|
||||
const getGitRootStub = sinon
|
||||
.stub(gitUtils, "getGitRoot")
|
||||
.resolves(repoRoot);
|
||||
sinon.stub(gitUtils, "getGitRoot").resolves(repoRoot);
|
||||
|
||||
// Diff ranges use repo-root-relative paths (as returned by the GitHub compare API)
|
||||
await fs.promises.writeFile(
|
||||
@@ -312,10 +276,6 @@ test.serial(
|
||||
sourceRoot,
|
||||
logger,
|
||||
);
|
||||
getFileOidsStubForOverlay.restore();
|
||||
getTempDirStub.restore();
|
||||
getDiffRangesStub.restore();
|
||||
getGitRootStub.restore();
|
||||
|
||||
const fileContent = await fs.promises.readFile(changesFilePath, "utf-8");
|
||||
const parsedContent = JSON.parse(fileContent) as { changes: string[] };
|
||||
|
||||
@@ -117,7 +117,7 @@ async function run(startedAt: Date) {
|
||||
}
|
||||
}
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
@@ -137,5 +137,3 @@ async function runWrapper() {
|
||||
}
|
||||
await checkForTimeout();
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
@@ -144,7 +144,7 @@ async function run(startedAt: Date): Promise<void> {
|
||||
const rawLanguages = getRawLanguagesNoAutodetect(
|
||||
getOptionalInput("languages"),
|
||||
);
|
||||
const analysisKinds = await getAnalysisKinds(logger);
|
||||
const analysisKinds = await getAnalysisKinds(logger, features);
|
||||
const initCodeQLResult = await initCodeQL(
|
||||
getOptionalInput("tools"),
|
||||
apiDetails,
|
||||
@@ -196,7 +196,7 @@ async function run(startedAt: Date): Promise<void> {
|
||||
}
|
||||
|
||||
/** Run the action and catch any unhandled errors. */
|
||||
async function runWrapper(): Promise<void> {
|
||||
export async function runWrapper(): Promise<void> {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
@@ -212,5 +212,3 @@ async function runWrapper(): Promise<void> {
|
||||
}
|
||||
await checkForTimeout();
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
@@ -12,7 +12,7 @@ import { uploadArtifacts } from "./debug-artifacts";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { checkGitHubVersionInRange, getErrorMessage } from "./util";
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
// To capture errors appropriately, keep as much code within the try-catch as
|
||||
// possible, and only use safe functions outside.
|
||||
|
||||
@@ -62,5 +62,3 @@ async function runWrapper() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
@@ -128,7 +128,7 @@ async function run(startedAt: Date) {
|
||||
}
|
||||
}
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
|
||||
@@ -204,5 +204,3 @@ async function startProxy(
|
||||
|
||||
return { host, port, cert: config.ca.cert, registries: registry_urls };
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
Binary file not shown.
@@ -12,7 +12,7 @@ import { EnvVar } from "./environment";
|
||||
import { getActionsLogger, withGroup } from "./logging";
|
||||
import { checkGitHubVersionInRange, getErrorMessage } from "./util";
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
// To capture errors appropriately, keep as much code within the try-catch as
|
||||
// possible, and only use safe functions outside.
|
||||
|
||||
@@ -48,5 +48,3 @@ async function runWrapper() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
@@ -165,7 +165,7 @@ async function run(startedAt: Date) {
|
||||
}
|
||||
}
|
||||
|
||||
async function runWrapper() {
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
@@ -182,5 +182,3 @@ async function runWrapper() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void runWrapper();
|
||||
|
||||
+1
-4
@@ -418,9 +418,7 @@ for (const [
|
||||
`checkActionVersion ${reportErrorDescription} for ${versionsDescription}`,
|
||||
async (t) => {
|
||||
const warningSpy = sinon.spy(core, "warning");
|
||||
const versionStub = sinon
|
||||
.stub(api, "getGitHubVersion")
|
||||
.resolves(githubVersion);
|
||||
sinon.stub(api, "getGitHubVersion").resolves(githubVersion);
|
||||
|
||||
// call checkActionVersion twice and assert below that warning is reported only once
|
||||
util.checkActionVersion(version, await api.getGitHubVersion());
|
||||
@@ -437,7 +435,6 @@ for (const [
|
||||
} else {
|
||||
t.false(warningSpy.called);
|
||||
}
|
||||
versionStub.restore();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user