fix: include cause of parsing errors in action output logs (#431)
This commit is contained in:
+5
-4
@@ -55,6 +55,7 @@ export default class Content {
|
|||||||
* @returns {Content} - the parsed JSON payload to use in requests.
|
* @returns {Content} - the parsed JSON payload to use in requests.
|
||||||
*/
|
*/
|
||||||
getContentPayload(config) {
|
getContentPayload(config) {
|
||||||
|
const errors = [];
|
||||||
if (!config.inputs.payload) {
|
if (!config.inputs.payload) {
|
||||||
throw new SlackError(
|
throw new SlackError(
|
||||||
config.core,
|
config.core,
|
||||||
@@ -71,8 +72,7 @@ export default class Content {
|
|||||||
return /** @type {Content} */ (content);
|
return /** @type {Content} */ (content);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
config.core.debug("Failed to parse input payload as YAML");
|
errors.push(error);
|
||||||
config.core.debug(error.message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -90,11 +90,12 @@ export default class Content {
|
|||||||
}
|
}
|
||||||
return JSON.parse(trimmed);
|
return JSON.parse(trimmed);
|
||||||
} catch (/** @type {any} */ error) {
|
} catch (/** @type {any} */ error) {
|
||||||
|
errors.unshift(error);
|
||||||
throw new SlackError(
|
throw new SlackError(
|
||||||
config.core,
|
config.core,
|
||||||
"Invalid input! Failed to parse contents of the provided payload",
|
"Invalid input! Failed to parse contents of the provided payload",
|
||||||
{
|
{
|
||||||
cause: error,
|
cause: { values: errors },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -140,7 +141,7 @@ export default class Content {
|
|||||||
config.core,
|
config.core,
|
||||||
"Invalid input! Failed to parse contents of the provided payload file",
|
"Invalid input! Failed to parse contents of the provided payload file",
|
||||||
{
|
{
|
||||||
cause: error,
|
cause: { values: [error] },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -1,12 +1,17 @@
|
|||||||
import core from "@actions/core";
|
import core from "@actions/core";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Cause
|
||||||
|
* @property {Error[]} [values] - Caught exceptions.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SlackError is a custom error wrapper for known errors of Slack GitHub Action.
|
* SlackError is a custom error wrapper for known errors of Slack GitHub Action.
|
||||||
*/
|
*/
|
||||||
export default class SlackError extends Error {
|
export default class SlackError extends Error {
|
||||||
/**
|
/**
|
||||||
* @typedef Options
|
* @typedef Options
|
||||||
* @property {Error} [cause] - thrown exception of this error.
|
* @property {Cause} [cause] - Reason for an error.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+11
-3
@@ -6,13 +6,21 @@ import send from "./send.js";
|
|||||||
* from the send.js file for testing purposes.
|
* from the send.js file for testing purposes.
|
||||||
*/
|
*/
|
||||||
try {
|
try {
|
||||||
send(core);
|
await send(core);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
core.error(error.message);
|
core.error(error.message);
|
||||||
core.debug(`${error.name} cause: ${error.cause}`);
|
/** @type {import('./errors.js').Cause} */
|
||||||
core.debug(`${error.name} stack: ${error.stack}`);
|
const causes = /** @type {any} */ (error.cause);
|
||||||
|
if (causes?.values) {
|
||||||
|
for (const cause of causes.values) {
|
||||||
|
core.info(`${cause.stack}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
core.info(`${error.stack}`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
core.error(`${error}`);
|
core.error(`${error}`);
|
||||||
}
|
}
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
+56
-1
@@ -1,6 +1,7 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import core from "@actions/core";
|
import core from "@actions/core";
|
||||||
import { assert } from "chai";
|
import { assert } from "chai";
|
||||||
|
import { YAMLException } from "js-yaml";
|
||||||
import Config from "../src/config.js";
|
import Config from "../src/config.js";
|
||||||
import Content from "../src/content.js";
|
import Content from "../src/content.js";
|
||||||
import SlackError from "../src/errors.js";
|
import SlackError from "../src/errors.js";
|
||||||
@@ -190,6 +191,11 @@ describe("content", () => {
|
|||||||
err.message,
|
err.message,
|
||||||
"Invalid input! Failed to parse contents of the provided payload",
|
"Invalid input! Failed to parse contents of the provided payload",
|
||||||
);
|
);
|
||||||
|
assert.isDefined(err.cause?.values);
|
||||||
|
assert.equal(err.cause.values.length, 2);
|
||||||
|
const [jsonError, yamlError] = err.cause.values;
|
||||||
|
assert.isTrue(jsonError instanceof SyntaxError);
|
||||||
|
assert.isTrue(yamlError instanceof YAMLException);
|
||||||
} else {
|
} else {
|
||||||
assert.fail("Failed to throw a SlackError", err);
|
assert.fail("Failed to throw a SlackError", err);
|
||||||
}
|
}
|
||||||
@@ -334,8 +340,10 @@ describe("content", () => {
|
|||||||
err.message,
|
err.message,
|
||||||
"Invalid input! Failed to parse contents of the provided payload file",
|
"Invalid input! Failed to parse contents of the provided payload file",
|
||||||
);
|
);
|
||||||
|
assert.isDefined(err.cause?.values);
|
||||||
|
assert.equal(err.cause.values.length, 1);
|
||||||
assert.include(
|
assert.include(
|
||||||
err.cause.message,
|
err.cause.values[0].message,
|
||||||
"Invalid input! Failed to parse file extension unknown.md",
|
"Invalid input! Failed to parse file extension unknown.md",
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -343,5 +351,52 @@ describe("content", () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("fails if invalid JSON exists in the input payload", async () => {
|
||||||
|
mocks.core.getInput.withArgs("payload-file-path").returns("example.json");
|
||||||
|
mocks.fs.readFileSync
|
||||||
|
.withArgs(path.resolve("example.json"), "utf-8")
|
||||||
|
.returns(`{
|
||||||
|
"message": "a truncated file without an end`);
|
||||||
|
try {
|
||||||
|
await send(mocks.core);
|
||||||
|
assert.fail("Failed to throw for invalid JSON");
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof SlackError) {
|
||||||
|
assert.include(
|
||||||
|
err.message,
|
||||||
|
"Invalid input! Failed to parse contents of the provided payload file",
|
||||||
|
);
|
||||||
|
assert.isDefined(err.cause?.values);
|
||||||
|
assert.equal(err.cause.values.length, 1);
|
||||||
|
assert.isTrue(err.cause.values[0] instanceof SyntaxError);
|
||||||
|
} else {
|
||||||
|
assert.fail("Failed to throw a SlackError", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails if invalid YAML exists in the input payload", async () => {
|
||||||
|
mocks.core.getInput.withArgs("payload-file-path").returns("example.yaml");
|
||||||
|
mocks.fs.readFileSync
|
||||||
|
.withArgs(path.resolve("example.yaml"), "utf-8")
|
||||||
|
.returns(`- "message": "assigned": "values"`);
|
||||||
|
try {
|
||||||
|
await send(mocks.core);
|
||||||
|
assert.fail("Failed to throw for invalid YAML");
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof SlackError) {
|
||||||
|
assert.include(
|
||||||
|
err.message,
|
||||||
|
"Invalid input! Failed to parse contents of the provided payload file",
|
||||||
|
);
|
||||||
|
assert.isDefined(err.cause?.values);
|
||||||
|
assert.equal(err.cause.values.length, 1);
|
||||||
|
assert.isTrue(err.cause.values[0] instanceof YAMLException);
|
||||||
|
} else {
|
||||||
|
assert.fail("Failed to throw a SlackError", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user