fixdiscover 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/main.js +21 -20
- package/package.json +1 -1
- package/src/cli.ts +5 -8
- package/src/jira.ts +11 -8
- package/src/linkfinder.ts +3 -2
- package/src/util.ts +8 -2
package/README.md
CHANGED
|
@@ -71,6 +71,7 @@ Options:
|
|
|
71
71
|
-c, --component [component] issue component
|
|
72
72
|
-u, --upstream [upstream] upstream project
|
|
73
73
|
-n, --nocolor disable color output (default: false)
|
|
74
|
+
-x, --dry dry run (default: false)
|
|
74
75
|
-h, --help display help for command
|
|
75
76
|
```
|
|
76
77
|
|
|
@@ -81,6 +82,8 @@ Options:
|
|
|
81
82
|
> ```bash
|
|
82
83
|
> NOCOLOR=true npx fixdiscover
|
|
83
84
|
> ```
|
|
85
|
+
>
|
|
86
|
+
> Similarly, you can enable dry run by setting the `DRY` environment variable to `true`.
|
|
84
87
|
|
|
85
88
|
### Examples
|
|
86
89
|
|
package/dist/main.js
CHANGED
|
@@ -78763,7 +78763,7 @@ function getDefaultValue(envName) {
|
|
|
78763
78763
|
return void 0;
|
|
78764
78764
|
}
|
|
78765
78765
|
const value = process.env[envName];
|
|
78766
|
-
if (envName === "NOCOLOR" && !value) {
|
|
78766
|
+
if ((envName === "NOCOLOR" || envName === "DRY") && !value) {
|
|
78767
78767
|
return false;
|
|
78768
78768
|
}
|
|
78769
78769
|
return value;
|
|
@@ -78775,16 +78775,15 @@ function getOptions(inputs) {
|
|
|
78775
78775
|
upstream: inputs.upstream || getDefaultValue("UPSTREAM")
|
|
78776
78776
|
};
|
|
78777
78777
|
}
|
|
78778
|
+
function escapeRegex(regex2) {
|
|
78779
|
+
return regex2.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
78780
|
+
}
|
|
78778
78781
|
|
|
78779
78782
|
// src/jira.ts
|
|
78780
78783
|
var Jira = class {
|
|
78781
|
-
constructor(instance, apiToken) {
|
|
78784
|
+
constructor(instance, apiToken, dry) {
|
|
78782
78785
|
this.instance = instance;
|
|
78783
|
-
this.
|
|
78784
|
-
storyPoints: "customfield_12310243",
|
|
78785
|
-
priority: "priority",
|
|
78786
|
-
severity: "customfield_12316142"
|
|
78787
|
-
};
|
|
78786
|
+
this.dry = dry;
|
|
78788
78787
|
this.baseJQL = 'Project = RHEL AND statusCategory = "To Do"';
|
|
78789
78788
|
this.JQL = "";
|
|
78790
78789
|
this.api = new import_jira.Version2Client({
|
|
@@ -78804,7 +78803,9 @@ var Jira = class {
|
|
|
78804
78803
|
this.JQL += " ORDER BY id DESC";
|
|
78805
78804
|
const response = await this.api.issueSearch.searchForIssuesUsingJqlPost({
|
|
78806
78805
|
jql: this.JQL,
|
|
78807
|
-
fields: ["id", "issuetype", "summary", "assignee", "comment"]
|
|
78806
|
+
fields: ["id", "issuetype", "summary", "assignee", "comment"],
|
|
78807
|
+
// We should paginate this, let's set 300 for now.
|
|
78808
|
+
maxResults: 300
|
|
78808
78809
|
});
|
|
78809
78810
|
return response.issues ?? raise("Jira.getIssues(): missing issues.");
|
|
78810
78811
|
}
|
|
@@ -78814,9 +78815,13 @@ var Jira = class {
|
|
|
78814
78815
|
});
|
|
78815
78816
|
return response ?? [];
|
|
78816
78817
|
}
|
|
78817
|
-
async setLabels(
|
|
78818
|
+
async setLabels(key, labels) {
|
|
78819
|
+
if (this.dry) {
|
|
78820
|
+
console.debug(`DRY: setLabels(${key}, ${labels})`);
|
|
78821
|
+
return;
|
|
78822
|
+
}
|
|
78818
78823
|
await this.api.issues.editIssue({
|
|
78819
|
-
issueIdOrKey:
|
|
78824
|
+
issueIdOrKey: key,
|
|
78820
78825
|
update: {
|
|
78821
78826
|
labels: labels.map((label) => ({ add: label }))
|
|
78822
78827
|
}
|
|
@@ -84419,9 +84424,9 @@ var LinkFinder = class {
|
|
|
84419
84424
|
constructor(upstreamStr) {
|
|
84420
84425
|
const [org, repo] = upstreamStr.split("/");
|
|
84421
84426
|
this.upstream = { org, repo };
|
|
84422
|
-
const upstreamEscaped = `${org}
|
|
84427
|
+
const upstreamEscaped = escapeRegex(`${org}/${repo}`);
|
|
84423
84428
|
this.regex = new RegExp(
|
|
84424
|
-
`https://github
|
|
84429
|
+
`https://github\\.com/${upstreamEscaped}/((pull|issues|commit)/([a-z\\d]+))`,
|
|
84425
84430
|
"g"
|
|
84426
84431
|
);
|
|
84427
84432
|
}
|
|
@@ -84507,7 +84512,7 @@ function cli() {
|
|
|
84507
84512
|
const program2 = new Command();
|
|
84508
84513
|
program2.name("fixdiscover").description(
|
|
84509
84514
|
"\u{1F50D} A small CLI tool is used to search for Jira issues with linked PRs and issues that are fixed in upstream projects"
|
|
84510
|
-
).version("1.
|
|
84515
|
+
).version("1.1.0");
|
|
84511
84516
|
program2.requiredOption(
|
|
84512
84517
|
"-c, --component [component]",
|
|
84513
84518
|
"issue component",
|
|
@@ -84516,11 +84521,7 @@ function cli() {
|
|
|
84516
84521
|
"-u, --upstream [upstream]",
|
|
84517
84522
|
"upstream project",
|
|
84518
84523
|
getDefaultValue("UPSTREAM")
|
|
84519
|
-
).option(
|
|
84520
|
-
"-n, --nocolor",
|
|
84521
|
-
"disable color output",
|
|
84522
|
-
getDefaultValue("NOCOLOR")
|
|
84523
|
-
);
|
|
84524
|
+
).option("-n, --nocolor", "disable color output", getDefaultValue("NOCOLOR")).option("-x, --dry", "dry run", getDefaultValue("DRY"));
|
|
84524
84525
|
return program2;
|
|
84525
84526
|
}
|
|
84526
84527
|
var runProgram = async () => {
|
|
@@ -84529,7 +84530,7 @@ var runProgram = async () => {
|
|
|
84529
84530
|
const options = getOptions(program2.opts());
|
|
84530
84531
|
const logger = new Logger(!!options.nocolor);
|
|
84531
84532
|
const jiraToken = process.env.JIRA_API_TOKEN ?? tokenUnavailable("jira");
|
|
84532
|
-
const jira = new Jira("https://issues.redhat.com", jiraToken);
|
|
84533
|
+
const jira = new Jira("https://issues.redhat.com", jiraToken, options.dry);
|
|
84533
84534
|
const githubToken = process.env.GITHUB_API_TOKEN ?? tokenUnavailable("github");
|
|
84534
84535
|
const octokit = getOctokit(githubToken);
|
|
84535
84536
|
const upstream = options.upstream ?? `${options.component}/${options.component}`;
|
|
@@ -84555,7 +84556,7 @@ var runProgram = async () => {
|
|
|
84555
84556
|
}
|
|
84556
84557
|
}
|
|
84557
84558
|
if (links.length > 0) {
|
|
84558
|
-
await jira.setLabels(issue.
|
|
84559
|
+
await jira.setLabels(issue.key, ["backport"]);
|
|
84559
84560
|
data.push({ key: jira.getIssueURL(issue.key), links });
|
|
84560
84561
|
}
|
|
84561
84562
|
}
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -17,7 +17,7 @@ export function cli(): Command {
|
|
|
17
17
|
.description(
|
|
18
18
|
'🔍 A small CLI tool is used to search for Jira issues with linked PRs and issues that are fixed in upstream projects'
|
|
19
19
|
)
|
|
20
|
-
.version('1.
|
|
20
|
+
.version('1.1.0');
|
|
21
21
|
|
|
22
22
|
program
|
|
23
23
|
.requiredOption(
|
|
@@ -30,11 +30,8 @@ export function cli(): Command {
|
|
|
30
30
|
'upstream project',
|
|
31
31
|
getDefaultValue('UPSTREAM')
|
|
32
32
|
)
|
|
33
|
-
.option(
|
|
34
|
-
|
|
35
|
-
'disable color output',
|
|
36
|
-
getDefaultValue('NOCOLOR')
|
|
37
|
-
);
|
|
33
|
+
.option('-n, --nocolor', 'disable color output', getDefaultValue('NOCOLOR'))
|
|
34
|
+
.option('-x, --dry', 'dry run', getDefaultValue('DRY'));
|
|
38
35
|
|
|
39
36
|
return program;
|
|
40
37
|
}
|
|
@@ -47,7 +44,7 @@ const runProgram = async () => {
|
|
|
47
44
|
const logger = new Logger(!!options.nocolor);
|
|
48
45
|
|
|
49
46
|
const jiraToken = process.env.JIRA_API_TOKEN ?? tokenUnavailable('jira');
|
|
50
|
-
const jira = new Jira('https://issues.redhat.com', jiraToken);
|
|
47
|
+
const jira = new Jira('https://issues.redhat.com', jiraToken, options.dry);
|
|
51
48
|
|
|
52
49
|
const githubToken =
|
|
53
50
|
process.env.GITHUB_API_TOKEN ?? tokenUnavailable('github');
|
|
@@ -88,7 +85,7 @@ const runProgram = async () => {
|
|
|
88
85
|
}
|
|
89
86
|
|
|
90
87
|
if (links.length > 0) {
|
|
91
|
-
await jira.setLabels(issue.
|
|
88
|
+
await jira.setLabels(issue.key, ['backport']);
|
|
92
89
|
data.push({ key: jira.getIssueURL(issue.key), links });
|
|
93
90
|
}
|
|
94
91
|
}
|
package/src/jira.ts
CHANGED
|
@@ -4,17 +4,13 @@ import { raise } from './util';
|
|
|
4
4
|
|
|
5
5
|
export class Jira {
|
|
6
6
|
readonly api: Version2Client;
|
|
7
|
-
readonly fields = {
|
|
8
|
-
storyPoints: 'customfield_12310243',
|
|
9
|
-
priority: 'priority',
|
|
10
|
-
severity: 'customfield_12316142',
|
|
11
|
-
};
|
|
12
7
|
readonly baseJQL = 'Project = RHEL AND statusCategory = "To Do"';
|
|
13
8
|
JQL = '';
|
|
14
9
|
|
|
15
10
|
constructor(
|
|
16
11
|
readonly instance: string,
|
|
17
|
-
apiToken: string
|
|
12
|
+
apiToken: string,
|
|
13
|
+
readonly dry: boolean
|
|
18
14
|
) {
|
|
19
15
|
this.api = new Version2Client({
|
|
20
16
|
host: instance,
|
|
@@ -37,6 +33,8 @@ export class Jira {
|
|
|
37
33
|
const response = await this.api.issueSearch.searchForIssuesUsingJqlPost({
|
|
38
34
|
jql: this.JQL,
|
|
39
35
|
fields: ['id', 'issuetype', 'summary', 'assignee', 'comment'],
|
|
36
|
+
// We should paginate this, let's set 300 for now.
|
|
37
|
+
maxResults: 300,
|
|
40
38
|
});
|
|
41
39
|
|
|
42
40
|
return response.issues ?? raise('Jira.getIssues(): missing issues.');
|
|
@@ -50,9 +48,14 @@ export class Jira {
|
|
|
50
48
|
return response ?? [];
|
|
51
49
|
}
|
|
52
50
|
|
|
53
|
-
async setLabels(
|
|
51
|
+
async setLabels(key: string, labels: string[]) {
|
|
52
|
+
if (this.dry) {
|
|
53
|
+
console.debug(`DRY: setLabels(${key}, ${labels})`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
54
57
|
await this.api.issues.editIssue({
|
|
55
|
-
issueIdOrKey:
|
|
58
|
+
issueIdOrKey: key,
|
|
56
59
|
update: {
|
|
57
60
|
labels: labels.map(label => ({ add: label })),
|
|
58
61
|
},
|
package/src/linkfinder.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Endpoints } from '@octokit/types';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
import { CustomOctokit } from './octokit';
|
|
5
|
+
import { escapeRegex } from './util';
|
|
5
6
|
|
|
6
7
|
const linkObjectSchema = z.object({
|
|
7
8
|
url: z.string(),
|
|
@@ -18,7 +19,7 @@ export class LinkFinder {
|
|
|
18
19
|
const [org, repo] = upstreamStr.split('/');
|
|
19
20
|
this.upstream = { org, repo };
|
|
20
21
|
|
|
21
|
-
const upstreamEscaped = `${org}
|
|
22
|
+
const upstreamEscaped = escapeRegex(`${org}/${repo}`);
|
|
22
23
|
|
|
23
24
|
// Match any GitHub link to issue, PR or commit to the upstream project
|
|
24
25
|
// Groups:
|
|
@@ -27,7 +28,7 @@ export class LinkFinder {
|
|
|
27
28
|
// 2 - type of link (pull, issues, commit)
|
|
28
29
|
// 3 - id or sha
|
|
29
30
|
this.regex = new RegExp(
|
|
30
|
-
`https:\/\/github
|
|
31
|
+
`https:\/\/github\\.com\/${upstreamEscaped}\/((pull|issues|commit)\/([a-z\\d]+))`,
|
|
31
32
|
'g'
|
|
32
33
|
);
|
|
33
34
|
}
|
package/src/util.ts
CHANGED
|
@@ -21,14 +21,16 @@ export function isDefaultValuesDisabled(): boolean {
|
|
|
21
21
|
return process.env['NODEFAULTS'] ? true : false;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export function getDefaultValue(
|
|
24
|
+
export function getDefaultValue(
|
|
25
|
+
envName: 'COMPONENT' | 'UPSTREAM' | 'NOCOLOR' | 'DRY'
|
|
26
|
+
) {
|
|
25
27
|
if (isDefaultValuesDisabled()) {
|
|
26
28
|
return undefined;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
const value = process.env[envName];
|
|
30
32
|
|
|
31
|
-
if (envName === 'NOCOLOR' && !value) {
|
|
33
|
+
if ((envName === 'NOCOLOR' || envName === 'DRY') && !value) {
|
|
32
34
|
return false;
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -42,3 +44,7 @@ export function getOptions(inputs: OptionValues): OptionValues {
|
|
|
42
44
|
upstream: inputs.upstream || getDefaultValue('UPSTREAM'),
|
|
43
45
|
};
|
|
44
46
|
}
|
|
47
|
+
|
|
48
|
+
export function escapeRegex(regex: string): string {
|
|
49
|
+
return regex.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
50
|
+
}
|