Skip to content

Commit 3912872

Browse files
feat: add GitHub action summary (#5)
* feat: add summary * chore: removed dependencies used for testing purposes * fix: remove await statements used dealing with synchronous steps
1 parent e33f843 commit 3912872

File tree

4 files changed

+168
-100
lines changed

4 files changed

+168
-100
lines changed

.github/workflows/node.js.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ jobs:
1818
- name: "@nodesecure/ci analysis"
1919
uses: ./
2020
with:
21-
warnings: error
21+
warnings: warning

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Contributing to NodeSecure
2-
2+
33
Contributions to NodeSecure include code, documentation, answering user questions and
44
running the project's infrastructure
55

index.js

Lines changed: 7 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,18 @@
1+
// Import Third-party dependencies
12
import core from "@actions/core";
23
import { runPipeline } from "@nodesecure/ci";
34

5+
// Import Internal Dependencies
6+
import { generateSummary } from "./src/summary.js";
7+
48
const directory = core.getInput("directory") ?? process.env.GITHUB_WORKSPACE;
59
const strategy = core.getInput("strategy");
610
const vulnerabilities = core.getInput("vulnerabilities");
711
const warnings = core.getInput("warnings");
812
const reporters = core.getInput("reporters");
913

10-
function generateOutcomeWithEmoji(reportData, hasSpecificOutcome) {
11-
if (hasSpecificOutcome) {
12-
if (warnings === "warning") {
13-
return `🟡 ${reportData.length}`;
14-
} else if (warnings === "off") {
15-
return "(skipped)";
16-
}
17-
}
18-
19-
if (reportData.length === 0) {
20-
return `✅ 0`;
21-
}
22-
23-
return `❌ ${reportData.length}`;
24-
}
25-
26-
function generateOutcomeDepsWarnings(depsWarnings) {
27-
return depsWarnings.flatMap(({ warnings, package: packageName }) =>
28-
warnings.map((warning) => {
29-
const location = warning.location.flatMap((location) =>
30-
location.join(":")
31-
);
32-
return `${packageName} from ${warning.file}:${location}`;
33-
})
34-
);
35-
}
36-
37-
function generateOutcomeVulns(vulns) {
38-
return vulns.map((vuln) => {
39-
const vulnRanges = vuln.vulnerableRanges.join(", ");
40-
41-
return `[${vuln.severity}] ${vuln.package}: ${vuln.title} ${vulnRanges}`;
42-
});
43-
}
44-
4514
try {
46-
const result = await runPipeline({
15+
const report = await runPipeline({
4716
warnings,
4817
strategy,
4918
reporters,
@@ -52,69 +21,9 @@ try {
5221
autoExitAfterFailure: false,
5322
});
5423

55-
const vulns = result.data.dependencies.vulnerabilities;
56-
const depsWarnings = result.data.dependencies.warnings;
57-
const globalWarnings = result.data.warnings;
58-
const isReportSuccessful = result.status === "success";
59-
60-
await core.summary
61-
.addHeading(
62-
`${
63-
isReportSuccessful ? "✅" : "❌"
64-
} [${result.status.toUpperCase()}]: @nodesecure/ci analysis`
65-
)
66-
.addTable([
67-
[
68-
{ data: "Global warnings", header: true },
69-
{ data: "Dependency warnings", header: true },
70-
{ data: "Dependency vulnerabilities", header: true },
71-
],
72-
[
73-
generateOutcomeWithEmoji(globalWarnings),
74-
generateOutcomeWithEmoji(depsWarnings, true),
75-
generateOutcomeWithEmoji(vulns),
76-
],
77-
])
78-
.addBreak();
79-
80-
if (vulns.length > 0) {
81-
await core.summary
82-
.addHeading(
83-
`(${generateOutcomeWithEmoji(vulns)}) Dependencies vulnerabilities:`
84-
)
85-
.addList(generateOutcomeVulns(vulns));
86-
await core.summary.addSeparator();
87-
}
88-
89-
if (globalWarnings.length > 0) {
90-
await core.summary
91-
.addHeading(
92-
`(${generateOutcomeWithEmoji(globalWarnings)}) Global warnings:`
93-
)
94-
.addList(globalWarnings);
95-
await core.summary.addSeparator();
96-
}
97-
98-
if (depsWarnings.length > 0) {
99-
await core.summary
100-
.addHeading(
101-
`(${generateOutcomeWithEmoji(
102-
depsWarnings,
103-
true
104-
)}) Dependencies warnings:`
105-
)
106-
.addList(generateOutcomeDepsWarnings(depsWarnings));
107-
}
108-
109-
await core.summary
110-
.addSeparator()
111-
.addLink(
112-
"View @nodesecure/ci documentation",
113-
"https://github.com/NodeSecure/ci"
114-
)
115-
.write();
24+
await generateSummary(report);
11625

117-
if (result.status === "failure") {
26+
if (report.status === "failure") {
11827
core.setFailed(`[FAILURE]: @nodesecure/ci checks failed.`);
11928
}
12029
} catch (error) {

src/summary.js

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Import Third-party dependencies
2+
import core from "@actions/core";
3+
4+
const kSuccessEmoji = "✅";
5+
const kFailureEmoji = "❌";
6+
const kInfoEmoji = "🟡";
7+
const kNodeSecureLogoSrc =
8+
"https://avatars.githubusercontent.com/u/85318671?s=96&v=4";
9+
const kActionBadges = [
10+
"https://img.shields.io/badge/Maintained%3F-yes-green.svg",
11+
"https://img.shields.io/github/license/Naereen/StrapDown.js.svg",
12+
"https://img.shields.io/badge/dynamic/json.svg?url=https://raw.githubusercontent.com/NodeSecure/ci-action/master/package.json&query=$.version&label=Version",
13+
];
14+
15+
function generateOutcomeEmoji(reportData, hasSpecificOutcome) {
16+
if (hasSpecificOutcome) {
17+
const warnings = core.getInput("warnings");
18+
if (warnings === "warning") {
19+
return `${kInfoEmoji} ${reportData.length}`;
20+
} else if (warnings === "off") {
21+
return "(skipped)";
22+
}
23+
}
24+
25+
if (reportData.length === 0) {
26+
return `${kSuccessEmoji} 0`;
27+
}
28+
29+
return `${kFailureEmoji} ${reportData.length}`;
30+
}
31+
32+
function generateOutcomeGlobalWarnings(globalWarnings) {
33+
return `<ul>
34+
${globalWarnings.map((warning) => `<li>${warning}</li>`).join("")}
35+
</ul>`;
36+
}
37+
38+
function generateOutcomeDepsWarnings(depsWarnings) {
39+
return `
40+
<br />
41+
<table>
42+
<tbody>
43+
<tr>
44+
<th>Package</th>
45+
<th>Kind</th>
46+
<th>File</th>
47+
<th>Location</th>
48+
</tr>
49+
${depsWarnings
50+
.flatMap(({ warnings, package: packageName }) =>
51+
warnings.map((warning) => {
52+
const location = warning.location.flatMap((location) =>
53+
location.join(":")
54+
);
55+
return `<tr>
56+
<td>${packageName}</td>
57+
<td>${warning.kind}</td>
58+
<td>${warning.file}</td>
59+
<td>${location}</td>
60+
</tr>`;
61+
})
62+
)
63+
.join("")}
64+
</tbody>
65+
</table>
66+
`;
67+
}
68+
69+
function generateOutcomeVulns(vulns) {
70+
return `
71+
<br />
72+
<table>
73+
<tbody>
74+
<tr>
75+
<th>Package</th>
76+
<th>Severity</th>
77+
<th>Title</th>
78+
<th>Ranges</th>
79+
</tr>
80+
${vulns
81+
.map((vuln) => {
82+
const vulnRanges = vuln.vulnerableRanges.join(", ");
83+
return `<tr>
84+
<td>${vuln.package}</td>
85+
<td><${vuln.severity}</td>
86+
<td>${vuln.title}</td>
87+
<td>${vulnRanges}</td>
88+
</tr>`;
89+
})
90+
.join("")}
91+
</tbody>
92+
</table>
93+
`;
94+
}
95+
96+
function generateOutcomeTitle(report) {
97+
const isReportSuccessful = report.status === "success";
98+
const emojiOutcome = isReportSuccessful ? kSuccessEmoji : kFailureEmoji;
99+
const outcomeStatus = isReportSuccessful ? "successful" : "failed";
100+
return `${emojiOutcome} [${report.status.toUpperCase()}]: @nodesecure/ci security checks ${outcomeStatus}.`;
101+
}
102+
103+
export async function generateSummary(report) {
104+
const vulns = report.data.dependencies.vulnerabilities;
105+
const depsWarnings = report.data.dependencies.warnings;
106+
const globalWarnings = report.data.warnings;
107+
108+
core.summary
109+
.addImage(kNodeSecureLogoSrc, "NodeSecure", { width: 50, height: 50 })
110+
.addHeading(generateOutcomeTitle(report), 3)
111+
.addTable([
112+
[
113+
{ data: "Global warnings", header: true },
114+
{ data: "Dependency warnings", header: true },
115+
{ data: "Dependency vulnerabilities", header: true },
116+
],
117+
[
118+
generateOutcomeEmoji(globalWarnings),
119+
generateOutcomeEmoji(depsWarnings, true),
120+
generateOutcomeEmoji(vulns),
121+
],
122+
])
123+
.addBreak();
124+
125+
if (globalWarnings.length > 0) {
126+
core.summary.addDetails(
127+
`[${generateOutcomeEmoji(globalWarnings)}] Global warnings:`,
128+
generateOutcomeGlobalWarnings(globalWarnings)
129+
);
130+
core.summary.addSeparator();
131+
}
132+
133+
if (depsWarnings.length > 0) {
134+
core.summary.addDetails(
135+
`[${generateOutcomeEmoji(depsWarnings, true)}] Dependencies warnings:`,
136+
generateOutcomeDepsWarnings(depsWarnings)
137+
);
138+
core.summary.addSeparator();
139+
}
140+
141+
if (vulns.length > 0) {
142+
core.summary.addDetails(
143+
`[${generateOutcomeEmoji(vulns)}] Dependencies vulnerabilities:`,
144+
generateOutcomeVulns(vulns)
145+
);
146+
core.summary.addSeparator();
147+
}
148+
149+
for (const badgeSrc of kActionBadges) {
150+
core.summary.addImage(badgeSrc);
151+
}
152+
153+
await core.summary
154+
.addLink(
155+
"View @nodesecure/ci documentation",
156+
"https://github.com/NodeSecure/ci"
157+
)
158+
.write();
159+
}

0 commit comments

Comments
 (0)