mirror of
https://github.com/github/codeql-action.git
synced 2026-10-14 14:51:36 +00:00
Compare commits
18 Commits
mbg/fs-abs
...
default-se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb94de7c5c | ||
|
|
9645acc2ff | ||
|
|
6a9f7e7fc3 | ||
|
|
67f44bc916 | ||
|
|
26c6f91e6b | ||
|
|
e3aeabd8f1 | ||
|
|
be78e02f2d | ||
|
|
bed4cc0475 | ||
|
|
ac8654a8f2 | ||
|
|
ecfc96e0a9 | ||
|
|
409be01136 | ||
|
|
f4a15a13fd | ||
|
|
4fdbb71b54 | ||
|
|
5136039854 | ||
|
|
19445ddfed | ||
|
|
317f264e1f | ||
|
|
309f6af89b | ||
|
|
cf8f7b9787 |
191
lib/entry-points.js
generated
191
lib/entry-points.js
generated
@@ -21656,8 +21656,8 @@ var require_core = __commonJS({
|
||||
exports2.warning = warning14;
|
||||
exports2.notice = notice;
|
||||
exports2.info = info8;
|
||||
exports2.startGroup = startGroup4;
|
||||
exports2.endGroup = endGroup4;
|
||||
exports2.startGroup = startGroup3;
|
||||
exports2.endGroup = endGroup3;
|
||||
exports2.group = group;
|
||||
exports2.saveState = saveState3;
|
||||
exports2.getState = getState3;
|
||||
@@ -21755,20 +21755,20 @@ Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
|
||||
function info8(message) {
|
||||
process.stdout.write(message + os7.EOL);
|
||||
}
|
||||
function startGroup4(name) {
|
||||
function startGroup3(name) {
|
||||
(0, command_1.issue)("group", name);
|
||||
}
|
||||
function endGroup4() {
|
||||
function endGroup3() {
|
||||
(0, command_1.issue)("endgroup");
|
||||
}
|
||||
function group(name, fn) {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
startGroup4(name);
|
||||
startGroup3(name);
|
||||
let result;
|
||||
try {
|
||||
result = yield fn();
|
||||
} finally {
|
||||
endGroup4();
|
||||
endGroup3();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
@@ -145686,22 +145686,22 @@ Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
|
||||
process.stdout.write(message + os7.EOL);
|
||||
}
|
||||
exports2.info = info8;
|
||||
function startGroup4(name) {
|
||||
function startGroup3(name) {
|
||||
(0, command_1.issue)("group", name);
|
||||
}
|
||||
exports2.startGroup = startGroup4;
|
||||
function endGroup4() {
|
||||
exports2.startGroup = startGroup3;
|
||||
function endGroup3() {
|
||||
(0, command_1.issue)("endgroup");
|
||||
}
|
||||
exports2.endGroup = endGroup4;
|
||||
exports2.endGroup = endGroup3;
|
||||
function group(name, fn) {
|
||||
return __awaiter2(this, void 0, void 0, function* () {
|
||||
startGroup4(name);
|
||||
startGroup3(name);
|
||||
let result;
|
||||
try {
|
||||
result = yield fn();
|
||||
} finally {
|
||||
endGroup4();
|
||||
endGroup3();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
@@ -166098,6 +166098,11 @@ var toolrunner = __toESM(require_toolrunner());
|
||||
var github = __toESM(require_github());
|
||||
var io2 = __toESM(require_io());
|
||||
|
||||
// src/config/action-config.ts
|
||||
function hasConfigKey(config, key) {
|
||||
return config[key] !== void 0;
|
||||
}
|
||||
|
||||
// src/environment.ts
|
||||
var core = __toESM(require_core());
|
||||
function getRequiredEnvVar(env, paramName) {
|
||||
@@ -170207,27 +170212,33 @@ function getWorkflowEvent(env = getEnv()) {
|
||||
);
|
||||
}
|
||||
}
|
||||
async function printDebugLogs(config) {
|
||||
async function printDebugLogs(logger2, config) {
|
||||
if (!hasConfigKey(config, "languages") || !hasConfigKey(config, "dbLocation")) {
|
||||
logger2.debug(
|
||||
`Not printing debug logs, because the configuration does not include the 'languages' or 'dbLocation' fields.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
for (const language of config.languages) {
|
||||
const databaseDirectory = getCodeQLDatabasePath(config, language);
|
||||
const logsDirectory = path2.join(databaseDirectory, "log");
|
||||
if (!doesDirectoryExist(logsDirectory)) {
|
||||
core4.info(`Directory ${logsDirectory} does not exist.`);
|
||||
logger2.info(`Directory ${logsDirectory} does not exist.`);
|
||||
continue;
|
||||
}
|
||||
const walkLogFiles = (dir) => {
|
||||
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
||||
if (entries.length === 0) {
|
||||
core4.info(`No debug logs found at directory ${logsDirectory}.`);
|
||||
logger2.info(`No debug logs found at directory ${logsDirectory}.`);
|
||||
}
|
||||
for (const entry of entries) {
|
||||
if (entry.isFile()) {
|
||||
const absolutePath = path2.resolve(dir, entry.name);
|
||||
core4.startGroup(
|
||||
logger2.startGroup(
|
||||
`CodeQL Debug Logs - ${language} - ${entry.name} from file at path ${absolutePath}`
|
||||
);
|
||||
process.stdout.write(fs2.readFileSync(absolutePath));
|
||||
core4.endGroup();
|
||||
logger2.info(fs2.readFileSync(absolutePath, "utf-8"));
|
||||
logger2.endGroup();
|
||||
} else if (entry.isDirectory()) {
|
||||
walkLogFiles(path2.resolve(dir, entry.name));
|
||||
}
|
||||
@@ -171401,9 +171412,14 @@ function getAddressString(address) {
|
||||
}
|
||||
|
||||
// src/status-report.ts
|
||||
var ACTION_DISPLAY_NAMES = {
|
||||
["finish" /* Analyze */]: "analyze",
|
||||
["init-post" /* InitPost */]: "init post"
|
||||
};
|
||||
function getDisplayActionName(actionName) {
|
||||
if (actionName === "finish" /* Analyze */) {
|
||||
return "analyze";
|
||||
const displayName = ACTION_DISPLAY_NAMES[actionName];
|
||||
if (displayName !== void 0) {
|
||||
return displayName;
|
||||
}
|
||||
return actionName;
|
||||
}
|
||||
@@ -175053,20 +175069,47 @@ function getLocalConfig(logger2, configFile, validateConfig) {
|
||||
function getPathToParsedConfigFile(tempDir) {
|
||||
return path11.join(tempDir, "config");
|
||||
}
|
||||
async function saveConfig(config, logger2) {
|
||||
function getPathToFailedConfigFile(tempDir) {
|
||||
return path11.join(tempDir, "failed-config");
|
||||
}
|
||||
function saveConfigFile(logger2, configFilePath, config) {
|
||||
const configString = JSON.stringify(config);
|
||||
const configFile = getPathToParsedConfigFile(config.tempDir);
|
||||
fs10.mkdirSync(path11.dirname(configFile), { recursive: true });
|
||||
fs10.writeFileSync(configFile, configString, "utf8");
|
||||
fs10.mkdirSync(path11.dirname(configFilePath), { recursive: true });
|
||||
fs10.writeFileSync(configFilePath, configString, "utf8");
|
||||
logger2.debug("Saved config:");
|
||||
logger2.debug(configString);
|
||||
}
|
||||
async function getConfig(tempDir, logger2) {
|
||||
const configFile = getPathToParsedConfigFile(tempDir);
|
||||
if (!fs10.existsSync(configFile)) {
|
||||
async function saveConfig(config, logger2) {
|
||||
const configFile = getPathToParsedConfigFile(config.tempDir);
|
||||
saveConfigFile(logger2, configFile, config);
|
||||
}
|
||||
function saveAbortedInitConfig(action, config) {
|
||||
if (action.name !== "init" /* Init */) {
|
||||
throw new Error(
|
||||
`Internal error: 'saveAbortedInitConfig' should only be called by '${"init" /* Init */}', but was called by '${action.name}'.`
|
||||
);
|
||||
}
|
||||
const failedConfig = {
|
||||
version: config.version,
|
||||
tempDir: config.tempDir,
|
||||
codeQLCmd: config.codeQLCmd,
|
||||
debugMode: config.debugMode,
|
||||
gitHubVersion: config.gitHubVersion,
|
||||
languages: config.languages,
|
||||
analysisKinds: config.analysisKinds
|
||||
};
|
||||
action.logger.debug(`Saving config for failed init step.`);
|
||||
saveConfigFile(
|
||||
action.logger,
|
||||
getPathToFailedConfigFile(config.tempDir),
|
||||
failedConfig
|
||||
);
|
||||
}
|
||||
function readConfigFile(logger2, configFilePath) {
|
||||
if (!fs10.existsSync(configFilePath)) {
|
||||
return void 0;
|
||||
}
|
||||
const configString = fs10.readFileSync(configFile, "utf8");
|
||||
const configString = fs10.readFileSync(configFilePath, "utf8");
|
||||
logger2.debug("Loaded config:");
|
||||
logger2.debug(configString);
|
||||
const config = JSON.parse(configString);
|
||||
@@ -175087,6 +175130,18 @@ async function getConfig(tempDir, logger2) {
|
||||
}
|
||||
return config;
|
||||
}
|
||||
async function getConfig(tempDir, logger2) {
|
||||
const configFile = getPathToParsedConfigFile(tempDir);
|
||||
return readConfigFile(logger2, configFile);
|
||||
}
|
||||
function getAbortedInitConfig(action, tempDir) {
|
||||
if (action.name !== "init-post" /* InitPost */) {
|
||||
throw new Error(
|
||||
`Internal error: 'getAbortedInitConfig' should only be called by '${"init-post" /* InitPost */}', but was called by '${action.name}'.`
|
||||
);
|
||||
}
|
||||
return readConfigFile(action.logger, getPathToFailedConfigFile(tempDir));
|
||||
}
|
||||
async function generateRegistries(registriesInput, tempDir, logger2) {
|
||||
const registries = parseRegistries(registriesInput);
|
||||
let registriesAuthTokens;
|
||||
@@ -186495,6 +186550,15 @@ async function run3(actionState) {
|
||||
}
|
||||
await checkInstallPython311(config.languages, codeql);
|
||||
} catch (unwrappedError) {
|
||||
if (config !== void 0) {
|
||||
try {
|
||||
saveAbortedInitConfig(actionState, config);
|
||||
} catch (configSaveError) {
|
||||
logger2.warning(
|
||||
`Unable to save configuration for '${"init-post" /* InitPost */}' step: ${getErrorMessage(configSaveError)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
const error3 = wrapError(unwrappedError);
|
||||
core19.setFailed(error3.message);
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
@@ -186923,14 +186987,14 @@ async function uploadFailureInfo(uploadAllAvailableDebugArtifacts, printDebugLog
|
||||
);
|
||||
}
|
||||
}
|
||||
if (config.debugMode) {
|
||||
if (config.debugMode && hasConfigKey(config, "dbLocation")) {
|
||||
logger2.info(
|
||||
"Debug mode is on. Uploading available database bundles and logs as Actions debugging artifacts..."
|
||||
);
|
||||
await uploadAllAvailableDebugArtifacts(codeql, config, logger2);
|
||||
await printDebugLogs2(config);
|
||||
await printDebugLogs2(logger2, config);
|
||||
}
|
||||
if (isSelfHostedRunner()) {
|
||||
if (isSelfHostedRunner() && config.dbLocation) {
|
||||
try {
|
||||
fs30.rmSync(config.dbLocation, {
|
||||
recursive: true,
|
||||
@@ -186945,6 +187009,10 @@ async function uploadFailureInfo(uploadAllAvailableDebugArtifacts, printDebugLog
|
||||
`Failed to clean up database cluster directory ${config.dbLocation}. Details: ${e}`
|
||||
);
|
||||
}
|
||||
} else if (config.dbLocation === void 0) {
|
||||
logger2.debug(
|
||||
"Skipping cleanup of database cluster directory since the configuration does not include a database path."
|
||||
);
|
||||
} else {
|
||||
logger2.debug(
|
||||
"Skipping cleanup of database cluster directory since we are running on a GitHub-hosted runner which will be automatically cleaned up."
|
||||
@@ -186968,6 +187036,11 @@ function isConclusiveJobStatus(jobStatus) {
|
||||
async function recordOverlayStatus(codeql, config, features, jobStatus, env, logger2) {
|
||||
if (config.overlayDatabaseMode !== "overlay-base" /* OverlayBase */ || env.getOptional("CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY" /* ANALYZE_DID_COMPLETE_SUCCESSFULLY */) === "true" || !await features.getValue("overlay_analysis_status_save" /* OverlayAnalysisStatusSave */)) {
|
||||
return;
|
||||
} else if (config.languages === void 0) {
|
||||
logger2.debug(
|
||||
"Not recording overlay status, because the configuration contains no 'languages'."
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!isConclusiveJobStatus(jobStatus) && !didCodeQlReportError(env)) {
|
||||
logger2.info(
|
||||
@@ -187070,15 +187143,19 @@ async function removeUploadedSarif(uploadFailedSarifResult, logger2) {
|
||||
}
|
||||
|
||||
// src/init-action-post.ts
|
||||
async function run4(startedAt) {
|
||||
const logger2 = getActionsLogger();
|
||||
async function run4(action) {
|
||||
const logger2 = action.logger;
|
||||
let config;
|
||||
let uploadFailedSarifResult;
|
||||
let dependencyCachingUsage;
|
||||
try {
|
||||
const jobStatus2 = getOptionalInput("job-status");
|
||||
restoreInputs(logger2);
|
||||
config = await getConfig(getTemporaryDirectory(), logger2);
|
||||
const tmpDir = getTemporaryDirectory();
|
||||
config = await getConfig(tmpDir, logger2);
|
||||
if (config === void 0) {
|
||||
config = getAbortedInitConfig(action, tmpDir);
|
||||
}
|
||||
const gitHubVersion = config?.gitHubVersion ?? await getGitHubVersion();
|
||||
checkGitHubVersionInRange(gitHubVersion, logger2);
|
||||
const repositoryNwo = getRepositoryNwo();
|
||||
@@ -187102,10 +187179,10 @@ async function run4(startedAt) {
|
||||
repositoryNwo,
|
||||
features,
|
||||
jobStatus2,
|
||||
getEnv(),
|
||||
action.env,
|
||||
logger2
|
||||
);
|
||||
if (await isAnalyzingDefaultBranch() && config.dependencyCachingEnabled !== "none" /* None */) {
|
||||
if (await isAnalyzingDefaultBranch() && hasConfigKey(config, "dependencyCachingEnabled") && config.dependencyCachingEnabled !== "none" /* None */) {
|
||||
dependencyCachingUsage = await getDependencyCacheUsage(logger2);
|
||||
}
|
||||
}
|
||||
@@ -187113,9 +187190,9 @@ async function run4(startedAt) {
|
||||
const error3 = wrapApiConfigurationError(wrapError(unwrappedError));
|
||||
core20.setFailed(error3.message);
|
||||
const statusReportBase2 = await createStatusReportBase(
|
||||
"init-post" /* InitPost */,
|
||||
action.name,
|
||||
getActionsStatus(error3),
|
||||
startedAt,
|
||||
action.startedAt,
|
||||
config,
|
||||
await checkDiskUsage(logger2),
|
||||
logger2,
|
||||
@@ -187127,12 +187204,12 @@ async function run4(startedAt) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
const jobStatus = getFinalJobStatus(config);
|
||||
const jobStatus = getFinalJobStatus(action, config);
|
||||
logger2.info(`CodeQL job status was ${getJobStatusDisplayName(jobStatus)}.`);
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
"init-post" /* InitPost */,
|
||||
action.name,
|
||||
"success",
|
||||
startedAt,
|
||||
action.startedAt,
|
||||
config,
|
||||
await checkDiskUsage(logger2),
|
||||
logger2
|
||||
@@ -187149,25 +187226,25 @@ async function run4(startedAt) {
|
||||
logger2.info("Status report sent for init-post step.");
|
||||
}
|
||||
}
|
||||
function getFinalJobStatus(config) {
|
||||
const existingJobStatus = getJobStatusFromEnvironment();
|
||||
function getFinalJobStatus(action, config) {
|
||||
const existingJobStatus = getJobStatusFromEnvironment(action.env);
|
||||
if (existingJobStatus !== void 0) {
|
||||
return existingJobStatus;
|
||||
}
|
||||
let jobStatus;
|
||||
if (process.env["CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY" /* ANALYZE_DID_COMPLETE_SUCCESSFULLY */] === "true") {
|
||||
exportEnvVar("CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, "JOB_STATUS_SUCCESS" /* SuccessStatus */);
|
||||
if (action.env.getOptional("CODEQL_ACTION_ANALYZE_DID_COMPLETE_SUCCESSFULLY" /* ANALYZE_DID_COMPLETE_SUCCESSFULLY */) === "true") {
|
||||
action.env.export("CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, "JOB_STATUS_SUCCESS" /* SuccessStatus */);
|
||||
jobStatus = "JOB_STATUS_SUCCESS" /* SuccessStatus */;
|
||||
} else if (config !== void 0) {
|
||||
jobStatus = "JOB_STATUS_CONFIGURATION_ERROR" /* ConfigErrorStatus */;
|
||||
} else {
|
||||
jobStatus = "JOB_STATUS_UNKNOWN" /* UnknownStatus */;
|
||||
}
|
||||
exportEnvVar("CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, jobStatus);
|
||||
action.env.export("CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */, jobStatus);
|
||||
return jobStatus;
|
||||
}
|
||||
function getJobStatusFromEnvironment() {
|
||||
const jobStatusFromEnvironment = process.env["CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */];
|
||||
function getJobStatusFromEnvironment(env) {
|
||||
const jobStatusFromEnvironment = env.getOptional("CODEQL_ACTION_JOB_STATUS" /* JOB_STATUS */);
|
||||
if (jobStatusFromEnvironment !== void 0) {
|
||||
if (Object.values(JobStatus).includes(jobStatusFromEnvironment)) {
|
||||
return jobStatusFromEnvironment;
|
||||
@@ -187176,20 +187253,12 @@ function getJobStatusFromEnvironment() {
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
var initPost = {
|
||||
name: "init-post" /* InitPost */,
|
||||
run: run4
|
||||
};
|
||||
async function runWrapper5() {
|
||||
const startedAt = /* @__PURE__ */ new Date();
|
||||
const logger2 = getActionsLogger();
|
||||
try {
|
||||
await run4(startedAt);
|
||||
} catch (error3) {
|
||||
core20.setFailed(`init post action failed: ${wrapError(error3).message}`);
|
||||
await sendUnhandledErrorStatusReport(
|
||||
"init-post" /* InitPost */,
|
||||
startedAt,
|
||||
error3,
|
||||
logger2
|
||||
);
|
||||
}
|
||||
return await runInActions(initPost);
|
||||
}
|
||||
|
||||
// src/resolve-environment-action.ts
|
||||
|
||||
@@ -19,6 +19,24 @@ import { getErrorMessage } from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
for (const [key, displayName] of Object.entries(
|
||||
statusReport.ACTION_DISPLAY_NAMES,
|
||||
)) {
|
||||
test(`getDisplayActionName - '${displayName}' instead of '${key}'`, (t) => {
|
||||
t.is(statusReport.getDisplayActionName(key as ActionName), displayName);
|
||||
});
|
||||
}
|
||||
|
||||
test("getDisplayActionName - returns action name", (t) => {
|
||||
for (const actionName of Object.values(ActionName)) {
|
||||
if (statusReport.ACTION_DISPLAY_NAMES[actionName]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
t.is(statusReport.getDisplayActionName(actionName), actionName);
|
||||
}
|
||||
});
|
||||
|
||||
interface RunInActionsTestOpts {
|
||||
runFn?: () => Promise<any>;
|
||||
expectedErrorMessage?: string;
|
||||
|
||||
@@ -7,12 +7,13 @@ import {
|
||||
isAnalyzingPullRequest,
|
||||
isDefaultSetup,
|
||||
isDynamicWorkflow,
|
||||
printDebugLogs,
|
||||
} from "./actions-util";
|
||||
import { computeAutomationID } from "./api-client";
|
||||
import { EnvVar } from "./environment";
|
||||
import { getRunnerLogger } from "./logging";
|
||||
import { setupTests } from "./testing-utils";
|
||||
import { initializeEnvironment } from "./util";
|
||||
import { RecordingLogger, setupTests } from "./testing-utils";
|
||||
import { initializeEnvironment, withTmpDir } from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
||||
@@ -100,6 +101,21 @@ test("computeAutomationID()", async (t) => {
|
||||
);
|
||||
});
|
||||
|
||||
test("printDebugLogs - doesn't throw when config doesn't have needed properties", async (t) => {
|
||||
const logger = new RecordingLogger();
|
||||
await t.notThrowsAsync(printDebugLogs(logger, {}));
|
||||
await t.notThrowsAsync(printDebugLogs(logger, { languages: [] }));
|
||||
await t.notThrowsAsync(printDebugLogs(logger, { dbLocation: "nowhere" }));
|
||||
});
|
||||
|
||||
test("printDebugLogs - doesn't throw when language db doesn't exist", async (t) =>
|
||||
withTmpDir(async (tmpDir) => {
|
||||
const logger = new RecordingLogger();
|
||||
await t.notThrowsAsync(
|
||||
printDebugLogs(logger, { dbLocation: tmpDir, languages: ["javascript"] }),
|
||||
);
|
||||
}));
|
||||
|
||||
test.serial("getPullRequestBranches() with pull request context", (t) => {
|
||||
withMockedContext(
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ import * as toolrunner from "@actions/exec/lib/toolrunner";
|
||||
import * as github from "@actions/github";
|
||||
import * as io from "@actions/io";
|
||||
|
||||
import type { Config } from "./config-utils";
|
||||
import { hasConfigKey, type Config } from "./config/action-config";
|
||||
import { Env, EnvVar, ActionsEnvVars, ReadOnlyEnv } from "./environment";
|
||||
import { Logger } from "./logging";
|
||||
import {
|
||||
@@ -131,28 +131,38 @@ export function getWorkflowEvent(env: Env = getEnv()): any {
|
||||
}
|
||||
}
|
||||
|
||||
export async function printDebugLogs(config: Config) {
|
||||
export async function printDebugLogs(logger: Logger, config: Partial<Config>) {
|
||||
if (
|
||||
!hasConfigKey(config, "languages") ||
|
||||
!hasConfigKey(config, "dbLocation")
|
||||
) {
|
||||
logger.debug(
|
||||
`Not printing debug logs, because the configuration does not include the 'languages' or 'dbLocation' fields.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const language of config.languages) {
|
||||
const databaseDirectory = getCodeQLDatabasePath(config, language);
|
||||
const logsDirectory = path.join(databaseDirectory, "log");
|
||||
if (!doesDirectoryExist(logsDirectory)) {
|
||||
core.info(`Directory ${logsDirectory} does not exist.`);
|
||||
logger.info(`Directory ${logsDirectory} does not exist.`);
|
||||
continue; // Skip this language database.
|
||||
}
|
||||
|
||||
const walkLogFiles = (dir: string) => {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
if (entries.length === 0) {
|
||||
core.info(`No debug logs found at directory ${logsDirectory}.`);
|
||||
logger.info(`No debug logs found at directory ${logsDirectory}.`);
|
||||
}
|
||||
for (const entry of entries) {
|
||||
if (entry.isFile()) {
|
||||
const absolutePath = path.resolve(dir, entry.name);
|
||||
core.startGroup(
|
||||
logger.startGroup(
|
||||
`CodeQL Debug Logs - ${language} - ${entry.name} from file at path ${absolutePath}`,
|
||||
);
|
||||
process.stdout.write(fs.readFileSync(absolutePath));
|
||||
core.endGroup();
|
||||
logger.info(fs.readFileSync(absolutePath, "utf-8"));
|
||||
logger.endGroup();
|
||||
} else if (entry.isDirectory()) {
|
||||
walkLogFiles(path.resolve(dir, entry.name));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { getTemporaryDirectory } from "./actions-util";
|
||||
import * as analyses from "./analyses";
|
||||
import { setupCppAutobuild } from "./autobuild";
|
||||
import { type CodeQL } from "./codeql";
|
||||
import type { ActionConfig } from "./config/action-config";
|
||||
import { defaultSuites } from "./config/db-config";
|
||||
import * as configUtils from "./config-utils";
|
||||
import {
|
||||
@@ -171,7 +172,7 @@ async function shouldExtractLanguage(
|
||||
}
|
||||
|
||||
export function dbIsFinalized(
|
||||
config: configUtils.Config,
|
||||
config: ActionConfig<"dbLocation">,
|
||||
language: Language,
|
||||
logger: Logger,
|
||||
) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import * as api from "./api-client";
|
||||
import * as outputCache from "./cli/output-cache";
|
||||
import type { VersionInfo } from "./cli/types";
|
||||
import { CliError, wrapCliConfigurationError } from "./cli-errors";
|
||||
import type { ActionConfig } from "./config/action-config";
|
||||
import { appendExtraQueryExclusions, type Config } from "./config-utils";
|
||||
import { DocUrl } from "./doc-url";
|
||||
import { EnvVar, getEnv, exportEnvVar } from "./environment";
|
||||
@@ -197,7 +198,7 @@ export interface CodeQL {
|
||||
diagnosticsExport(
|
||||
sarifFile: string,
|
||||
automationDetailsId: string | undefined,
|
||||
config: Config,
|
||||
config: ActionConfig<"tempDir">,
|
||||
): Promise<void>;
|
||||
/** Get the location of an extractor for the specified language. */
|
||||
resolveExtractor(language: Language): Promise<string>;
|
||||
@@ -913,7 +914,7 @@ async function getCodeQLForCmd(
|
||||
async diagnosticsExport(
|
||||
sarifFile: string,
|
||||
automationDetailsId: string | undefined,
|
||||
config: Config,
|
||||
config: ActionConfig<"tempDir">,
|
||||
): Promise<void> {
|
||||
const args = [
|
||||
"diagnostics",
|
||||
@@ -1237,7 +1238,9 @@ export async function getTrapCachingExtractorConfigArgsForLang(
|
||||
*
|
||||
* This will not exist if the configuration is being parsed in the Action.
|
||||
*/
|
||||
function getGeneratedCodeScanningConfigPath(config: Config): string {
|
||||
function getGeneratedCodeScanningConfigPath(
|
||||
config: ActionConfig<"tempDir">,
|
||||
): string {
|
||||
return path.resolve(config.tempDir, "user-config.yaml");
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,11 @@ import {
|
||||
import * as api from "./api-client";
|
||||
import { getCachingKind } from "./caching-utils";
|
||||
import { type CodeQL } from "./codeql";
|
||||
import { type Config } from "./config/action-config";
|
||||
import {
|
||||
type AbortedInitConfig,
|
||||
type ActionConfig,
|
||||
type Config,
|
||||
} from "./config/action-config";
|
||||
import {
|
||||
calculateAugmentation,
|
||||
ExcludeQueryFilter,
|
||||
@@ -71,6 +75,7 @@ import {
|
||||
import { OverlayDatabaseMode } from "./overlay/overlay-database-mode";
|
||||
import { shouldSkipOverlayAnalysis } from "./overlay/status";
|
||||
import { RepositoryNwo } from "./repository";
|
||||
import { ActionName } from "./status-report";
|
||||
import { ToolsFeature } from "./tools-features";
|
||||
import { downloadTrapCaches } from "./trap-caching";
|
||||
import {
|
||||
@@ -1379,31 +1384,86 @@ export function getPathToParsedConfigFile(tempDir: string): string {
|
||||
return path.join(tempDir, "config");
|
||||
}
|
||||
|
||||
function getPathToFailedConfigFile(tempDir: string): string {
|
||||
return path.join(tempDir, "failed-config");
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the given config to the path returned from getPathToParsedConfigFile.
|
||||
* Writes {@link config} to {@link configFilePath}.
|
||||
*
|
||||
* This function is not intended to be called, other than by
|
||||
* {@link saveConfig} and {@link saveAbortedInitConfig} below.
|
||||
*/
|
||||
export async function saveConfig(config: Config, logger: Logger) {
|
||||
function saveConfigFile(
|
||||
logger: Logger,
|
||||
configFilePath: string,
|
||||
config: Partial<Config>,
|
||||
) {
|
||||
const configString = JSON.stringify(config);
|
||||
const configFile = getPathToParsedConfigFile(config.tempDir);
|
||||
fs.mkdirSync(path.dirname(configFile), { recursive: true });
|
||||
fs.writeFileSync(configFile, configString, "utf8");
|
||||
fs.mkdirSync(path.dirname(configFilePath), { recursive: true });
|
||||
fs.writeFileSync(configFilePath, configString, "utf8");
|
||||
logger.debug("Saved config:");
|
||||
logger.debug(configString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the config that has been saved to the given temp dir.
|
||||
* If the config could not be found then returns undefined.
|
||||
* Store the given config to the path returned from getPathToParsedConfigFile.
|
||||
*/
|
||||
export async function getConfig(
|
||||
tempDir: string,
|
||||
export async function saveConfig(config: Config, logger: Logger) {
|
||||
const configFile = getPathToParsedConfigFile(config.tempDir);
|
||||
saveConfigFile(logger, configFile, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store parts of {@link config} as a failed configuration file.
|
||||
*
|
||||
* The file will only contain parts of {@link config} that are relevant to
|
||||
* the `init-post` step in the workflow.
|
||||
*/
|
||||
export function saveAbortedInitConfig(
|
||||
action: ActionState<["Base", "Logger"]>,
|
||||
config: Config,
|
||||
) {
|
||||
// Should only be called by init.
|
||||
if (action.name !== ActionName.Init) {
|
||||
throw new Error(
|
||||
`Internal error: 'saveAbortedInitConfig' should only be called by '${ActionName.Init}', but was called by '${action.name}'.`,
|
||||
);
|
||||
}
|
||||
|
||||
// Create a copy of the input `Config` that only contains the keys we want.
|
||||
const failedConfig: AbortedInitConfig = {
|
||||
version: config.version,
|
||||
tempDir: config.tempDir,
|
||||
codeQLCmd: config.codeQLCmd,
|
||||
debugMode: config.debugMode,
|
||||
gitHubVersion: config.gitHubVersion,
|
||||
languages: config.languages,
|
||||
analysisKinds: config.analysisKinds,
|
||||
};
|
||||
|
||||
action.logger.debug(`Saving config for failed init step.`);
|
||||
saveConfigFile(
|
||||
action.logger,
|
||||
getPathToFailedConfigFile(config.tempDir),
|
||||
failedConfig,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to read a {@link Config} from {@link configFilePath}.
|
||||
*
|
||||
* This function is not intended to be called, other than by
|
||||
* {@link getConfig} and {@link getAbortedInitConfig} below.
|
||||
*/
|
||||
function readConfigFile(
|
||||
logger: Logger,
|
||||
): Promise<Config | undefined> {
|
||||
const configFile = getPathToParsedConfigFile(tempDir);
|
||||
if (!fs.existsSync(configFile)) {
|
||||
configFilePath: string,
|
||||
): Config | undefined {
|
||||
if (!fs.existsSync(configFilePath)) {
|
||||
return undefined;
|
||||
}
|
||||
const configString = fs.readFileSync(configFile, "utf8");
|
||||
const configString = fs.readFileSync(configFilePath, "utf8");
|
||||
logger.debug("Loaded config:");
|
||||
logger.debug(configString);
|
||||
|
||||
@@ -1428,6 +1488,38 @@ export async function getConfig(
|
||||
return config as Config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the config that has been saved to the given temp dir.
|
||||
* If the config could not be found then returns undefined.
|
||||
*/
|
||||
export async function getConfig(
|
||||
tempDir: string,
|
||||
logger: Logger,
|
||||
): Promise<Config | undefined> {
|
||||
const configFile = getPathToParsedConfigFile(tempDir);
|
||||
return readConfigFile(logger, configFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a {@link AbortedInitConfig} from a failed `init` step if it exists.
|
||||
*
|
||||
* The result will only contain parts of a {@link Config} that are relevant to
|
||||
* the `init-post` step in the workflow.
|
||||
*/
|
||||
export function getAbortedInitConfig(
|
||||
action: ActionState<["Base", "Logger"]>,
|
||||
tempDir: string,
|
||||
): AbortedInitConfig | undefined {
|
||||
// Only intended to be called by the `init-post` action.
|
||||
if (action.name !== ActionName.InitPost) {
|
||||
throw new Error(
|
||||
`Internal error: 'getAbortedInitConfig' should only be called by '${ActionName.InitPost}', but was called by '${action.name}'.`,
|
||||
);
|
||||
}
|
||||
|
||||
return readConfigFile(action.logger, getPathToFailedConfigFile(tempDir));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a `qlconfig.yml` file from the `registries` input.
|
||||
* This file is used by the CodeQL CLI to list the registries to use for each
|
||||
@@ -1618,21 +1710,27 @@ export function appendExtraQueryExclusions(
|
||||
/**
|
||||
* Returns `true` if Code Scanning analysis is enabled, or `false` if not.
|
||||
*/
|
||||
export function isCodeScanningEnabled(config: Config): boolean {
|
||||
export function isCodeScanningEnabled(
|
||||
config: ActionConfig<"analysisKinds">,
|
||||
): boolean {
|
||||
return config.analysisKinds.includes(AnalysisKind.CodeScanning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if Code Quality analysis is enabled, or `false` if not.
|
||||
*/
|
||||
export function isCodeQualityEnabled(config: Config): boolean {
|
||||
export function isCodeQualityEnabled(
|
||||
config: ActionConfig<"analysisKinds">,
|
||||
): boolean {
|
||||
return config.analysisKinds.includes(AnalysisKind.CodeQuality);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if Code Scanning Risk Assessment analysis is enabled, or `false` if not.
|
||||
*/
|
||||
export function isRiskAssessmentEnabled(config: Config): boolean {
|
||||
export function isRiskAssessmentEnabled(
|
||||
config: ActionConfig<"analysisKinds">,
|
||||
): boolean {
|
||||
return config.analysisKinds.includes(AnalysisKind.RiskAssessment);
|
||||
}
|
||||
|
||||
|
||||
@@ -126,3 +126,38 @@ export interface Config {
|
||||
*/
|
||||
enableFileCoverageInformation: boolean;
|
||||
}
|
||||
|
||||
/** Properties available in `Config`. */
|
||||
export type ActionConfigProperties = keyof Config;
|
||||
|
||||
/**
|
||||
* The CodeQL Action configuration state that is persisted
|
||||
* between steps of the CodeQL Action in a CodeQL workflow.
|
||||
*
|
||||
* By default, this is equivalent to `Config`. By explicitly
|
||||
* providing required property names for `T` this can be restricted
|
||||
* to requiring only those. For example, in `ActionConfig<"codeQLCmd">`
|
||||
* all properties except `codeqlCmd` are optional.
|
||||
*/
|
||||
export type ActionConfig<
|
||||
T extends ActionConfigProperties = ActionConfigProperties,
|
||||
> = Partial<Config> & Pick<Config, T>;
|
||||
|
||||
/** Helper function to check whether `config` has `key` to narrow the type of `config`. */
|
||||
export function hasConfigKey<Key extends ActionConfigProperties>(
|
||||
config: Partial<Config>,
|
||||
key: Key,
|
||||
): config is ActionConfig<Key> {
|
||||
return config[key] !== undefined;
|
||||
}
|
||||
|
||||
/** A minimal configuration that we can make available even when `init` aborts. */
|
||||
export type AbortedInitConfig = ActionConfig<
|
||||
| "version"
|
||||
| "tempDir"
|
||||
| "gitHubVersion"
|
||||
| "debugMode"
|
||||
| "languages"
|
||||
| "analysisKinds"
|
||||
| "codeQLCmd"
|
||||
>;
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getOptionalInput, getTemporaryDirectory } from "./actions-util";
|
||||
import { dbIsFinalized } from "./analyze";
|
||||
import { scanArtifactsForTokens } from "./artifact-scanner";
|
||||
import { type CodeQL } from "./codeql";
|
||||
import { Config } from "./config-utils";
|
||||
import type { ActionConfig } from "./config/action-config";
|
||||
import { EnvVar, exportEnvVar } from "./environment";
|
||||
import * as json from "./json";
|
||||
import { Language } from "./languages";
|
||||
@@ -89,7 +89,7 @@ export async function uploadCombinedSarifArtifacts(
|
||||
* @return The path to that debug artifact, or undefined if an error occurs.
|
||||
*/
|
||||
function tryPrepareSarifDebugArtifact(
|
||||
config: Config,
|
||||
config: ActionConfig<"dbLocation">,
|
||||
language: Language,
|
||||
logger: Logger,
|
||||
): string | undefined {
|
||||
@@ -131,7 +131,7 @@ function tryPrepareSarifDebugArtifact(
|
||||
*/
|
||||
async function tryBundleDatabase(
|
||||
codeql: CodeQL,
|
||||
config: Config,
|
||||
config: ActionConfig<"dbLocation">,
|
||||
language: Language,
|
||||
logger: Logger,
|
||||
): Promise<string | undefined> {
|
||||
@@ -157,6 +157,11 @@ async function tryBundleDatabase(
|
||||
}
|
||||
}
|
||||
|
||||
/** The minimal configuration needed for `tryUploadAllAvailableDebugArtifacts`. */
|
||||
export type UploadDebugArtifactsConfig = ActionConfig<
|
||||
"languages" | "gitHubVersion" | "dbLocation" | "debugArtifactName"
|
||||
>;
|
||||
|
||||
/**
|
||||
* Attempt to upload all available debug artifacts.
|
||||
*
|
||||
@@ -164,7 +169,7 @@ async function tryBundleDatabase(
|
||||
*/
|
||||
export async function tryUploadAllAvailableDebugArtifacts(
|
||||
codeql: CodeQL,
|
||||
config: Config,
|
||||
config: UploadDebugArtifactsConfig,
|
||||
logger: Logger,
|
||||
) {
|
||||
const filesToUpload: string[] = [];
|
||||
@@ -353,7 +358,7 @@ export async function getArtifactUploaderClient(
|
||||
* all files in the database folder and return the path.
|
||||
*/
|
||||
async function createPartialDatabaseBundle(
|
||||
config: Config,
|
||||
config: ActionConfig<"dbLocation">,
|
||||
language: Language,
|
||||
): Promise<string> {
|
||||
const databasePath = getCodeQLDatabasePath(config, language);
|
||||
@@ -394,7 +399,7 @@ async function createPartialDatabaseBundle(
|
||||
*/
|
||||
async function createDatabaseBundleCli(
|
||||
codeql: CodeQL,
|
||||
config: Config,
|
||||
config: ActionConfig<"dbLocation">,
|
||||
language: Language,
|
||||
): Promise<string> {
|
||||
const databaseBundlePath = await bundleDb(
|
||||
|
||||
@@ -7,6 +7,11 @@ import * as actionsUtil from "./actions-util";
|
||||
import { CodeScanning, RiskAssessment } from "./analyses";
|
||||
import { getApiClient, getGitHubVersion } from "./api-client";
|
||||
import { CodeQL, getCodeQL } from "./codeql";
|
||||
import {
|
||||
hasConfigKey,
|
||||
type AbortedInitConfig,
|
||||
type ActionConfig,
|
||||
} from "./config/action-config";
|
||||
import {
|
||||
Config,
|
||||
isCodeScanningEnabled,
|
||||
@@ -16,6 +21,7 @@ import {
|
||||
getArtifactSuffix,
|
||||
getArtifactUploaderClient,
|
||||
sanitizeArtifactName,
|
||||
type UploadDebugArtifactsConfig,
|
||||
} from "./debug-artifacts";
|
||||
import * as dependencyCaching from "./dependency-caching";
|
||||
import { EnvVar, ReadOnlyEnv } from "./environment";
|
||||
@@ -86,6 +92,11 @@ interface FailedSarifInfo {
|
||||
checkoutPath: string;
|
||||
}
|
||||
|
||||
/** The minimal Action configuration required to prepare and upload a failed SARIF. */
|
||||
export type FailedSarifConfig = ActionConfig<
|
||||
"codeQLCmd" | "tempDir" | "analysisKinds" | "languages"
|
||||
>;
|
||||
|
||||
/**
|
||||
* Tries to prepare a SARIF file that contains information about a failed analysis.
|
||||
*
|
||||
@@ -94,7 +105,7 @@ interface FailedSarifInfo {
|
||||
async function prepareFailedSarif(
|
||||
logger: Logger,
|
||||
features: FeatureEnablement,
|
||||
config: Config,
|
||||
config: FailedSarifConfig,
|
||||
): Promise<Result<FailedSarifInfo, UploadFailedSarifResult>> {
|
||||
if (!config.codeQLCmd) {
|
||||
return new Failure({
|
||||
@@ -160,7 +171,7 @@ async function prepareFailedSarif(
|
||||
async function generateFailedSarif(
|
||||
logger: Logger,
|
||||
features: FeatureEnablement,
|
||||
config: Config,
|
||||
config: ActionConfig<"codeQLCmd" | "tempDir">,
|
||||
category: string | undefined,
|
||||
checkoutPath: string,
|
||||
sarifFile?: string,
|
||||
@@ -192,7 +203,7 @@ async function generateFailedSarif(
|
||||
* category for the workflow.
|
||||
*/
|
||||
async function maybeUploadFailedSarif(
|
||||
config: Config,
|
||||
config: FailedSarifConfig,
|
||||
repositoryNwo: RepositoryNwo,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
@@ -227,7 +238,7 @@ async function maybeUploadFailedSarif(
|
||||
|
||||
/** Uploads a failed SARIF file as workflow artifact, if it can be generated. */
|
||||
async function maybeUploadFailedSarifArtifact(
|
||||
config: Config,
|
||||
config: FailedSarifConfig,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
): Promise<UploadFailedSarifResult> {
|
||||
@@ -267,7 +278,7 @@ async function maybeUploadFailedSarifArtifact(
|
||||
* @returns The results of uploading the SARIF file for the failure.
|
||||
*/
|
||||
export async function tryUploadSarifIfRunFailed(
|
||||
config: Config,
|
||||
config: FailedSarifConfig,
|
||||
repositoryNwo: RepositoryNwo,
|
||||
features: FeatureEnablement,
|
||||
logger: Logger,
|
||||
@@ -324,12 +335,12 @@ export async function tryUploadSarifIfRunFailed(
|
||||
export async function uploadFailureInfo(
|
||||
uploadAllAvailableDebugArtifacts: (
|
||||
codeql: CodeQL,
|
||||
config: Config,
|
||||
config: UploadDebugArtifactsConfig,
|
||||
logger: Logger,
|
||||
) => Promise<void>,
|
||||
printDebugLogs: (config: Config) => Promise<void>,
|
||||
printDebugLogs: (logger: Logger, config: Partial<Config>) => Promise<void>,
|
||||
codeql: CodeQL,
|
||||
config: Config,
|
||||
config: Config | AbortedInitConfig,
|
||||
repositoryNwo: RepositoryNwo,
|
||||
features: FeatureEnablement,
|
||||
jobStatus: string | undefined,
|
||||
@@ -376,15 +387,15 @@ export async function uploadFailureInfo(
|
||||
}
|
||||
|
||||
// Upload appropriate Actions artifacts for debugging
|
||||
if (config.debugMode) {
|
||||
if (config.debugMode && hasConfigKey(config, "dbLocation")) {
|
||||
logger.info(
|
||||
"Debug mode is on. Uploading available database bundles and logs as Actions debugging artifacts...",
|
||||
);
|
||||
await uploadAllAvailableDebugArtifacts(codeql, config, logger);
|
||||
await printDebugLogs(config);
|
||||
await printDebugLogs(logger, config);
|
||||
}
|
||||
|
||||
if (actionsUtil.isSelfHostedRunner()) {
|
||||
if (actionsUtil.isSelfHostedRunner() && config.dbLocation) {
|
||||
try {
|
||||
fs.rmSync(config.dbLocation, {
|
||||
recursive: true,
|
||||
@@ -399,6 +410,10 @@ export async function uploadFailureInfo(
|
||||
`Failed to clean up database cluster directory ${config.dbLocation}. Details: ${e}`,
|
||||
);
|
||||
}
|
||||
} else if (config.dbLocation === undefined) {
|
||||
logger.debug(
|
||||
"Skipping cleanup of database cluster directory since the configuration does not include a database path.",
|
||||
);
|
||||
} else {
|
||||
logger.debug(
|
||||
"Skipping cleanup of database cluster directory since we are running on a GitHub-hosted " +
|
||||
@@ -447,7 +462,7 @@ function isConclusiveJobStatus(jobStatus: string | undefined): boolean {
|
||||
*/
|
||||
async function recordOverlayStatus(
|
||||
codeql: CodeQL,
|
||||
config: Config,
|
||||
config: Partial<Config>,
|
||||
features: FeatureEnablement,
|
||||
jobStatus: string | undefined,
|
||||
env: ReadOnlyEnv,
|
||||
@@ -459,6 +474,11 @@ async function recordOverlayStatus(
|
||||
!(await features.getValue(Feature.OverlayAnalysisStatusSave))
|
||||
) {
|
||||
return;
|
||||
} else if (config.languages === undefined) {
|
||||
logger.debug(
|
||||
"Not recording overlay status, because the configuration contains no 'languages'.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only record a failure when the job outcome tells us something about the analysis. A cancelled
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import * as core from "@actions/core";
|
||||
|
||||
import { runInActions, type Action, type ActionState } from "./action-common";
|
||||
import {
|
||||
restoreInputs,
|
||||
getOptionalInput,
|
||||
@@ -15,22 +16,25 @@ import {
|
||||
import { getGitHubVersion, wrapApiConfigurationError } from "./api-client";
|
||||
import { CachingKind } from "./caching-utils";
|
||||
import { getCodeQL } from "./codeql";
|
||||
import { type Config, getConfig } from "./config-utils";
|
||||
import {
|
||||
hasConfigKey,
|
||||
type AbortedInitConfig,
|
||||
type Config,
|
||||
} from "./config/action-config";
|
||||
import { getAbortedInitConfig, getConfig } from "./config-utils";
|
||||
import * as debugArtifacts from "./debug-artifacts";
|
||||
import {
|
||||
DependencyCachingUsageReport,
|
||||
getDependencyCacheUsage,
|
||||
} from "./dependency-caching";
|
||||
import { EnvVar, getEnv, exportEnvVar } from "./environment";
|
||||
import { EnvVar, type ReadOnlyEnv } from "./environment";
|
||||
import { initFeatures } from "./feature-flags";
|
||||
import * as gitUtils from "./git-utils";
|
||||
import * as initActionPostHelper from "./init-action-post-helper";
|
||||
import { getActionsLogger } from "./logging";
|
||||
import { getRepositoryNwo } from "./repository";
|
||||
import {
|
||||
StatusReportBase,
|
||||
sendStatusReport,
|
||||
sendUnhandledErrorStatusReport,
|
||||
createStatusReportBase,
|
||||
getActionsStatus,
|
||||
ActionName,
|
||||
@@ -45,16 +49,17 @@ interface InitPostStatusReport
|
||||
initActionPostHelper.JobStatusReport,
|
||||
initActionPostHelper.DependencyCachingUsageReport {}
|
||||
|
||||
async function run(startedAt: Date) {
|
||||
async function run(action: ActionState<["Base", "Logger", "Env"]>) {
|
||||
// To capture errors appropriately, keep as much code within the try-catch as
|
||||
// possible, and only use safe functions outside.
|
||||
const logger = action.logger;
|
||||
|
||||
const logger = getActionsLogger();
|
||||
let config: Config | undefined;
|
||||
let config: Config | AbortedInitConfig | undefined;
|
||||
let uploadFailedSarifResult:
|
||||
| initActionPostHelper.UploadFailedSarifResult
|
||||
| undefined;
|
||||
let dependencyCachingUsage: DependencyCachingUsageReport | undefined;
|
||||
|
||||
try {
|
||||
// Read the job status before restoring inputs, since it is provided by the Actions runtime
|
||||
// environment for this step and would otherwise be overwritten by the value that the `init`
|
||||
@@ -64,7 +69,15 @@ async function run(startedAt: Date) {
|
||||
// Restore inputs from `init` Action.
|
||||
restoreInputs(logger);
|
||||
|
||||
config = await getConfig(getTemporaryDirectory(), logger);
|
||||
const tmpDir = getTemporaryDirectory();
|
||||
config = await getConfig(tmpDir, logger);
|
||||
|
||||
// If we weren't able to read the normal configuration file, the `init` step failed before it could write it.
|
||||
// Try to load the "failed" configuration file instead which may have been written by the `init` step
|
||||
// with a minimal configuration for this post-action.
|
||||
if (config === undefined) {
|
||||
config = getAbortedInitConfig(action, tmpDir);
|
||||
}
|
||||
|
||||
const gitHubVersion = config?.gitHubVersion ?? (await getGitHubVersion());
|
||||
checkGitHubVersionInRange(gitHubVersion, logger);
|
||||
@@ -92,7 +105,7 @@ async function run(startedAt: Date) {
|
||||
repositoryNwo,
|
||||
features,
|
||||
jobStatus,
|
||||
getEnv(),
|
||||
action.env,
|
||||
logger,
|
||||
);
|
||||
|
||||
@@ -102,6 +115,7 @@ async function run(startedAt: Date) {
|
||||
// and where caching may not be enabled.
|
||||
if (
|
||||
(await gitUtils.isAnalyzingDefaultBranch()) &&
|
||||
hasConfigKey(config, "dependencyCachingEnabled") &&
|
||||
config.dependencyCachingEnabled !== CachingKind.None
|
||||
) {
|
||||
dependencyCachingUsage = await getDependencyCacheUsage(logger);
|
||||
@@ -112,9 +126,9 @@ async function run(startedAt: Date) {
|
||||
core.setFailed(error.message);
|
||||
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
ActionName.InitPost,
|
||||
action.name,
|
||||
getActionsStatus(error),
|
||||
startedAt,
|
||||
action.startedAt,
|
||||
config,
|
||||
await checkDiskUsage(logger),
|
||||
logger,
|
||||
@@ -126,13 +140,13 @@ async function run(startedAt: Date) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
const jobStatus = getFinalJobStatus(config);
|
||||
const jobStatus = getFinalJobStatus(action, config);
|
||||
logger.info(`CodeQL job status was ${getJobStatusDisplayName(jobStatus)}.`);
|
||||
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
ActionName.InitPost,
|
||||
action.name,
|
||||
"success",
|
||||
startedAt,
|
||||
action.startedAt,
|
||||
config,
|
||||
await checkDiskUsage(logger),
|
||||
logger,
|
||||
@@ -157,16 +171,21 @@ async function run(startedAt: Date) {
|
||||
* Otherwise, we determine the job status based on whether the analyze step
|
||||
* completed successfully and whether we have a valid CodeQL config.
|
||||
*/
|
||||
function getFinalJobStatus(config: Config | undefined): JobStatus {
|
||||
const existingJobStatus = getJobStatusFromEnvironment();
|
||||
function getFinalJobStatus(
|
||||
action: ActionState<["Env"]>,
|
||||
config: Partial<Config> | undefined,
|
||||
): JobStatus {
|
||||
const existingJobStatus = getJobStatusFromEnvironment(action.env);
|
||||
if (existingJobStatus !== undefined) {
|
||||
return existingJobStatus;
|
||||
}
|
||||
|
||||
let jobStatus: JobStatus;
|
||||
|
||||
if (process.env[EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY] === "true") {
|
||||
exportEnvVar(EnvVar.JOB_STATUS, JobStatus.SuccessStatus);
|
||||
if (
|
||||
action.env.getOptional(EnvVar.ANALYZE_DID_COMPLETE_SUCCESSFULLY) === "true"
|
||||
) {
|
||||
action.env.export(EnvVar.JOB_STATUS, JobStatus.SuccessStatus);
|
||||
jobStatus = JobStatus.SuccessStatus;
|
||||
} else if (config !== undefined) {
|
||||
// - We have computed a CodeQL config
|
||||
@@ -191,7 +210,7 @@ function getFinalJobStatus(config: Config | undefined): JobStatus {
|
||||
|
||||
// This shouldn't be necessary, but in the odd case that we run more than one
|
||||
// `init` post step, ensure the job status is consistent between them.
|
||||
exportEnvVar(EnvVar.JOB_STATUS, jobStatus);
|
||||
action.env.export(EnvVar.JOB_STATUS, jobStatus);
|
||||
return jobStatus;
|
||||
}
|
||||
|
||||
@@ -200,8 +219,8 @@ function getFinalJobStatus(config: Config | undefined): JobStatus {
|
||||
*
|
||||
* If the job status is invalid, return `UnknownStatus`.
|
||||
*/
|
||||
function getJobStatusFromEnvironment(): JobStatus | undefined {
|
||||
const jobStatusFromEnvironment = process.env[EnvVar.JOB_STATUS];
|
||||
function getJobStatusFromEnvironment(env: ReadOnlyEnv): JobStatus | undefined {
|
||||
const jobStatusFromEnvironment = env.getOptional(EnvVar.JOB_STATUS);
|
||||
|
||||
if (jobStatusFromEnvironment !== undefined) {
|
||||
// Validate the job status from the environment. If it is invalid, return unknown.
|
||||
@@ -216,18 +235,11 @@ function getJobStatusFromEnvironment(): JobStatus | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const initPost: Action = {
|
||||
name: ActionName.InitPost,
|
||||
run,
|
||||
};
|
||||
|
||||
export async function runWrapper() {
|
||||
const startedAt = new Date();
|
||||
const logger = getActionsLogger();
|
||||
try {
|
||||
await run(startedAt);
|
||||
} catch (error) {
|
||||
core.setFailed(`init post action failed: ${wrapError(error).message}`);
|
||||
await sendUnhandledErrorStatusReport(
|
||||
ActionName.InitPost,
|
||||
startedAt,
|
||||
error,
|
||||
logger,
|
||||
);
|
||||
}
|
||||
return await runInActions(initPost);
|
||||
}
|
||||
|
||||
@@ -457,6 +457,18 @@ async function run(
|
||||
|
||||
await checkInstallPython311(config.languages, codeql);
|
||||
} catch (unwrappedError) {
|
||||
// If we have initialised the Action state, then try to save a subset of it for use by the `init-post` action
|
||||
// so that it can try to create and upload additional diagnostic information.
|
||||
if (config !== undefined) {
|
||||
try {
|
||||
configUtils.saveAbortedInitConfig(actionState, config);
|
||||
} catch (configSaveError) {
|
||||
logger.warning(
|
||||
`Unable to save configuration for '${ActionName.InitPost}' step: ${getErrorMessage(configSaveError)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const error = wrapError(unwrappedError);
|
||||
core.setFailed(error.message);
|
||||
const statusReportBase = await createStatusReportBase(
|
||||
|
||||
@@ -61,13 +61,21 @@ export enum ActionName {
|
||||
UploadSarif = "upload-sarif",
|
||||
}
|
||||
|
||||
/** A partial mapping from `ActionName` to names for actions whose display name is not the `ActionName` value. */
|
||||
export const ACTION_DISPLAY_NAMES: Partial<Record<ActionName, string>> = {
|
||||
[ActionName.Analyze]: "analyze",
|
||||
[ActionName.InitPost]: "init post",
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps an `ActionName` to its display name. Usually that is the same, except
|
||||
* for `ActionName.Analyze` where it is `"analyze"` instead of `"finish"`.
|
||||
* Maps an {@link ActionName} to its display name. Usually that is the same, except
|
||||
* for actions which have a different display name in {@link ACTION_DISPLAY_NAMES}.
|
||||
*/
|
||||
export function getDisplayActionName(actionName: ActionName): string {
|
||||
if (actionName === ActionName.Analyze) {
|
||||
return "analyze";
|
||||
const displayName = ACTION_DISPLAY_NAMES[actionName];
|
||||
|
||||
if (displayName !== undefined) {
|
||||
return displayName;
|
||||
}
|
||||
return actionName;
|
||||
}
|
||||
|
||||
12
src/util.ts
12
src/util.ts
@@ -12,6 +12,7 @@ import * as semver from "semver";
|
||||
|
||||
import * as apiCompatibility from "./api-compatibility.json";
|
||||
import type { CodeQL } from "./codeql";
|
||||
import type { ActionConfig } from "./config/action-config";
|
||||
import type { Pack } from "./config/db-config";
|
||||
import type { Config } from "./config-utils";
|
||||
import {
|
||||
@@ -445,7 +446,10 @@ export function getThreadsFlag(
|
||||
/**
|
||||
* Get the path where the CodeQL database for the given language lives.
|
||||
*/
|
||||
export function getCodeQLDatabasePath(config: Config, language: Language) {
|
||||
export function getCodeQLDatabasePath(
|
||||
config: ActionConfig<"dbLocation">,
|
||||
language: Language,
|
||||
) {
|
||||
return path.resolve(config.dbLocation, language);
|
||||
}
|
||||
|
||||
@@ -639,7 +643,9 @@ export async function codeQlVersionAtLeast(
|
||||
return semver.gte((await codeql.getVersion()).version, requiredVersion);
|
||||
}
|
||||
|
||||
export function getBaseDatabaseOidsFilePath(config: Config): string {
|
||||
export function getBaseDatabaseOidsFilePath(
|
||||
config: ActionConfig<"dbLocation">,
|
||||
): string {
|
||||
return path.join(config.dbLocation, BASE_DATABASE_OIDS_FILE_NAME);
|
||||
}
|
||||
|
||||
@@ -651,7 +657,7 @@ export function getBaseDatabaseOidsFilePath(config: Config): string {
|
||||
* given `includeDiagnostics` value.
|
||||
*/
|
||||
export async function bundleDb(
|
||||
config: Config,
|
||||
config: ActionConfig<"dbLocation">,
|
||||
language: Language,
|
||||
codeql: CodeQL,
|
||||
dbName: string,
|
||||
|
||||
Reference in New Issue
Block a user