Merge pull request #4161 from github/mbg/improve-json-failures

Add context to JSON parse errors
This commit is contained in:
Michael B. Gale
2026-09-24 11:18:23 +00:00
committed by GitHub
12 changed files with 147 additions and 63 deletions

96
lib/entry-points.js generated
View File

@@ -145996,7 +145996,13 @@ function parseMatrixInput(matrixInput) {
if (matrixInput === void 0 || matrixInput === "null") {
return void 0;
}
return JSON.parse(matrixInput);
try {
return JSON.parse(matrixInput);
} catch (err) {
throw new Error(
`Failed to parse matrix input '${matrixInput}': ${getErrorMessage(err)}`
);
}
}
function wrapError(error3) {
return error3 instanceof Error ? error3 : new Error(String(error3));
@@ -146064,7 +146070,11 @@ var BuildMode = /* @__PURE__ */ ((BuildMode3) => {
return BuildMode3;
})(BuildMode || {});
function cloneObject(obj) {
return JSON.parse(JSON.stringify(obj));
try {
return JSON.parse(JSON.stringify(obj));
} catch (err) {
throw new Error(`Cloning object failed: ${getErrorMessage(err)}`);
}
}
async function cleanUpPath(file, name, logger) {
logger.debug(`Cleaning up ${name}.`);
@@ -146389,14 +146399,22 @@ var persistInputs = function(env = getEnv()) {
);
core3.saveState(persistedInputsKey, JSON.stringify(inputEnvironmentVariables));
};
var restoreInputs = function() {
const persistedInputs = core3.getState(persistedInputsKey);
if (persistedInputs) {
for (const [name, value] of JSON.parse(persistedInputs)) {
process.env[name] = value;
function restoreInputs(logger) {
try {
const persistedInputsValue = core3.getState(persistedInputsKey);
if (persistedInputsValue) {
const persistedInputs = JSON.parse(persistedInputsValue);
for (const [name, value] of persistedInputs) {
process.env[name] = value;
}
}
} catch (err) {
logger.error(`Unable to restore inputs: ${getErrorMessage(err)}`);
throw new Error(
"Failed to restore inputs from the state set by this action's main execution."
);
}
};
}
function getPullRequestBranches(env = getEnv()) {
const pullRequest = github.context.payload.pull_request;
if (pullRequest) {
@@ -152818,25 +152836,28 @@ async function endTracingForCluster(codeql, config, logger) {
);
}
}
async function getTracerConfigForCluster(config) {
const tracingEnvVariables = JSON.parse(
fs15.readFileSync(
path14.resolve(
config.dbLocation,
"temp/tracingEnvironment/start-tracing.json"
),
"utf8"
)
async function getTracerConfigForCluster(logger, config) {
const filePath = path14.resolve(
config.dbLocation,
"temp/tracingEnvironment/start-tracing.json"
);
return {
env: tracingEnvVariables
};
try {
const tracingEnvVariables = JSON.parse(fs15.readFileSync(filePath, "utf8"));
return {
env: tracingEnvVariables
};
} catch (err) {
logger.error(
`Failed to parse tracing environment from '${filePath}': ${getErrorMessage(err)}`
);
throw new Error(`Failed to parse tracing environment from '${filePath}'.`);
}
}
async function getCombinedTracerConfig(codeql, config) {
async function getCombinedTracerConfig(logger, codeql, config) {
if (!await shouldEnableIndirectTracing(codeql, config)) {
return void 0;
}
return await getTracerConfigForCluster(config);
return await getTracerConfigForCluster(logger, config);
}
// src/codeql.ts
@@ -153265,7 +153286,14 @@ async function getCodeQLForCmd(logger, cmd, checkVersion) {
}
}
).exec();
return JSON.parse(extractorPath);
try {
return JSON.parse(extractorPath);
} catch (err) {
throw new Error(
`Failed to parse extractor path for '${language}' from CLI: ${getErrorMessage(err)}
Output was: ${extractorPath}`
);
}
},
async resolveQueriesStartingPacks(queries) {
const codeqlArgs = [
@@ -155738,7 +155766,13 @@ function getToolNames(sarifFile) {
return Object.keys(toolNames);
}
function readSarifFile(sarifFilePath) {
return JSON.parse(fs21.readFileSync(sarifFilePath, "utf8"));
try {
return JSON.parse(fs21.readFileSync(sarifFilePath, "utf8"));
} catch (err) {
throw new Error(
`Parsing SARIF file at '${sarifFilePath}' failed: ${getErrorMessage(err)}`
);
}
}
function combineSarifFiles(sarifFiles, logger) {
logger.info(`Loading SARIF file(s)`);
@@ -158883,7 +158917,7 @@ var import_async = __toESM(require_async(), 1);
var import_path7 = require("path");
// node_modules/archiver/lib/error.js
var import_util35 = __toESM(require("util"), 1);
var import_util36 = __toESM(require("util"), 1);
var ERROR_CODES = {
ABORTED: "archive was aborted",
DIRECTORYDIRPATHREQUIRED: "diretory dirpath argument must be a non-empty string value",
@@ -158908,7 +158942,7 @@ function ArchiverError(code, data) {
this.code = code;
this.data = data;
}
import_util35.default.inherits(ArchiverError, Error);
import_util36.default.inherits(ArchiverError, Error);
// node_modules/archiver/lib/core.js
var import_readable_stream2 = __toESM(require_ours(), 1);
@@ -161703,8 +161737,8 @@ async function createDatabaseBundleCli(codeql, config, language) {
// src/analyze-action-post.ts
async function runWrapper2() {
try {
restoreInputs();
const logger = getActionsLogger();
restoreInputs(logger);
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);
if (process.env["CODEQL_ACTION_INIT_HAS_RUN" /* INIT_ACTION_HAS_RUN */] === "true") {
@@ -162599,7 +162633,7 @@ exec ${goBinaryPath} "$@"`
qlconfigFile
);
}
const tracerConfig = await getCombinedTracerConfig(codeql, config);
const tracerConfig = await getCombinedTracerConfig(logger, codeql, config);
if (tracerConfig !== void 0) {
for (const [key, value] of Object.entries(tracerConfig.env)) {
core22.exportVariable(key, value);
@@ -163000,7 +163034,7 @@ async function run4(startedAt) {
let dependencyCachingUsage;
try {
const jobStatus2 = getOptionalInput("job-status");
restoreInputs();
restoreInputs(logger);
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);
const repositoryNwo = getRepositoryNwo();
@@ -164151,7 +164185,7 @@ var core29 = __toESM(require_core());
async function runWrapper9() {
const logger = getActionsLogger();
try {
restoreInputs();
restoreInputs(logger);
const pid = core29.getState("proxy-process-pid");
if (pid) {
process.kill(Number(pid));
@@ -164302,8 +164336,8 @@ async function runWrapper10() {
var core31 = __toESM(require_core());
async function runWrapper11() {
try {
restoreInputs();
const logger = getActionsLogger();
restoreInputs(logger);
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);
if (process.env["CODEQL_ACTION_INIT_HAS_RUN" /* INIT_ACTION_HAS_RUN */] !== "true") {

View File

@@ -14,6 +14,7 @@ import {
getCodeQLDatabasePath,
ConfigurationError,
getEnv,
getErrorMessage,
} from "./util";
/**
@@ -412,14 +413,23 @@ export const persistInputs = function (env: Env = getEnv()) {
/**
* Restores all inputs to the action from the persisted state.
*/
export const restoreInputs = function () {
const persistedInputs = core.getState(persistedInputsKey);
if (persistedInputs) {
for (const [name, value] of JSON.parse(persistedInputs)) {
process.env[name] = value;
export function restoreInputs(logger: Logger) {
try {
const persistedInputsValue = core.getState(persistedInputsKey);
if (persistedInputsValue) {
const persistedInputs = JSON.parse(persistedInputsValue);
for (const [name, value] of persistedInputs) {
process.env[name] = value;
}
}
} catch (err) {
logger.error(`Unable to restore inputs: ${getErrorMessage(err)}`);
throw new Error(
"Failed to restore inputs from the state set by this action's main execution.",
);
}
};
}
export interface PullRequestBranches {
base: string;

View File

@@ -25,8 +25,8 @@ export async function runWrapper() {
// possible, and only use safe functions outside.
try {
actionsUtil.restoreInputs();
const logger = getActionsLogger();
actionsUtil.restoreInputs(logger);
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);

View File

@@ -952,7 +952,13 @@ async function getCodeQLForCmd(
},
},
).exec();
return JSON.parse(extractorPath) as string;
try {
return JSON.parse(extractorPath) as string;
} catch (err) {
throw new Error(
`Failed to parse extractor path for '${language}' from CLI: ${getErrorMessage(err)}\nOutput was: ${extractorPath}`,
);
}
},
async resolveQueriesStartingPacks(queries: string[]): Promise<string[]> {
const codeqlArgs = [

View File

@@ -62,7 +62,7 @@ async function run(startedAt: Date) {
const jobStatus = getOptionalInput("job-status");
// Restore inputs from `init` Action.
restoreInputs();
restoreInputs(logger);
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);

View File

@@ -716,7 +716,7 @@ async function run(
);
}
const tracerConfig = await getCombinedTracerConfig(codeql, config);
const tracerConfig = await getCombinedTracerConfig(logger, codeql, config);
if (tracerConfig !== undefined) {
for (const [key, value] of Object.entries(tracerConfig.env)) {
core.exportVariable(key, value);

View File

@@ -1,6 +1,7 @@
import * as fs from "fs";
import { Logger } from "../logging";
import { getErrorMessage } from "../util";
import * as sarif from "sarif";
@@ -48,7 +49,13 @@ export function getToolNames(sarifFile: Partial<sarif.Log>): string[] {
* @returns The resulting JSON value, cast to a SARIF `Log`.
*/
export function readSarifFile(sarifFilePath: string): Partial<sarif.Log> {
return JSON.parse(fs.readFileSync(sarifFilePath, "utf8")) as sarif.Log;
try {
return JSON.parse(fs.readFileSync(sarifFilePath, "utf8")) as sarif.Log;
} catch (err) {
throw new Error(
`Parsing SARIF file at '${sarifFilePath}' failed: ${getErrorMessage(err)}`,
);
}
}
// Takes a list of paths to sarif files and combines them together,

View File

@@ -20,7 +20,7 @@ export async function runWrapper() {
try {
// Restore inputs from `start-proxy` Action.
actionsUtil.restoreInputs();
actionsUtil.restoreInputs(logger);
// Kill the running proxy
const pid = core.getState("proxy-process-pid");

View File

@@ -7,7 +7,12 @@ import * as sinon from "sinon";
import { CodeQL, getCodeQLForTesting } from "./codeql";
import * as configUtils from "./config-utils";
import { BuiltInLanguage } from "./languages";
import { createTestConfig, makeVersionInfo, setupTests } from "./testing-utils";
import {
createTestConfig,
makeVersionInfo,
RecordingLogger,
setupTests,
} from "./testing-utils";
import { ToolsFeature } from "./tools-features";
import { getCombinedTracerConfig } from "./tracer-config";
import * as util from "./util";
@@ -42,18 +47,20 @@ async function stubCodeql(
}
test("getCombinedTracerConfig - return undefined when no languages are traced languages", async (t) => {
const logger = new RecordingLogger();
await util.withTmpDir(async (tmpDir) => {
const config = getTestConfig(tmpDir);
// No traced languages
config.languages = [BuiltInLanguage.javascript, BuiltInLanguage.python];
t.deepEqual(
await getCombinedTracerConfig(await stubCodeql(), config),
await getCombinedTracerConfig(logger, await stubCodeql(), config),
undefined,
);
});
});
test("getCombinedTracerConfig", async (t) => {
const logger = new RecordingLogger();
await util.withTmpDir(async (tmpDir) => {
const config = getTestConfig(tmpDir);
@@ -82,7 +89,11 @@ test("getCombinedTracerConfig", async (t) => {
);
fs.writeFileSync(startTracingJson, JSON.stringify(startTracingEnv));
const result = await getCombinedTracerConfig(await stubCodeql(), config);
const result = await getCombinedTracerConfig(
logger,
await stubCodeql(),
config,
);
t.notDeepEqual(result, undefined);
t.false(Object.prototype.hasOwnProperty.call(result?.env, "CODEQL_RUNNER"));

View File

@@ -4,7 +4,7 @@ import * as path from "path";
import { type CodeQL } from "./codeql";
import { type Config } from "./config-utils";
import { Logger } from "./logging";
import { asyncSome, BuildMode } from "./util";
import { asyncSome, BuildMode, getErrorMessage } from "./util";
export type TracerConfig = {
env: { [key: string]: string };
@@ -77,23 +77,28 @@ export async function endTracingForCluster(
}
async function getTracerConfigForCluster(
logger: Logger,
config: Config,
): Promise<TracerConfig> {
const tracingEnvVariables = JSON.parse(
fs.readFileSync(
path.resolve(
config.dbLocation,
"temp/tracingEnvironment/start-tracing.json",
),
"utf8",
),
const filePath = path.resolve(
config.dbLocation,
"temp/tracingEnvironment/start-tracing.json",
);
return {
env: tracingEnvVariables,
};
try {
const tracingEnvVariables = JSON.parse(fs.readFileSync(filePath, "utf8"));
return {
env: tracingEnvVariables,
};
} catch (err) {
logger.error(
`Failed to parse tracing environment from '${filePath}': ${getErrorMessage(err)}`,
);
throw new Error(`Failed to parse tracing environment from '${filePath}'.`);
}
}
export async function getCombinedTracerConfig(
logger: Logger,
codeql: CodeQL,
config: Config,
): Promise<TracerConfig | undefined> {
@@ -101,5 +106,5 @@ export async function getCombinedTracerConfig(
return undefined;
}
return await getTracerConfigForCluster(config);
return await getTracerConfigForCluster(logger, config);
}

View File

@@ -17,9 +17,10 @@ export async function runWrapper() {
// possible, and only use safe functions outside.
try {
// Restore inputs from `upload-sarif` Action.
actionsUtil.restoreInputs();
const logger = getActionsLogger();
// Restore inputs from `upload-sarif` Action.
actionsUtil.restoreInputs(logger);
const gitHubVersion = await getGitHubVersion();
checkGitHubVersionInRange(gitHubVersion, logger);

View File

@@ -894,7 +894,13 @@ export function parseMatrixInput(
if (matrixInput === undefined || matrixInput === "null") {
return undefined;
}
return JSON.parse(matrixInput) as { [key: string]: string };
try {
return JSON.parse(matrixInput) as { [key: string]: string };
} catch (err) {
throw new Error(
`Failed to parse matrix input '${matrixInput}': ${getErrorMessage(err)}`,
);
}
}
export function wrapError(error: unknown): Error {
@@ -1037,7 +1043,11 @@ export enum BuildMode {
}
export function cloneObject<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj)) as T;
try {
return JSON.parse(JSON.stringify(obj)) as T;
} catch (err) {
throw new Error(`Cloning object failed: ${getErrorMessage(err)}`);
}
}
export async function cleanUpPath(file: string, name: string, logger: Logger) {