Merge branch 'main' into redsun82/skip-sarif-upload

This commit is contained in:
Paolo Tranquilli
2025-10-07 17:17:13 +02:00
23 changed files with 1219 additions and 509 deletions

View File

@@ -247,9 +247,14 @@ export function isSelfHostedRunner() {
return process.env.RUNNER_ENVIRONMENT === "self-hosted";
}
/** Determines whether the workflow trigger is `dynamic`. */
export function isDynamicWorkflow(): boolean {
return getWorkflowEventName() === "dynamic";
}
/** Determines whether we are running in default setup. */
export function isDefaultSetup(): boolean {
return getWorkflowEventName() === "dynamic";
return isDynamicWorkflow();
}
export function prettyPrintInvocation(cmd: string, args: string[]): string {

View File

@@ -1,5 +1,6 @@
import * as path from "path";
import * as toolcache from "@actions/tool-cache";
import test from "ava";
import * as sinon from "sinon";
@@ -254,6 +255,117 @@ test("setupCodeQLBundle logs the CodeQL CLI version being used when asked to dow
});
});
test("getCodeQLSource correctly returns latest version from toolcache when tools == toolcache", async (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
process.env["GITHUB_EVENT_NAME"] = "dynamic";
const latestToolcacheVersion = "3.2.1";
const latestVersionPath = "/path/to/latest";
const testVersions = ["2.3.1", latestToolcacheVersion, "1.2.3"];
const findAllVersionsStub = sinon
.stub(toolcache, "findAllVersions")
.returns(testVersions);
const findStub = sinon.stub(toolcache, "find");
findStub
.withArgs("CodeQL", latestToolcacheVersion)
.returns(latestVersionPath);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const source = await setupCodeql.getCodeQLSource(
"toolcache",
SAMPLE_DEFAULT_CLI_VERSION,
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
logger,
);
// Check that the toolcache functions were called with the expected arguments
t.assert(
findAllVersionsStub.calledOnceWith("CodeQL"),
`toolcache.findAllVersions("CodeQL") wasn't called`,
);
t.assert(
findStub.calledOnceWith("CodeQL", latestToolcacheVersion),
`toolcache.find("CodeQL", ${latestToolcacheVersion}) wasn't called`,
);
// Check that `sourceType` and `toolsVersion` match expectations.
t.is(source.sourceType, "toolcache");
t.is(source.toolsVersion, latestToolcacheVersion);
// Check that key messages we would expect to find in the log are present.
const expectedMessages: string[] = [
`Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: toolcache'.`,
`CLI version ${latestToolcacheVersion} is the latest version in the toolcache.`,
`Using CodeQL CLI version ${latestToolcacheVersion} from toolcache at ${latestVersionPath}`,
];
for (const expectedMessage of expectedMessages) {
t.assert(
loggedMessages.some(
(msg) =>
typeof msg.message === "string" &&
msg.message.includes(expectedMessage),
),
`Expected '${expectedMessage}' in the logger output, but didn't find it.`,
);
}
});
});
test("getCodeQLSource falls back to downloading the CLI if the toolcache doesn't have a CodeQL CLI when tools == toolcache", async (t) => {
const loggedMessages: LoggedMessage[] = [];
const logger = getRecordingLogger(loggedMessages);
process.env["GITHUB_EVENT_NAME"] = "dynamic";
const testVersions = [];
const findAllVersionsStub = sinon
.stub(toolcache, "findAllVersions")
.returns(testVersions);
await withTmpDir(async (tmpDir) => {
setupActionsVars(tmpDir, tmpDir);
const source = await setupCodeql.getCodeQLSource(
"toolcache",
SAMPLE_DEFAULT_CLI_VERSION,
SAMPLE_DOTCOM_API_DETAILS,
GitHubVariant.DOTCOM,
false,
logger,
);
// Check that the toolcache functions were called with the expected arguments
t.assert(
findAllVersionsStub.calledWith("CodeQL"),
`toolcache.findAllVersions("CodeQL") wasn't called`,
);
// Check that `sourceType` and `toolsVersion` match expectations.
t.is(source.sourceType, "download");
t.is(source.toolsVersion, SAMPLE_DEFAULT_CLI_VERSION.cliVersion);
// Check that key messages we would expect to find in the log are present.
const expectedMessages: string[] = [
`Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: toolcache'.`,
`Found no CodeQL CLI in the toolcache, ignoring 'tools: toolcache'...`,
];
for (const expectedMessage of expectedMessages) {
t.assert(
loggedMessages.some(
(msg) =>
typeof msg.message === "string" &&
msg.message.includes(expectedMessage),
),
`Expected '${expectedMessage}' in the logger output, but didn't find it.`,
);
}
});
});
test('tryGetTagNameFromUrl extracts the right tag name for a repo name containing "codeql-bundle"', (t) => {
t.is(
setupCodeql.tryGetTagNameFromUrl(
@@ -263,3 +375,15 @@ test('tryGetTagNameFromUrl extracts the right tag name for a repo name containin
"codeql-bundle-v2.19.0",
);
});
test("getLatestToolcacheVersion returns undefined if there are no CodeQL CLIs in the toolcache", (t) => {
sinon.stub(toolcache, "findAllVersions").returns([]);
t.is(setupCodeql.getLatestToolcacheVersion(getRunnerLogger(true)), undefined);
});
test("getLatestToolcacheVersion returns latest version in the toolcache", (t) => {
const testVersions = ["2.3.1", "3.2.1", "1.2.3"];
sinon.stub(toolcache, "findAllVersions").returns(testVersions);
t.is(setupCodeql.getLatestToolcacheVersion(getRunnerLogger(true)), "3.2.1");
});

View File

@@ -7,7 +7,7 @@ import { default as deepEqual } from "fast-deep-equal";
import * as semver from "semver";
import { v4 as uuidV4 } from "uuid";
import { isRunningLocalAction } from "./actions-util";
import { isDynamicWorkflow, isRunningLocalAction } from "./actions-util";
import * as api from "./api-client";
import * as defaults from "./defaults.json";
import {
@@ -38,6 +38,7 @@ const CODEQL_NIGHTLIES_REPOSITORY_NAME = "codeql-cli-nightlies";
const CODEQL_BUNDLE_VERSION_ALIAS: string[] = ["linked", "latest"];
const CODEQL_NIGHTLY_TOOLS_INPUTS = ["nightly", "nightly-latest"];
const CODEQL_TOOLCACHE_INPUT = "toolcache";
function getCodeQLBundleExtension(
compressionMethod: tar.CompressionMethod,
@@ -346,6 +347,44 @@ export async function getCodeQLSource(
"`tools: latest` has been renamed to `tools: linked`, but the old name is still supported. No action is required.",
);
}
} else if (
toolsInput !== undefined &&
toolsInput === CODEQL_TOOLCACHE_INPUT
) {
let latestToolcacheVersion: string | undefined;
// We only allow `toolsInput === "toolcache"` for `dynamic` events. In general, using `toolsInput === "toolcache"`
// can lead to alert wobble and so it shouldn't be used for an analysis where results are intended to be uploaded.
// We also allow this in test mode.
const allowToolcacheValue = isDynamicWorkflow() || util.isInTestMode();
if (allowToolcacheValue) {
// If `toolsInput === "toolcache"`, try to find the latest version of the CLI that's available in the toolcache
// and use that. We perform this check here since we can set `cliVersion` directly and don't want to default to
// the linked version.
logger.info(
`Attempting to use the latest CodeQL CLI version in the toolcache, as requested by 'tools: ${toolsInput}'.`,
);
latestToolcacheVersion = getLatestToolcacheVersion(logger);
if (latestToolcacheVersion) {
cliVersion = latestToolcacheVersion;
}
}
if (latestToolcacheVersion === undefined) {
if (allowToolcacheValue) {
logger.info(
`Found no CodeQL CLI in the toolcache, ignoring 'tools: ${toolsInput}'...`,
);
} else {
logger.warning(
`Ignoring 'tools: ${toolsInput}' because the workflow was not triggered dynamically.`,
);
}
cliVersion = defaultCliVersion.cliVersion;
tagName = defaultCliVersion.tagName;
}
} else if (toolsInput !== undefined) {
// If a tools URL was provided, then use that.
tagName = tryGetTagNameFromUrl(toolsInput, logger);
@@ -816,9 +855,38 @@ async function getNightlyToolsUrl(logger: Logger) {
}
}
/**
* Gets the latest version of the CodeQL CLI that is available in the toolcache, or `undefined`
* if no CodeQL CLI is available in the toolcache.
*
* @param logger The logger to use.
* @returns The latest version of the CodeQL CLI that is available in the toolcache, or `undefined` if there is none.
*/
export function getLatestToolcacheVersion(logger: Logger): string | undefined {
const allVersions = toolcache
.findAllVersions("CodeQL")
.sort((a, b) => semver.compare(b, a));
logger.debug(
`Found the following versions of the CodeQL tools in the toolcache: ${JSON.stringify(
allVersions,
)}.`,
);
if (allVersions.length > 0) {
const latestToolcacheVersion = allVersions[0];
logger.info(
`CLI version ${latestToolcacheVersion} is the latest version in the toolcache.`,
);
return latestToolcacheVersion;
}
return undefined;
}
function isReservedToolsValue(tools: string): boolean {
return (
CODEQL_BUNDLE_VERSION_ALIAS.includes(tools) ||
CODEQL_NIGHTLY_TOOLS_INPUTS.includes(tools)
CODEQL_NIGHTLY_TOOLS_INPUTS.includes(tools) ||
tools === CODEQL_TOOLCACHE_INPUT
);
}

View File

@@ -352,14 +352,14 @@ async function uploadPayload(
payload: any,
repositoryNwo: RepositoryNwo,
logger: Logger,
target: analyses.SARIF_UPLOAD_ENDPOINT,
analysis: analyses.AnalysisConfig,
): Promise<string> {
logger.info("Uploading results");
if (util.shouldSkipSarifUpload()) {
const payloadSaveFile = path.join(
actionsUtil.getTemporaryDirectory(),
"payload.json",
`payload-${analysis.kind}.json`,
);
logger.info(
`SARIF upload disabled by an environment variable. Saving to ${payloadSaveFile}`,
@@ -372,7 +372,7 @@ async function uploadPayload(
const client = api.getApiClient();
try {
const response = await client.request(target, {
const response = await client.request(analysis.target, {
owner: repositoryNwo.owner,
repo: repositoryNwo.repo,
data: payload,
@@ -806,7 +806,7 @@ export async function uploadSpecifiedFiles(
payload,
getRepositoryNwo(),
logger,
uploadTarget.target,
uploadTarget,
);
logger.endGroup();