-
Notifications
You must be signed in to change notification settings - Fork 19
Initial attempt at adding an option for grouping related issues #118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,10 @@ | ||
| import type { Finding, ResolvedFiling, RepeatedFiling } from "./types.d.js"; | ||
| import type { | ||
| Finding, | ||
| ResolvedFiling, | ||
| RepeatedFiling, | ||
| FindingGroupIssue, | ||
| Filing, | ||
| } from "./types.d.js"; | ||
| import process from "node:process"; | ||
| import core from "@actions/core"; | ||
| import { Octokit } from "@octokit/core"; | ||
|
|
@@ -11,18 +17,20 @@ import { isResolvedFiling } from "./isResolvedFiling.js"; | |
| import { openIssue } from "./openIssue.js"; | ||
| import { reopenIssue } from "./reopenIssue.js"; | ||
| import { updateFilingsWithNewFindings } from "./updateFilingsWithNewFindings.js"; | ||
| import type { OctokitResponse } from "@octokit/types"; | ||
| const OctokitWithThrottling = Octokit.plugin(throttling); | ||
|
|
||
| export default async function () { | ||
| core.info("Started 'file' action"); | ||
| const findings: Finding[] = JSON.parse( | ||
| core.getInput("findings", { required: true }) | ||
| core.getInput("findings", { required: true }), | ||
| ); | ||
| const repoWithOwner = core.getInput("repository", { required: true }); | ||
| const token = core.getInput("token", { required: true }); | ||
| const cachedFilings: (ResolvedFiling | RepeatedFiling)[] = JSON.parse( | ||
| core.getInput("cached_filings", { required: false }) || "[]" | ||
| core.getInput("cached_filings", { required: false }) || "[]", | ||
| ); | ||
| const shouldOpenGroupedIssues = core.getBooleanInput("open_grouped_issues"); | ||
| core.debug(`Input: 'findings: ${JSON.stringify(findings)}'`); | ||
| core.debug(`Input: 'repository: ${repoWithOwner}'`); | ||
| core.debug(`Input: 'cached_filings: ${JSON.stringify(cachedFilings)}'`); | ||
|
|
@@ -32,7 +40,7 @@ export default async function () { | |
| throttle: { | ||
| onRateLimit: (retryAfter, options, octokit, retryCount) => { | ||
| octokit.log.warn( | ||
| `Request quota exhausted for request ${options.method} ${options.url}` | ||
| `Request quota exhausted for request ${options.method} ${options.url}`, | ||
| ); | ||
| if (retryCount < 3) { | ||
| octokit.log.info(`Retrying after ${retryAfter} seconds!`); | ||
|
|
@@ -41,7 +49,7 @@ export default async function () { | |
| }, | ||
| onSecondaryRateLimit: (retryAfter, options, octokit, retryCount) => { | ||
| octokit.log.warn( | ||
| `Secondary rate limit hit for request ${options.method} ${options.url}` | ||
| `Secondary rate limit hit for request ${options.method} ${options.url}`, | ||
| ); | ||
| if (retryCount < 3) { | ||
| octokit.log.info(`Retrying after ${retryAfter} seconds!`); | ||
|
|
@@ -52,8 +60,12 @@ export default async function () { | |
| }); | ||
| const filings = updateFilingsWithNewFindings(cachedFilings, findings); | ||
|
|
||
| // Track new issues for grouping | ||
| const newIssuesByProblemShort: Record<string, FindingGroupIssue[]> = {}; | ||
| const trackingIssueUrls: Record<string, string> = {}; | ||
|
|
||
| for (const filing of filings) { | ||
| let response; | ||
| let response: OctokitResponse<any> | undefined; | ||
| try { | ||
| if (isResolvedFiling(filing)) { | ||
| // Close the filing’s issue (if necessary) | ||
|
|
@@ -62,7 +74,18 @@ export default async function () { | |
| } else if (isNewFiling(filing)) { | ||
| // Open a new issue for the filing | ||
| response = await openIssue(octokit, repoWithOwner, filing.findings[0]); | ||
| (filing as any).issue = { state: "open" } as Issue; | ||
| (filing as Filing).issue = { state: "open" } as Issue; | ||
| // Track for grouping | ||
| if (shouldOpenGroupedIssues) { | ||
| const problemShort: string = filing.findings[0].problemShort; | ||
| if (!newIssuesByProblemShort[problemShort]) { | ||
| newIssuesByProblemShort[problemShort] = []; | ||
| } | ||
| newIssuesByProblemShort[problemShort].push({ | ||
| url: response.data.html_url, | ||
| id: response.data.number, | ||
| }); | ||
|
Comment on lines
+84
to
+87
|
||
| } | ||
| } else if (isRepeatedFiling(filing)) { | ||
| // Reopen the filing’s issue (if necessary) | ||
| response = await reopenIssue(octokit, new Issue(filing.issue)); | ||
|
|
@@ -75,7 +98,7 @@ export default async function () { | |
| filing.issue.url = response.data.html_url; | ||
| filing.issue.title = response.data.title; | ||
| core.info( | ||
| `Set issue ${response.data.title} (${repoWithOwner}#${response.data.number}) state to ${filing.issue.state}` | ||
| `Set issue ${response.data.title} (${repoWithOwner}#${response.data.number}) state to ${filing.issue.state}`, | ||
| ); | ||
| } | ||
| } catch (error) { | ||
|
|
@@ -84,6 +107,40 @@ export default async function () { | |
| } | ||
| } | ||
|
|
||
| // Open tracking issues for each root cause and link back from each newly-created issue | ||
JoyceZhu marked this conversation as resolved.
Show resolved
Hide resolved
JoyceZhu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (shouldOpenGroupedIssues) { | ||
| for (const [problemShort, issues] of Object.entries( | ||
| newIssuesByProblemShort, | ||
| )) { | ||
| if (issues.length > 1) { | ||
| const title: string = `Accessibility tracking issue for all ${problemShort} issues`; | ||
| const body: string = | ||
| `# ${problemShort} issues\n\n` + | ||
| issues.map((issue) => `- [ ] ${issue.url}`).join("\n"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! I am sure we can convert this to subissues eventually if we want, but I think this is a great start. |
||
| try { | ||
| const trackingResponse = await octokit.request( | ||
| `POST /repos/${repoWithOwner}/issues`, | ||
| { | ||
| owner: repoWithOwner.split("/")[0], | ||
| repo: repoWithOwner.split("/")[1], | ||
| title, | ||
| body, | ||
| }, | ||
| ); | ||
| const trackingUrl: string = trackingResponse.data.html_url; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we will want to wrap this in a model in the near future (especially if we start working with other issue types), specifically this part:
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like the rest of the scanner codebase doesn't use models and we typically haven't in our other code (other than the examples you've linked), so I'm not sure if we want to have different architecture in different places. |
||
| trackingIssueUrls[problemShort] = trackingUrl; | ||
| core.info( | ||
| `Opened tracking issue for '${problemShort}': ${issues.length} issues.`, | ||
JoyceZhu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ); | ||
| } catch (error) { | ||
| core.warning( | ||
| `Failed to open grouped issue for '${problemShort}': ${error}`, | ||
| ); | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
110
to
141
|
||
| } | ||
|
Comment on lines
110
to
142
|
||
|
|
||
| core.setOutput("filings", JSON.stringify(filings)); | ||
| core.debug(`Output: 'filings: ${JSON.stringify(filings)}'`); | ||
| core.info("Finished 'file' action"); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.