mirror of
https://github.com/github/codeql-action.git
synced 2025-12-31 03:30:14 +08:00
Merge pull request #2234 from github/mbg/clearer-file-command-failure
This commit is contained in:
@@ -426,6 +426,14 @@ export function getWorkflowRunAttempt(): number {
|
||||
return workflowRunAttempt;
|
||||
}
|
||||
|
||||
export class FileCmdNotFoundError extends Error {
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
|
||||
this.name = "FileCmdNotFoundError";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to obtain the output of the `file` command for the file at the specified path.
|
||||
* The output will vary depending on the type of `file`, which operating system we are running on, etc.
|
||||
@@ -433,25 +441,32 @@ export function getWorkflowRunAttempt(): number {
|
||||
export const getFileType = async (filePath: string): Promise<string> => {
|
||||
let stderr = "";
|
||||
let stdout = "";
|
||||
|
||||
let fileCmdPath: string;
|
||||
|
||||
try {
|
||||
fileCmdPath = await safeWhich.safeWhich("file");
|
||||
} catch (e) {
|
||||
throw new FileCmdNotFoundError(
|
||||
`The \`file\` program is required, but does not appear to be installed. Please install it: ${e}`,
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// The `file` command will output information about the type of file pointed at by `filePath`.
|
||||
// For binary files, this may include e.g. whether they are static of dynamic binaries.
|
||||
// The `-L` switch instructs the command to follow symbolic links.
|
||||
await new toolrunner.ToolRunner(
|
||||
await safeWhich.safeWhich("file"),
|
||||
["-L", filePath],
|
||||
{
|
||||
silent: true,
|
||||
listeners: {
|
||||
stdout: (data) => {
|
||||
stdout += data.toString();
|
||||
},
|
||||
stderr: (data) => {
|
||||
stderr += data.toString();
|
||||
},
|
||||
await new toolrunner.ToolRunner(fileCmdPath, ["-L", filePath], {
|
||||
silent: true,
|
||||
listeners: {
|
||||
stdout: (data) => {
|
||||
stdout += data.toString();
|
||||
},
|
||||
stderr: (data) => {
|
||||
stderr += data.toString();
|
||||
},
|
||||
},
|
||||
).exec();
|
||||
}).exec();
|
||||
return stdout.trim();
|
||||
} catch (e) {
|
||||
core.info(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mkdirSync, writeFileSync } from "fs";
|
||||
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
||||
import path from "path";
|
||||
|
||||
import { Config } from "./config-utils";
|
||||
@@ -55,6 +55,17 @@ export interface DiagnosticMessage {
|
||||
attributes?: { [key: string]: any };
|
||||
}
|
||||
|
||||
/** Represents a diagnostic message that has not yet been written to the database. */
|
||||
interface UnwrittenDiagnostic {
|
||||
/** The diagnostic message that has not yet been written. */
|
||||
diagnostic: DiagnosticMessage;
|
||||
/** The language the diagnostic is for. */
|
||||
language: Language;
|
||||
}
|
||||
|
||||
/** A list of diagnostics which have not yet been written to disk. */
|
||||
let unwrittenDiagnostics: UnwrittenDiagnostic[] = [];
|
||||
|
||||
/**
|
||||
* Constructs a new diagnostic message with the specified id and name, as well as optional additional data.
|
||||
*
|
||||
@@ -76,15 +87,45 @@ export function makeDiagnostic(
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given diagnostic to the database.
|
||||
* Adds the given diagnostic to the database. If the database does not yet exist,
|
||||
* the diagnostic will be written to it once it has been created.
|
||||
*
|
||||
* @param config The configuration that tells us where to store the diagnostic.
|
||||
* @param language The language which the diagnostic is for.
|
||||
* @param diagnostic The diagnostic message to add to the database.
|
||||
*/
|
||||
export function addDiagnostic(
|
||||
config: Config,
|
||||
language: Language,
|
||||
diagnostic: DiagnosticMessage,
|
||||
) {
|
||||
const logger = getActionsLogger();
|
||||
const databasePath = getCodeQLDatabasePath(config, language);
|
||||
|
||||
// Check that the database exists before writing to it. If the database does not yet exist,
|
||||
// store the diagnostic in memory and write it later.
|
||||
if (existsSync(databasePath)) {
|
||||
writeDiagnostic(config, language, diagnostic);
|
||||
} else {
|
||||
logger.debug(
|
||||
`Writing a diagnostic for ${language}, but the database at ${databasePath} does not exist yet.`,
|
||||
);
|
||||
|
||||
unwrittenDiagnostics.push({ diagnostic, language });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given diagnostic to the database.
|
||||
*
|
||||
* @param config The configuration that tells us where to store the diagnostic.
|
||||
* @param language The language which the diagnostic is for.
|
||||
* @param diagnostic The diagnostic message to add to the database.
|
||||
*/
|
||||
function writeDiagnostic(
|
||||
config: Config,
|
||||
language: Language,
|
||||
diagnostic: DiagnosticMessage,
|
||||
) {
|
||||
const logger = getActionsLogger();
|
||||
const diagnosticsPath = path.resolve(
|
||||
@@ -105,5 +146,36 @@ export function addDiagnostic(
|
||||
writeFileSync(jsonPath, JSON.stringify(diagnostic));
|
||||
} catch (err) {
|
||||
logger.warning(`Unable to write diagnostic message to database: ${err}`);
|
||||
logger.debug(JSON.stringify(diagnostic));
|
||||
}
|
||||
}
|
||||
|
||||
/** Report if there are unwritten diagnostics and write them to the log. */
|
||||
export function logUnwrittenDiagnostics() {
|
||||
const logger = getActionsLogger();
|
||||
const num = unwrittenDiagnostics.length;
|
||||
if (num > 0) {
|
||||
logger.warning(
|
||||
`${num} diagnostic(s) could not be written to the database and will not appear on the Tool Status Page.`,
|
||||
);
|
||||
|
||||
for (const unwritten of unwrittenDiagnostics) {
|
||||
logger.debug(JSON.stringify(unwritten.diagnostic));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Writes all unwritten diagnostics to disk. */
|
||||
export function flushDiagnostics(config: Config) {
|
||||
const logger = getActionsLogger();
|
||||
logger.debug(
|
||||
`Writing ${unwrittenDiagnostics.length} diagnostic(s) to database.`,
|
||||
);
|
||||
|
||||
for (const unwritten of unwrittenDiagnostics) {
|
||||
writeDiagnostic(config, unwritten.language, unwritten.diagnostic);
|
||||
}
|
||||
|
||||
// Reset the unwritten diagnostics array.
|
||||
unwrittenDiagnostics = [];
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { safeWhich } from "@chrisgavin/safe-which";
|
||||
import { v4 as uuidV4 } from "uuid";
|
||||
|
||||
import {
|
||||
FileCmdNotFoundError,
|
||||
getActionVersion,
|
||||
getFileType,
|
||||
getOptionalInput,
|
||||
@@ -15,6 +16,12 @@ import {
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import { CodeQL } from "./codeql";
|
||||
import * as configUtils from "./config-utils";
|
||||
import {
|
||||
addDiagnostic,
|
||||
flushDiagnostics,
|
||||
logUnwrittenDiagnostics,
|
||||
makeDiagnostic,
|
||||
} from "./diagnostics";
|
||||
import { EnvVar } from "./environment";
|
||||
import { Feature, Features } from "./feature-flags";
|
||||
import { checkInstallPython311, initCodeQL, initConfig, runInit } from "./init";
|
||||
@@ -372,6 +379,27 @@ async function run() {
|
||||
logger.warning(
|
||||
`Failed to determine the location of the Go binary: ${e}`,
|
||||
);
|
||||
|
||||
if (e instanceof FileCmdNotFoundError) {
|
||||
addDiagnostic(
|
||||
config,
|
||||
Language.go,
|
||||
makeDiagnostic(
|
||||
"go/workflow/file-program-unavailable",
|
||||
"The `file` program is required on Linux, but does not appear to be installed",
|
||||
{
|
||||
markdownMessage:
|
||||
"CodeQL was unable to find the `file` program on this system. Ensure that the `file` program is installed on Linux runners and accessible.",
|
||||
visibility: {
|
||||
statusPage: true,
|
||||
telemetry: true,
|
||||
cliSummaryTable: true,
|
||||
},
|
||||
severity: "warning",
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,6 +527,10 @@ async function run() {
|
||||
}
|
||||
}
|
||||
|
||||
// Write diagnostics to the database that we previously stored in memory because the database
|
||||
// did not exist until now.
|
||||
flushDiagnostics(config);
|
||||
|
||||
core.setOutput("codeql-path", config.codeQLCmd);
|
||||
} catch (unwrappedError) {
|
||||
const error = wrapError(unwrappedError);
|
||||
@@ -514,6 +546,8 @@ async function run() {
|
||||
error,
|
||||
);
|
||||
return;
|
||||
} finally {
|
||||
logUnwrittenDiagnostics();
|
||||
}
|
||||
await sendCompletedStatusReport(
|
||||
startedAt,
|
||||
|
||||
Reference in New Issue
Block a user