create github issue with a formatted body

Co-authored-by: Harry Hogg <hhogg@spotify.com>
Signed-off-by: Himanshu Mishra <himanshu@orkohunter.net>
This commit is contained in:
Himanshu Mishra
2021-10-26 14:43:30 +02:00
parent 3cb1e8e6ee
commit bf76bb7a1d
+45 -13
View File
@@ -15,15 +15,22 @@
*/
// eslint-disable-next-line import/no-extraneous-dependencies
import { Octokit } from '@octokit/rest';
// The GitHub workflow .github/workflows/
import syncJsonOutput from '../snyk.json';
// Generated by GitHub workflow .github/workflows/snyk-github-issue-creator
import synkJsonOutput from '../snyk.json';
// Pattern for a GitHub Issue title
// Snyk vulnerability [Vulnerability ID]
type Vulnerability = {
description: string;
id: string;
snykId: string;
packages: Set<string>;
};
// Remember to fix me!
const GH_OWNER = 'orkohunter';
const GH_REPO = 'backstage';
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
});
@@ -32,14 +39,16 @@ const fetchSnykGithubIssueMap = async (): Promise<Record<string, number>> => {
const snykGithubIssueMap: Record<string, number> = {};
const iterator = octokit.paginate.iterator(octokit.rest.issues.listForRepo, {
owner: 'backstage',
repo: 'backstage',
// TODO(Harry/Himanshu): Use a CLI flag for these values.
owner: GH_OWNER,
repo: GH_REPO,
per_page: 100,
labels: 'snyk',
labels: 'snyk-vulnerability',
});
for await (const { data: issues } of iterator) {
for (const issue of issues) {
// Gets the Vulnerability ID from square braces
const match = /\([([A-Z0-9-]+)\])/.exec(issue.title);
if (match && match[1]) {
@@ -51,13 +60,31 @@ const fetchSnykGithubIssueMap = async (): Promise<Record<string, number>> => {
return snykGithubIssueMap;
};
const generateIssueBody = (vulnerability: Vulnerability) => {
let issueBody = '';
issueBody += '## Affecting Packages/Plugins\n';
vulnerability.packages.forEach(pkgName => {
issueBody += `* ${pkgName}\n`;
});
// TODO: Use displayTargetFile in snyk.json to create hyperlinks
issueBody += '\n';
issueBody += vulnerability.description;
return issueBody;
};
const createGithubIssue = (vulnerability: Vulnerability) => {
console.log(
`Create issue for vulnerability ${
vulnerability.id
vulnerability.snykId
} affecting packages ${Array.from(vulnerability.packages)}`,
);
// TODO(hhogg): Create github issue with the contents from a Snyk issue.
octokit.issues.create({
owner: GH_OWNER,
repo: GH_REPO,
title: `Snyk vulnerability [${vulnerability.snykId}]`,
labels: ['snyk-vulnerability', 'help wanted'],
body: generateIssueBody(vulnerability),
});
};
const updateGithubIssue = (
@@ -65,7 +92,7 @@ const updateGithubIssue = (
vulnerability: Vulnerability,
) => {
console.log(
`Update issue ${githubIssueId} for vulnerability ${vulnerability.id}`,
`Update issue ${githubIssueId} for vulnerability ${vulnerability.snykId}`,
);
// TODO(hhogg): Update github issue with the contents from a Snyk issue.
};
@@ -75,12 +102,12 @@ const closeGithubIssue = (githubIssueId: number) => {
// TODO(hhogg): Delete a github issue
};
(async () => {
async function main() {
const snykGithubIssueMap = await fetchSnykGithubIssueMap();
const vulnerabilityStore: Record<string, Vulnerability> = {};
// Group the Snyk vulnerabilities, and aggregate the affecting packages.
syncJsonOutput.forEach(({ projectName, vulnerabilities }) => {
synkJsonOutput.forEach(({ projectName, vulnerabilities }) => {
vulnerabilities.forEach(
({ id, description }: { id: string; description: string }) => {
if (id !== undefined && description !== undefined) {
@@ -89,7 +116,7 @@ const closeGithubIssue = (githubIssueId: number) => {
} else {
vulnerabilityStore[id] = {
description,
id,
snykId: id,
packages: new Set([projectName]),
};
}
@@ -113,4 +140,9 @@ const closeGithubIssue = (githubIssueId: number) => {
closeGithubIssue(githubIssueId);
}
});
})();
}
main().catch(error => {
console.error(error.stack);
process.exit(1);
});