-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathissue.ts
More file actions
172 lines (160 loc) · 5.78 KB
/
issue.ts
File metadata and controls
172 lines (160 loc) · 5.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import { Command } from "commander";
import ora from "ora";
import ansis from "ansis";
import { checkApiToken } from "../utils/auth";
import { handleError } from "../utils/error";
import { getOutputFormat, pickDeep, printJson } from "../utils/output";
import { printIssueDetail } from "../utils/formatting";
import { AnalysisService } from "../api/client/services/AnalysisService";
import { ToolsService } from "../api/client/services/ToolsService";
import { FileService } from "../api/client/services/FileService";
import { IssueStateBody } from "../api/client/models/IssueStateBody";
export function registerIssueCommand(program: Command) {
program
.command("issue")
.alias("iss")
.description("Show full details of a single quality issue")
.argument("<provider>", "git provider (gh, gl, or bb)")
.argument("<organization>", "organization name")
.argument("<repository>", "repository name")
.argument("<issueId>", "issue ID (shown at the bottom of each issue card)")
.option("-I, --ignore", "ignore this issue")
.option(
"-R, --ignore-reason <reason>",
"reason for ignoring (AcceptedUse|FalsePositive|NotExploitable|TestCode|ExternalCode)",
"AcceptedUse",
)
.option("-m, --ignore-comment <comment>", "optional comment for the ignore action", "")
.option("-U, --unignore", "unignore this issue")
.addHelpText(
"after",
`
Examples:
$ codacy issue gh my-org my-repo 12345
$ codacy issue gh my-org my-repo 12345 --output json
$ codacy issue gh my-org my-repo 12345 --ignore
$ codacy issue gh my-org my-repo 12345 --ignore --ignore-reason FalsePositive --ignore-comment "Not applicable here"
$ codacy issue gh my-org my-repo 12345 --unignore`,
)
.action(async function (
this: Command,
provider: string,
organization: string,
repository: string,
issueIdStr: string,
) {
try {
checkApiToken();
const format = getOutputFormat(this);
const issueId = parseInt(issueIdStr, 10);
const shouldIgnore: boolean = !!this.opts().ignore;
const shouldUnignore: boolean = !!this.opts().unignore;
const ignoreReason = this.opts().ignoreReason as IssueStateBody["reason"];
const ignoreComment: string = this.opts().ignoreComment;
if (isNaN(issueId)) {
console.error(ansis.red(`Invalid issue ID: ${issueIdStr}`));
process.exit(1);
}
const spinner = ora("Fetching issue details...").start();
const issueResponse = await AnalysisService.getIssue(
provider,
organization,
repository,
issueId,
);
const issue = issueResponse.data;
// Fetch pattern info and file context in parallel
const lineNumber = issue.lineNumber;
const startLine = Math.max(1, lineNumber - 5);
const endLine = lineNumber + 5;
const [patternResponse, fileContentResponse] = await Promise.all([
ToolsService.getPattern(
issue.toolInfo.uuid,
issue.patternInfo.id,
).catch(() => null),
FileService.getFileContent(
provider,
organization,
repository,
encodeURIComponent(issue.filePath),
startLine,
endLine,
).catch((e) => {
console.log("File path: ", issue.filePath);
console.error(ansis.red(`Error fetching file content: ${e}`));
return null;
}),
]);
spinner.stop();
const pattern = patternResponse?.data ?? null;
const lines = fileContentResponse?.data ?? null;
if (format === "json") {
printJson(pickDeep({ issue, pattern, lines }, [
// Issue
"issue.patternInfo.severityLevel",
"issue.patternInfo.category",
"issue.patternInfo.subCategory",
"issue.patternInfo.id",
"issue.message",
"issue.filePath",
"issue.lineNumber",
"issue.lineText",
"issue.suggestion",
"issue.resultDataId",
"issue.issueId",
"issue.toolInfo.name",
"issue.toolInfo.uuid",
"issue.falsePositiveProbability",
"issue.falsePositiveThreshold",
"issue.falsePositiveReason",
// Pattern
"pattern.id",
"pattern.title",
"pattern.description",
"pattern.rationale",
"pattern.solution",
"pattern.tags",
// Code lines
"lines",
]));
return;
}
if (!shouldIgnore && !shouldUnignore) {
printIssueDetail(issue, pattern, lines);
}
if (shouldIgnore) {
const ignoreSpinner = ora("Ignoring issue...").start();
await AnalysisService.updateIssueState(
provider,
organization,
repository,
issue.issueId,
{
ignored: true,
reason: ignoreReason,
comment: ignoreComment || undefined,
},
);
ignoreSpinner.succeed(
`Issue #${issueId} ignored (reason: ${ignoreReason}).`,
);
console.log(
ansis.dim(`Run a new analysis to see changes reflected: codacy repository ${provider} ${organization} ${repository} --reanalyze`),
);
}
if (shouldUnignore) {
const unignoreSpinner = ora("Unignoring issue...").start();
await AnalysisService.updateIssueState(
provider,
organization,
repository,
issue.issueId,
{ ignored: false },
);
unignoreSpinner.succeed(`Issue #${issueId} unignored.`);
}
} catch (err) {
handleError(err);
}
});
}