Skip to content

Commit ab965e5

Browse files
authored
fix: include cause of parsing errors in action output logs (#431)
1 parent c7dafcf commit ab965e5

File tree

4 files changed

+78
-9
lines changed

4 files changed

+78
-9
lines changed

src/content.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export default class Content {
5555
* @returns {Content} - the parsed JSON payload to use in requests.
5656
*/
5757
getContentPayload(config) {
58+
const errors = [];
5859
if (!config.inputs.payload) {
5960
throw new SlackError(
6061
config.core,
@@ -71,8 +72,7 @@ export default class Content {
7172
return /** @type {Content} */ (content);
7273
} catch (error) {
7374
if (error instanceof Error) {
74-
config.core.debug("Failed to parse input payload as YAML");
75-
config.core.debug(error.message);
75+
errors.push(error);
7676
}
7777
}
7878
try {
@@ -90,11 +90,12 @@ export default class Content {
9090
}
9191
return JSON.parse(trimmed);
9292
} catch (/** @type {any} */ error) {
93+
errors.unshift(error);
9394
throw new SlackError(
9495
config.core,
9596
"Invalid input! Failed to parse contents of the provided payload",
9697
{
97-
cause: error,
98+
cause: { values: errors },
9899
},
99100
);
100101
}
@@ -140,7 +141,7 @@ export default class Content {
140141
config.core,
141142
"Invalid input! Failed to parse contents of the provided payload file",
142143
{
143-
cause: error,
144+
cause: { values: [error] },
144145
},
145146
);
146147
}

src/errors.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import core from "@actions/core";
22

3+
/**
4+
* @typedef Cause
5+
* @property {Error[]} [values] - Caught exceptions.
6+
*/
7+
38
/**
49
* SlackError is a custom error wrapper for known errors of Slack GitHub Action.
510
*/
611
export default class SlackError extends Error {
712
/**
813
* @typedef Options
9-
* @property {Error} [cause] - thrown exception of this error.
14+
* @property {Cause} [cause] - Reason for an error.
1015
*/
1116

1217
/**

src/index.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,21 @@ import send from "./send.js";
66
* from the send.js file for testing purposes.
77
*/
88
try {
9-
send(core);
9+
await send(core);
1010
} catch (error) {
1111
if (error instanceof Error) {
1212
core.error(error.message);
13-
core.debug(`${error.name} cause: ${error.cause}`);
14-
core.debug(`${error.name} stack: ${error.stack}`);
13+
/** @type {import('./errors.js').Cause} */
14+
const causes = /** @type {any} */ (error.cause);
15+
if (causes?.values) {
16+
for (const cause of causes.values) {
17+
core.info(`${cause.stack}`);
18+
}
19+
} else {
20+
core.info(`${error.stack}`);
21+
}
1522
} else {
1623
core.error(`${error}`);
1724
}
25+
throw error;
1826
}

test/content.spec.js

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import path from "node:path";
22
import core from "@actions/core";
33
import { assert } from "chai";
4+
import { YAMLException } from "js-yaml";
45
import Config from "../src/config.js";
56
import Content from "../src/content.js";
67
import SlackError from "../src/errors.js";
@@ -190,6 +191,11 @@ describe("content", () => {
190191
err.message,
191192
"Invalid input! Failed to parse contents of the provided payload",
192193
);
194+
assert.isDefined(err.cause?.values);
195+
assert.equal(err.cause.values.length, 2);
196+
const [jsonError, yamlError] = err.cause.values;
197+
assert.isTrue(jsonError instanceof SyntaxError);
198+
assert.isTrue(yamlError instanceof YAMLException);
193199
} else {
194200
assert.fail("Failed to throw a SlackError", err);
195201
}
@@ -334,14 +340,63 @@ describe("content", () => {
334340
err.message,
335341
"Invalid input! Failed to parse contents of the provided payload file",
336342
);
343+
assert.isDefined(err.cause?.values);
344+
assert.equal(err.cause.values.length, 1);
337345
assert.include(
338-
err.cause.message,
346+
err.cause.values[0].message,
339347
"Invalid input! Failed to parse file extension unknown.md",
340348
);
341349
} else {
342350
assert.fail("Failed to throw a SlackError", err);
343351
}
344352
}
345353
});
354+
355+
it("fails if invalid JSON exists in the input payload", async () => {
356+
mocks.core.getInput.withArgs("payload-file-path").returns("example.json");
357+
mocks.fs.readFileSync
358+
.withArgs(path.resolve("example.json"), "utf-8")
359+
.returns(`{
360+
"message": "a truncated file without an end`);
361+
try {
362+
await send(mocks.core);
363+
assert.fail("Failed to throw for invalid JSON");
364+
} catch (err) {
365+
if (err instanceof SlackError) {
366+
assert.include(
367+
err.message,
368+
"Invalid input! Failed to parse contents of the provided payload file",
369+
);
370+
assert.isDefined(err.cause?.values);
371+
assert.equal(err.cause.values.length, 1);
372+
assert.isTrue(err.cause.values[0] instanceof SyntaxError);
373+
} else {
374+
assert.fail("Failed to throw a SlackError", err);
375+
}
376+
}
377+
});
378+
379+
it("fails if invalid YAML exists in the input payload", async () => {
380+
mocks.core.getInput.withArgs("payload-file-path").returns("example.yaml");
381+
mocks.fs.readFileSync
382+
.withArgs(path.resolve("example.yaml"), "utf-8")
383+
.returns(`- "message": "assigned": "values"`);
384+
try {
385+
await send(mocks.core);
386+
assert.fail("Failed to throw for invalid YAML");
387+
} catch (err) {
388+
if (err instanceof SlackError) {
389+
assert.include(
390+
err.message,
391+
"Invalid input! Failed to parse contents of the provided payload file",
392+
);
393+
assert.isDefined(err.cause?.values);
394+
assert.equal(err.cause.values.length, 1);
395+
assert.isTrue(err.cause.values[0] instanceof YAMLException);
396+
} else {
397+
assert.fail("Failed to throw a SlackError", err);
398+
}
399+
}
400+
});
346401
});
347402
});

0 commit comments

Comments
 (0)