Merge pull request #2234 from github/mbg/clearer-file-command-failure
This commit is contained in:
commit
2b2cee5229
12 changed files with 378 additions and 23 deletions
103
.github/workflows/__go-indirect-tracing-workaround-no-file-program.yml
generated
vendored
Normal file
103
.github/workflows/__go-indirect-tracing-workaround-no-file-program.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Warning: This file is generated automatically, and should not be modified.
|
||||
# Instead, please modify the template in the pr-checks directory and run:
|
||||
# (cd pr-checks; pip install ruamel.yaml@0.17.31 && python3 sync.py)
|
||||
# to regenerate this file.
|
||||
|
||||
name: 'PR Check - Go: diagnostic when `file` is not installed'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GO111MODULE: auto
|
||||
CODEQL_EXTRACTOR_JAVA_AGENT_DISABLE_KOTLIN: 'true'
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- releases/v*
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- ready_for_review
|
||||
schedule:
|
||||
- cron: '0 5 * * *'
|
||||
workflow_dispatch: {}
|
||||
jobs:
|
||||
go-indirect-tracing-workaround-no-file-program:
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
version: stable-v2.14.6
|
||||
name: 'Go: diagnostic when `file` is not installed'
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
timeout-minutes: 45
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Setup Python on MacOS
|
||||
uses: actions/setup-python@v5
|
||||
if: >-
|
||||
matrix.os == 'macos-latest' && (
|
||||
|
||||
matrix.version == 'stable-20230403' ||
|
||||
|
||||
matrix.version == 'stable-v2.13.5' ||
|
||||
|
||||
matrix.version == 'stable-v2.14.6')
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Prepare test
|
||||
id: prepare-test
|
||||
uses: ./.github/actions/prepare-test
|
||||
with:
|
||||
version: ${{ matrix.version }}
|
||||
use-all-platform-bundle: 'false'
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
# We need a Go version that ships with statically linked binaries on Linux
|
||||
go-version: '>=1.21.0'
|
||||
- name: Remove `file` program
|
||||
run: |
|
||||
echo $(which file)
|
||||
sudo rm -rf $(which file)
|
||||
echo $(which file)
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
languages: go
|
||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||
- name: Build code
|
||||
shell: bash
|
||||
run: go build main.go
|
||||
- uses: ./../action/analyze
|
||||
with:
|
||||
output: ${{ runner.temp }}/results
|
||||
upload-database: false
|
||||
- name: Check diagnostic appears in SARIF
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SARIF_PATH: ${{ runner.temp }}/results/go.sarif
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
const sarif = JSON.parse(fs.readFileSync(process.env['SARIF_PATH'], 'utf8'));
|
||||
const run = sarif.runs[0];
|
||||
|
||||
const toolExecutionNotifications = run.invocations[0].toolExecutionNotifications;
|
||||
const statusPageNotifications = toolExecutionNotifications.filter(n =>
|
||||
n.descriptor.id === 'go/workflow/file-program-unavailable' && n.properties?.visibility?.statusPage
|
||||
);
|
||||
if (statusPageNotifications.length !== 1) {
|
||||
core.setFailed(
|
||||
'Expected exactly one status page reporting descriptor for this diagnostic in the ' +
|
||||
`'runs[].invocations[].toolExecutionNotifications[]' SARIF property, but found ` +
|
||||
`${statusPageNotifications.length}. All notification reporting descriptors: ` +
|
||||
`${JSON.stringify(toolExecutionNotifications)}.`
|
||||
);
|
||||
}
|
||||
env:
|
||||
CODEQL_ACTION_TEST_MODE: true
|
||||
|
|
@ -16,6 +16,7 @@ Note that the only difference between `v2` and `v3` of the CodeQL Action is the
|
|||
We recommend removing any references to these from your workflows. For more information, see the release notes for CodeQL Action v3.23.0 and v2.23.0.
|
||||
- Automatically overwrite an existing database if found on the filesystem. [#2229](https://github.com/github/codeql-action/pull/2229)
|
||||
- Bump the minimum CodeQL bundle version to 2.12.6. [#2232](https://github.com/github/codeql-action/pull/2232)
|
||||
- A more relevant log message and a diagnostic are now emitted when the `file` program is not installed on a Linux runner, but is required for Go tracing to succeed. [#2234](https://github.com/github/codeql-action/pull/2234)
|
||||
|
||||
## 3.24.10 - 05 Apr 2024
|
||||
|
||||
|
|
|
|||
18
lib/actions-util.js
generated
18
lib/actions-util.js
generated
|
|
@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getFileType = exports.getWorkflowRunAttempt = exports.getWorkflowRunID = exports.getUploadValue = exports.printDebugLogs = exports.isAnalyzingDefaultBranch = exports.getRelativeScriptPath = exports.isRunningLocalAction = exports.getWorkflowEventName = exports.getActionVersion = exports.getRef = exports.determineMergeBaseCommitOid = exports.getCommitOid = exports.getTemporaryDirectory = exports.getOptionalInput = exports.getRequiredInput = void 0;
|
||||
exports.getFileType = exports.FileCmdNotFoundError = exports.getWorkflowRunAttempt = exports.getWorkflowRunID = exports.getUploadValue = exports.printDebugLogs = exports.isAnalyzingDefaultBranch = exports.getRelativeScriptPath = exports.isRunningLocalAction = exports.getWorkflowEventName = exports.getActionVersion = exports.getRef = exports.determineMergeBaseCommitOid = exports.getCommitOid = exports.getTemporaryDirectory = exports.getOptionalInput = exports.getRequiredInput = void 0;
|
||||
const fs = __importStar(require("fs"));
|
||||
const path = __importStar(require("path"));
|
||||
const core = __importStar(require("@actions/core"));
|
||||
|
|
@ -380,6 +380,13 @@ function getWorkflowRunAttempt() {
|
|||
return workflowRunAttempt;
|
||||
}
|
||||
exports.getWorkflowRunAttempt = getWorkflowRunAttempt;
|
||||
class FileCmdNotFoundError extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "FileCmdNotFoundError";
|
||||
}
|
||||
}
|
||||
exports.FileCmdNotFoundError = 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.
|
||||
|
|
@ -387,11 +394,18 @@ exports.getWorkflowRunAttempt = getWorkflowRunAttempt;
|
|||
const getFileType = async (filePath) => {
|
||||
let stderr = "";
|
||||
let stdout = "";
|
||||
let fileCmdPath;
|
||||
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], {
|
||||
await new toolrunner.ToolRunner(fileCmdPath, ["-L", filePath], {
|
||||
silent: true,
|
||||
listeners: {
|
||||
stdout: (data) => {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
54
lib/diagnostics.js
generated
54
lib/diagnostics.js
generated
|
|
@ -3,11 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.addDiagnostic = exports.makeDiagnostic = void 0;
|
||||
exports.flushDiagnostics = exports.logUnwrittenDiagnostics = exports.addDiagnostic = exports.makeDiagnostic = void 0;
|
||||
const fs_1 = require("fs");
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const logging_1 = require("./logging");
|
||||
const util_1 = require("./util");
|
||||
/** A list of diagnostics which have not yet been written to disk. */
|
||||
let unwrittenDiagnostics = [];
|
||||
/**
|
||||
* Constructs a new diagnostic message with the specified id and name, as well as optional additional data.
|
||||
*
|
||||
|
|
@ -25,12 +27,35 @@ function makeDiagnostic(id, name, data = undefined) {
|
|||
}
|
||||
exports.makeDiagnostic = 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.
|
||||
*/
|
||||
function addDiagnostic(config, language, diagnostic) {
|
||||
const logger = (0, logging_1.getActionsLogger)();
|
||||
const databasePath = (0, util_1.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 ((0, fs_1.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 });
|
||||
}
|
||||
}
|
||||
exports.addDiagnostic = addDiagnostic;
|
||||
/**
|
||||
* 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, language, diagnostic) {
|
||||
const logger = (0, logging_1.getActionsLogger)();
|
||||
const diagnosticsPath = path_1.default.resolve((0, util_1.getCodeQLDatabasePath)(config, language), "diagnostic", "codeql-action");
|
||||
try {
|
||||
|
|
@ -41,7 +66,30 @@ function addDiagnostic(config, language, diagnostic) {
|
|||
}
|
||||
catch (err) {
|
||||
logger.warning(`Unable to write diagnostic message to database: ${err}`);
|
||||
logger.debug(JSON.stringify(diagnostic));
|
||||
}
|
||||
}
|
||||
exports.addDiagnostic = addDiagnostic;
|
||||
/** Report if there are unwritten diagnostics and write them to the log. */
|
||||
function logUnwrittenDiagnostics() {
|
||||
const logger = (0, logging_1.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));
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.logUnwrittenDiagnostics = logUnwrittenDiagnostics;
|
||||
/** Writes all unwritten diagnostics to disk. */
|
||||
function flushDiagnostics(config) {
|
||||
const logger = (0, logging_1.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 = [];
|
||||
}
|
||||
exports.flushDiagnostics = flushDiagnostics;
|
||||
//# sourceMappingURL=diagnostics.js.map
|
||||
|
|
@ -1 +1 @@
|
|||
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":";;;;;;AAAA,2BAA8C;AAC9C,gDAAwB;AAIxB,uCAA6C;AAC7C,iCAA+C;AAmD/C;;;;;;;GAOG;AACH,SAAgB,cAAc,CAC5B,EAAU,EACV,IAAY,EACZ,OAA+C,SAAS;IAExD,OAAO;QACL,GAAG,IAAI;QACP,SAAS,EAAE,IAAI,EAAE,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACtD,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE;KACtC,CAAC;AACJ,CAAC;AAVD,wCAUC;AAED;;;;;GAKG;AACH,SAAgB,aAAa,CAC3B,MAAc,EACd,QAAkB,EAClB,UAA6B;IAE7B,MAAM,MAAM,GAAG,IAAA,0BAAgB,GAAE,CAAC;IAClC,MAAM,eAAe,GAAG,cAAI,CAAC,OAAO,CAClC,IAAA,4BAAqB,EAAC,MAAM,EAAE,QAAQ,CAAC,EACvC,YAAY,EACZ,eAAe,CAChB,CAAC;IAEF,IAAI,CAAC;QACH,gDAAgD;QAChD,IAAA,cAAS,EAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEhD,MAAM,QAAQ,GAAG,cAAI,CAAC,OAAO,CAC3B,eAAe,EACf,iBAAiB,UAAU,CAAC,SAAS,OAAO,CAC7C,CAAC;QAEF,IAAA,kBAAa,EAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;IACtD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,OAAO,CAAC,mDAAmD,GAAG,EAAE,CAAC,CAAC;IAC3E,CAAC;AACH,CAAC;AAzBD,sCAyBC"}
|
||||
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"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"}
|
||||
18
lib/init-action.js
generated
18
lib/init-action.js
generated
|
|
@ -30,6 +30,7 @@ const safe_which_1 = require("@chrisgavin/safe-which");
|
|||
const uuid_1 = require("uuid");
|
||||
const actions_util_1 = require("./actions-util");
|
||||
const api_client_1 = require("./api-client");
|
||||
const diagnostics_1 = require("./diagnostics");
|
||||
const environment_1 = require("./environment");
|
||||
const feature_flags_1 = require("./feature-flags");
|
||||
const init_1 = require("./init");
|
||||
|
|
@ -223,6 +224,17 @@ async function run() {
|
|||
}
|
||||
catch (e) {
|
||||
logger.warning(`Failed to determine the location of the Go binary: ${e}`);
|
||||
if (e instanceof actions_util_1.FileCmdNotFoundError) {
|
||||
(0, diagnostics_1.addDiagnostic)(config, languages_1.Language.go, (0, diagnostics_1.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",
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Limit RAM and threads for extractors. When running extractors, the CodeQL CLI obeys the
|
||||
|
|
@ -300,6 +312,9 @@ async function run() {
|
|||
core.exportVariable(key, value);
|
||||
}
|
||||
}
|
||||
// Write diagnostics to the database that we previously stored in memory because the database
|
||||
// did not exist until now.
|
||||
(0, diagnostics_1.flushDiagnostics)(config);
|
||||
core.setOutput("codeql-path", config.codeQLCmd);
|
||||
}
|
||||
catch (unwrappedError) {
|
||||
|
|
@ -308,6 +323,9 @@ async function run() {
|
|||
await sendCompletedStatusReport(startedAt, config, toolsDownloadDurationMs, toolsFeatureFlagsValid, toolsSource, toolsVersion, logger, error);
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
(0, diagnostics_1.logUnwrittenDiagnostics)();
|
||||
}
|
||||
await sendCompletedStatusReport(startedAt, config, toolsDownloadDurationMs, toolsFeatureFlagsValid, toolsSource, toolsVersion, logger);
|
||||
}
|
||||
function getTrapCachingEnabled() {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,50 @@
|
|||
name: "Go: diagnostic when `file` is not installed"
|
||||
description: "Checks that we emit a diagnostic if the `file` program is not installed"
|
||||
# only Linux is affected
|
||||
operatingSystems: ["ubuntu"]
|
||||
# pinned to a version which does not support statically linked binaries for indirect tracing
|
||||
versions: ["stable-v2.14.6"]
|
||||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
# We need a Go version that ships with statically linked binaries on Linux
|
||||
go-version: ">=1.21.0"
|
||||
- name: Remove `file` program
|
||||
run: |
|
||||
echo $(which file)
|
||||
sudo rm -rf $(which file)
|
||||
echo $(which file)
|
||||
- uses: ./../action/init
|
||||
with:
|
||||
languages: go
|
||||
tools: ${{ steps.prepare-test.outputs.tools-url }}
|
||||
- name: Build code
|
||||
shell: bash
|
||||
run: go build main.go
|
||||
- uses: ./../action/analyze
|
||||
with:
|
||||
output: "${{ runner.temp }}/results"
|
||||
upload-database: false
|
||||
- name: Check diagnostic appears in SARIF
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
SARIF_PATH: "${{ runner.temp }}/results/go.sarif"
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
const sarif = JSON.parse(fs.readFileSync(process.env['SARIF_PATH'], 'utf8'));
|
||||
const run = sarif.runs[0];
|
||||
|
||||
const toolExecutionNotifications = run.invocations[0].toolExecutionNotifications;
|
||||
const statusPageNotifications = toolExecutionNotifications.filter(n =>
|
||||
n.descriptor.id === 'go/workflow/file-program-unavailable' && n.properties?.visibility?.statusPage
|
||||
);
|
||||
if (statusPageNotifications.length !== 1) {
|
||||
core.setFailed(
|
||||
'Expected exactly one status page reporting descriptor for this diagnostic in the ' +
|
||||
`'runs[].invocations[].toolExecutionNotifications[]' SARIF property, but found ` +
|
||||
`${statusPageNotifications.length}. All notification reporting descriptors: ` +
|
||||
`${JSON.stringify(toolExecutionNotifications)}.`
|
||||
);
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue