Merge branch 'main' into henrymercer/autobuild-with-direct-tracing
This commit is contained in:
commit
ea78e51e31
24 changed files with 431 additions and 35 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ import consoleLogLevel from "console-log-level";
|
|||
|
||||
import { getActionVersion, getRequiredInput } from "./actions-util";
|
||||
import {
|
||||
ConfigurationError,
|
||||
getRequiredEnvParam,
|
||||
GITHUB_DOTCOM_URL,
|
||||
GitHubVariant,
|
||||
GitHubVersion,
|
||||
isHTTPError,
|
||||
parseGitHubUrl,
|
||||
parseMatrixInput,
|
||||
} from "./util";
|
||||
|
|
@ -192,3 +194,15 @@ export function computeAutomationID(
|
|||
|
||||
return automationID;
|
||||
}
|
||||
|
||||
export function wrapApiConfigurationError(e: unknown) {
|
||||
if (isHTTPError(e)) {
|
||||
if (
|
||||
e.message.includes("API rate limit exceeded for site ID installation") ||
|
||||
/^ref .* not found in this repository$/.test(e.message)
|
||||
) {
|
||||
return new ConfigurationError(e.message);
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ export enum CliConfigErrorCategory {
|
|||
NoSupportedBuildCommandSucceeded = "NoSupportedBuildCommandSucceeded",
|
||||
NoSupportedBuildSystemDetected = "NoSupportedBuildSystemDetected",
|
||||
SwiftBuildFailed = "SwiftBuildFailed",
|
||||
UnsupportedBuildMode = "UnsupportedBuildMode",
|
||||
}
|
||||
|
||||
type CliErrorConfiguration = {
|
||||
|
|
@ -220,6 +221,13 @@ export const cliErrorsConfig: Record<
|
|||
),
|
||||
],
|
||||
},
|
||||
[CliConfigErrorCategory.UnsupportedBuildMode]: {
|
||||
cliErrorMessageCandidates: [
|
||||
new RegExp(
|
||||
"does not support the .* build mode. Please try using one of the following build modes instead",
|
||||
),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -500,6 +528,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);
|
||||
|
|
@ -515,6 +547,8 @@ async function run() {
|
|||
error,
|
||||
);
|
||||
return;
|
||||
} finally {
|
||||
logUnwrittenDiagnostics();
|
||||
}
|
||||
await sendCompletedStatusReport(
|
||||
startedAt,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import * as jsonschema from "jsonschema";
|
|||
import * as actionsUtil from "./actions-util";
|
||||
import { getOptionalInput, getRequiredInput } from "./actions-util";
|
||||
import * as api from "./api-client";
|
||||
import { getGitHubVersion } from "./api-client";
|
||||
import { getGitHubVersion, wrapApiConfigurationError } from "./api-client";
|
||||
import { CodeQL, getCodeQL } from "./codeql";
|
||||
import { getConfig } from "./config-utils";
|
||||
import { EnvVar } from "./environment";
|
||||
|
|
@ -256,7 +256,7 @@ async function uploadPayload(
|
|||
break;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
throw wrapApiConfigurationError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue