wuffle 0.74.0 → 0.76.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/app.yml +1 -0
- package/lib/apps/auth-routes/AuthRoutes.js +16 -14
- package/lib/apps/automatic-dev-flow/AutomaticDevFlow.js +7 -8
- package/lib/apps/background-sync/BackgroundSync.js +30 -5
- package/lib/apps/board-api-routes/board-api-routes.js +33 -29
- package/lib/apps/dump-store/s3/S3.js +12 -0
- package/lib/apps/events-sync/EventsSync.js +20 -3
- package/lib/apps/github-app/GithubApp.js +31 -3
- package/lib/apps/github-checks/GithubChecks.js +2 -0
- package/lib/apps/github-comments/GithubComments.js +9 -78
- package/lib/apps/github-comments/GithubCommentsBackend.js +132 -0
- package/lib/apps/github-comments/index.js +3 -1
- package/lib/apps/github-issues/GithubIssues.js +161 -9
- package/lib/apps/github-reviews/GithubReviews.js +2 -0
- package/lib/apps/github-statuses/GithubStatuses.js +2 -0
- package/lib/apps/issue-filter/IssueFilter.js +5 -3
- package/lib/apps/search/Search.js +9 -5
- package/lib/apps/webhook-events/WebhookEvents.js +12 -5
- package/lib/events.js +2 -2
- package/lib/filters.js +4 -2
- package/lib/probot/CustomProbot.js +2 -1
- package/lib/util/links.js +16 -0
- package/lib/util/search.js +1 -1
- package/package.json +7 -7
- package/public/bundle.js +1 -1
- package/public/bundle.js.map +1 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { repoAndOwner } from '../../util/index.js';
|
|
2
|
+
|
|
3
|
+
import gql from 'fake-tag';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef { {
|
|
7
|
+
* id: number,
|
|
8
|
+
* node_id: string,
|
|
9
|
+
* body: string,
|
|
10
|
+
* created_at: string,
|
|
11
|
+
* authorAssociation: string,
|
|
12
|
+
* html_url: string,
|
|
13
|
+
* user: {
|
|
14
|
+
* id: number,
|
|
15
|
+
* node_id: string,
|
|
16
|
+
* login: string,
|
|
17
|
+
* avatar_url: string,
|
|
18
|
+
* html_url: string,
|
|
19
|
+
* type: string
|
|
20
|
+
* }
|
|
21
|
+
* } } GithubComment
|
|
22
|
+
*
|
|
23
|
+
* @typedef { import('../../util/meta.js').Issue } Issue
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* This component fetches GitHub comments data required for background synchronization.
|
|
29
|
+
*
|
|
30
|
+
* @constructor
|
|
31
|
+
*
|
|
32
|
+
* @param {import('../github-client/GithubClient.js').default} githubClient
|
|
33
|
+
*/
|
|
34
|
+
export default function GithubCommentsBackend(githubClient) {
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Return comments for given issue.
|
|
38
|
+
*
|
|
39
|
+
* @param {Issue} issue
|
|
40
|
+
*
|
|
41
|
+
* @return {Promise<GithubComment[]>}
|
|
42
|
+
*/
|
|
43
|
+
this.getIssueComments = async function(issue) {
|
|
44
|
+
|
|
45
|
+
const {
|
|
46
|
+
number
|
|
47
|
+
} = issue;
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
repo,
|
|
51
|
+
owner
|
|
52
|
+
} = repoAndOwner(issue);
|
|
53
|
+
|
|
54
|
+
const github = await githubClient.getOrgScoped(owner);
|
|
55
|
+
|
|
56
|
+
const result = await github.graphql(gql`
|
|
57
|
+
|
|
58
|
+
fragment CommentInfo on IssueComment {
|
|
59
|
+
id: databaseId
|
|
60
|
+
node_id: id
|
|
61
|
+
body: bodyText
|
|
62
|
+
created_at: publishedAt
|
|
63
|
+
authorAssociation,
|
|
64
|
+
html_url: url,
|
|
65
|
+
user: author {
|
|
66
|
+
login
|
|
67
|
+
avatar_url: avatarUrl,
|
|
68
|
+
html_url: url,
|
|
69
|
+
type: __typename
|
|
70
|
+
... on User {
|
|
71
|
+
id: databaseId
|
|
72
|
+
node_id: id
|
|
73
|
+
}
|
|
74
|
+
... on Bot {
|
|
75
|
+
id: databaseId
|
|
76
|
+
node_id: id
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
query FetchComments(
|
|
82
|
+
$repo: String!,
|
|
83
|
+
$owner: String!,
|
|
84
|
+
$issue_number: Int!,
|
|
85
|
+
$after: String
|
|
86
|
+
) {
|
|
87
|
+
repository(name: $repo, owner: $owner) {
|
|
88
|
+
issueOrPullRequest(number: $issue_number) {
|
|
89
|
+
...on Issue {
|
|
90
|
+
comments(first: 100, after: $after) {
|
|
91
|
+
edges {
|
|
92
|
+
node {
|
|
93
|
+
...CommentInfo
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
pageInfo {
|
|
97
|
+
endCursor
|
|
98
|
+
hasNextPage
|
|
99
|
+
}
|
|
100
|
+
totalCount
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
...on PullRequest {
|
|
104
|
+
comments(first: 100, after: $after) {
|
|
105
|
+
edges {
|
|
106
|
+
node {
|
|
107
|
+
...CommentInfo
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
pageInfo {
|
|
111
|
+
endCursor
|
|
112
|
+
hasNextPage
|
|
113
|
+
}
|
|
114
|
+
totalCount
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}`,
|
|
120
|
+
{
|
|
121
|
+
owner,
|
|
122
|
+
repo,
|
|
123
|
+
issue_number: number
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const comments = /** @type { GithubComment[] } */ (
|
|
127
|
+
result.repository.issueOrPullRequest.comments.edges.map(e => e.node)
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return comments;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import GithubComments from './GithubComments.js';
|
|
2
|
+
import GithubCommentsBackend from './GithubCommentsBackend.js';
|
|
2
3
|
|
|
3
4
|
export default {
|
|
4
5
|
__init__: [ 'githubComments' ],
|
|
5
|
-
githubComments: [ 'type', GithubComments ]
|
|
6
|
+
githubComments: [ 'type', GithubComments ],
|
|
7
|
+
githubCommentsBackend: [ 'type', GithubCommentsBackend ]
|
|
6
8
|
};
|
|
@@ -4,8 +4,17 @@ const {
|
|
|
4
4
|
CLOSES
|
|
5
5
|
} = linkTypes;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @typedef { import('probot').Context } ProbotContext
|
|
9
|
+
*
|
|
10
|
+
* @typedef { import('../../columns.js').ColumnDefinition } ColumnDefinition
|
|
11
|
+
*/
|
|
7
12
|
|
|
8
13
|
/**
|
|
14
|
+
* Moves GitHub issues between board columns by applying the
|
|
15
|
+
* corresponding state (open/closed), label, and assignee changes
|
|
16
|
+
* via the GitHub API.
|
|
17
|
+
*
|
|
9
18
|
* @constructor
|
|
10
19
|
*
|
|
11
20
|
* @param {any} config
|
|
@@ -18,6 +27,16 @@ export default function GithubIssues(config, logger, columns) {
|
|
|
18
27
|
name: 'wuffle:github-issues'
|
|
19
28
|
});
|
|
20
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Return the assignee update required to add `newAssignee` to the
|
|
32
|
+
* issue, or an empty object if the assignee is already set or not
|
|
33
|
+
* provided.
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} issue
|
|
36
|
+
* @param {string|null} newAssignee
|
|
37
|
+
*
|
|
38
|
+
* @return {{ assignees?: string[] }}
|
|
39
|
+
*/
|
|
21
40
|
function getAssigneeUpdate(issue, newAssignee) {
|
|
22
41
|
|
|
23
42
|
if (!newAssignee) {
|
|
@@ -39,6 +58,15 @@ export default function GithubIssues(config, logger, columns) {
|
|
|
39
58
|
|
|
40
59
|
}
|
|
41
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Return the state update (`open` / `closed`) required to reflect
|
|
63
|
+
* the new column, or an empty object if no change is needed.
|
|
64
|
+
*
|
|
65
|
+
* @param {Object} issue
|
|
66
|
+
* @param {ColumnDefinition} newColumn
|
|
67
|
+
*
|
|
68
|
+
* @return {{ state?: 'open'|'closed' }}
|
|
69
|
+
*/
|
|
42
70
|
function getStateUpdate(issue, newColumn) {
|
|
43
71
|
|
|
44
72
|
let update = {};
|
|
@@ -55,16 +83,29 @@ export default function GithubIssues(config, logger, columns) {
|
|
|
55
83
|
return update;
|
|
56
84
|
}
|
|
57
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Return the label changes required to reflect the new column:
|
|
88
|
+
* the column label to add and any other column labels to remove.
|
|
89
|
+
*
|
|
90
|
+
* @param {Object} issue
|
|
91
|
+
* @param {ColumnDefinition} newColumn
|
|
92
|
+
*
|
|
93
|
+
* @return {{ addLabels: string[], removeLabels: string[] }}
|
|
94
|
+
*/
|
|
58
95
|
function getLabelUpdate(issue, newColumn) {
|
|
59
96
|
|
|
60
|
-
const issueLabels =
|
|
97
|
+
const issueLabels = /** @type { string[] } */ (
|
|
98
|
+
issue.labels.map(l => l.name)
|
|
99
|
+
);
|
|
61
100
|
|
|
62
101
|
const newLabel = newColumn.label;
|
|
63
102
|
|
|
64
103
|
const addLabels = (!newLabel || issueLabels.includes(newLabel)) ? [] : [ newLabel ];
|
|
65
104
|
|
|
66
|
-
const removeLabels =
|
|
67
|
-
|
|
105
|
+
const removeLabels = /** @type { string[] } */ (
|
|
106
|
+
columns.getAll().map(c => c.label).filter(
|
|
107
|
+
label => label && label !== newLabel && issueLabels.includes(label)
|
|
108
|
+
)
|
|
68
109
|
);
|
|
69
110
|
|
|
70
111
|
return {
|
|
@@ -73,6 +114,10 @@ export default function GithubIssues(config, logger, columns) {
|
|
|
73
114
|
};
|
|
74
115
|
}
|
|
75
116
|
|
|
117
|
+
/**
|
|
118
|
+
* @param {ProbotContext} context
|
|
119
|
+
* @param {number} issue_number
|
|
120
|
+
*/
|
|
76
121
|
function findIssue(context, issue_number) {
|
|
77
122
|
|
|
78
123
|
const params = context.repo({ issue_number });
|
|
@@ -88,12 +133,43 @@ export default function GithubIssues(config, logger, columns) {
|
|
|
88
133
|
});
|
|
89
134
|
}
|
|
90
135
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Fetch an issue by number and move it to a new column if the
|
|
138
|
+
* optional predicate passes. Resolves to `false` if the issue is
|
|
139
|
+
* not found or the predicate rejects it.
|
|
140
|
+
*
|
|
141
|
+
* @param {ProbotContext} context
|
|
142
|
+
* @param {number} number
|
|
143
|
+
* @param {ColumnDefinition} newColumn
|
|
144
|
+
* @param {string|null} newAssignee
|
|
145
|
+
* @param {(issue: Object) => boolean} [test]
|
|
146
|
+
*
|
|
147
|
+
* @return {Promise<void|false>}
|
|
148
|
+
*/
|
|
149
|
+
async function findAndMoveIssue(context, number, newColumn, newAssignee, test = (issue) => true) {
|
|
150
|
+
const issue = await findIssue(context, number);
|
|
151
|
+
|
|
152
|
+
if (!issue || !test(issue)) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return moveIssue(context, issue, newColumn, newAssignee);
|
|
94
157
|
}
|
|
95
158
|
|
|
96
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Move all issues referenced via `closes` links in the given issue
|
|
161
|
+
* or pull request body to a new column.
|
|
162
|
+
*
|
|
163
|
+
* Only issues within the same repository are moved.
|
|
164
|
+
*
|
|
165
|
+
* @param {ProbotContext} context
|
|
166
|
+
* @param {Object} issue
|
|
167
|
+
* @param {ColumnDefinition} newColumn
|
|
168
|
+
* @param {string|null} [newAssignee=null]
|
|
169
|
+
*
|
|
170
|
+
* @return {Promise<(void|false)[]>}
|
|
171
|
+
*/
|
|
172
|
+
async function moveReferencedIssues(context, issue, newColumn, newAssignee = null) {
|
|
97
173
|
|
|
98
174
|
// TODO(nikku): do that lazily, i.e. react to PR label changes?
|
|
99
175
|
// would slower the movement but support manual moving-issue with PR
|
|
@@ -128,7 +204,18 @@ export default function GithubIssues(config, logger, columns) {
|
|
|
128
204
|
}));
|
|
129
205
|
}
|
|
130
206
|
|
|
131
|
-
|
|
207
|
+
/**
|
|
208
|
+
* Move an issue to a new column, applying state, label, and
|
|
209
|
+
* assignee updates via the GitHub API.
|
|
210
|
+
*
|
|
211
|
+
* @param {ProbotContext} context
|
|
212
|
+
* @param {Object} issue
|
|
213
|
+
* @param {ColumnDefinition} newColumn
|
|
214
|
+
* @param {string|null} [newAssignee=null]
|
|
215
|
+
*
|
|
216
|
+
* @return {Promise<void>}
|
|
217
|
+
*/
|
|
218
|
+
function moveIssue(context, issue, newColumn, newAssignee = null) {
|
|
132
219
|
|
|
133
220
|
const {
|
|
134
221
|
number: issue_number
|
|
@@ -199,22 +286,87 @@ export default function GithubIssues(config, logger, columns) {
|
|
|
199
286
|
);
|
|
200
287
|
}
|
|
201
288
|
|
|
202
|
-
return Promise.all(invocations);
|
|
289
|
+
return Promise.all(invocations).then(() => {});
|
|
203
290
|
}
|
|
204
291
|
|
|
205
292
|
|
|
206
293
|
// api /////////////////////////////
|
|
207
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Move an issue to a new column, applying state, label, and
|
|
297
|
+
* assignee updates via the GitHub API.
|
|
298
|
+
*
|
|
299
|
+
* @param {ProbotContext} context
|
|
300
|
+
* @param {Object} issue
|
|
301
|
+
* @param {ColumnDefinition} newColumn
|
|
302
|
+
* @param {string} [newAssignee]
|
|
303
|
+
*
|
|
304
|
+
* @return {Promise<void>}
|
|
305
|
+
*/
|
|
208
306
|
this.moveIssue = moveIssue;
|
|
209
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Move all issues referenced via `closes` links in the given issue
|
|
310
|
+
* or pull request body to a new column.
|
|
311
|
+
*
|
|
312
|
+
* Only issues within the same repository are moved.
|
|
313
|
+
*
|
|
314
|
+
* @param {ProbotContext} context
|
|
315
|
+
* @param {Object} issue
|
|
316
|
+
* @param {ColumnDefinition} newColumn
|
|
317
|
+
* @param {string|null} [newAssignee=null]
|
|
318
|
+
*
|
|
319
|
+
* @return {Promise<(void|false)[]>}
|
|
320
|
+
*/
|
|
210
321
|
this.moveReferencedIssues = moveReferencedIssues;
|
|
211
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Return the state update (`open` / `closed`) required to reflect
|
|
325
|
+
* the new column, or an empty object if no change is needed.
|
|
326
|
+
*
|
|
327
|
+
* @param {Object} issue
|
|
328
|
+
* @param {ColumnDefinition} newColumn
|
|
329
|
+
*
|
|
330
|
+
* @return {{ state?: 'open'|'closed' }}
|
|
331
|
+
*/
|
|
212
332
|
this.getStateUpdate = getStateUpdate;
|
|
213
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Return the assignee update required to add `newAssignee` to the
|
|
336
|
+
* issue, or an empty object if the assignee is already set or not
|
|
337
|
+
* provided.
|
|
338
|
+
*
|
|
339
|
+
* @param {Object} issue
|
|
340
|
+
* @param {string} [newAssignee]
|
|
341
|
+
*
|
|
342
|
+
* @return {{ assignees?: string[] }}
|
|
343
|
+
*/
|
|
214
344
|
this.getAssigneeUpdate = getAssigneeUpdate;
|
|
215
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Return the label changes required to reflect the new column:
|
|
348
|
+
* the column label to add and any other column labels to remove.
|
|
349
|
+
*
|
|
350
|
+
* @param {Object} issue
|
|
351
|
+
* @param {ColumnDefinition} newColumn
|
|
352
|
+
*
|
|
353
|
+
* @return {{ addLabels: string[], removeLabels: string[] }}
|
|
354
|
+
*/
|
|
216
355
|
this.getLabelUpdate = getLabelUpdate;
|
|
217
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Fetch an issue by number and move it to a new column if the
|
|
359
|
+
* optional predicate passes. Resolves to `false` if the issue is
|
|
360
|
+
* not found or the predicate rejects it.
|
|
361
|
+
*
|
|
362
|
+
* @param {ProbotContext} context
|
|
363
|
+
* @param {number} number
|
|
364
|
+
* @param {ColumnDefinition} newColumn
|
|
365
|
+
* @param {string|null} newAssignee
|
|
366
|
+
* @param {(issue: Object) => boolean} [test]
|
|
367
|
+
*
|
|
368
|
+
* @return {Promise<void|false>}
|
|
369
|
+
*/
|
|
218
370
|
this.findAndMoveIssue = findAndMoveIssue;
|
|
219
371
|
|
|
220
372
|
}
|
|
@@ -5,6 +5,8 @@ import { filterUser, filterPull } from '../../filters.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* This component updates the stored issues based on GitHub events.
|
|
7
7
|
*
|
|
8
|
+
* It also hooks into `BackgroundSync` to fetch reviews for a PR.
|
|
9
|
+
*
|
|
8
10
|
* @constructor
|
|
9
11
|
*
|
|
10
12
|
* @param {import('../webhook-events/WebhookEvents.js').default} webhookEvents
|
|
@@ -5,6 +5,8 @@ import { filterPull } from '../../filters.js';
|
|
|
5
5
|
/**
|
|
6
6
|
* This component updates synchronizes GitHub statuses with pull requests.
|
|
7
7
|
*
|
|
8
|
+
* It also hooks into `BackgroundSync` to fetch statuses for a PR.
|
|
9
|
+
*
|
|
8
10
|
* @constructor
|
|
9
11
|
*
|
|
10
12
|
* @param {import('../webhook-events/WebhookEvents.js').default} webhookEvents
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef { { ignoreFilter?: string } } StoreFilterConfig
|
|
3
|
+
*
|
|
4
|
+
* @typedef { Extract<import('@octokit/webhooks').EmitterWebhookEventName, 'issues' | `issues.${string}` | 'issue_comment' | `issue_comment.${string}` | 'pull_request' | `pull_request.${string}` | 'pull_request_review' | `pull_request_review.${string}` | 'pull_request_review_comment' | `pull_request_review_comment.${string}` | 'pull_request_review_thread' | `pull_request_review_thread.${string}`> } IssueOrPullWebhookEventName
|
|
3
5
|
*/
|
|
4
6
|
|
|
5
7
|
import { filterIssueOrPull } from '../../filters.js';
|
|
@@ -41,7 +43,7 @@ export default function IssueFilter(config, store, search, logger) {
|
|
|
41
43
|
/**
|
|
42
44
|
* @param {import('../../types.js').Logger } log
|
|
43
45
|
*
|
|
44
|
-
* @return { (
|
|
46
|
+
* @return {<E extends IssueOrPullWebhookEventName>(fn: (context: import('probot').Context<E>) => any) => (context: import('probot').Context<E>) => any}
|
|
45
47
|
*/
|
|
46
48
|
function createWebhookFilter(log) {
|
|
47
49
|
|
|
@@ -49,7 +51,7 @@ export default function IssueFilter(config, store, search, logger) {
|
|
|
49
51
|
|
|
50
52
|
return (context) => {
|
|
51
53
|
|
|
52
|
-
const payload = context.payload;
|
|
54
|
+
const payload = /** @type {any} */ (context.payload);
|
|
53
55
|
|
|
54
56
|
const issueOrPull = filterIssueOrPull(
|
|
55
57
|
payload.issue || payload.pull_request,
|
|
@@ -79,7 +81,7 @@ export default function IssueFilter(config, store, search, logger) {
|
|
|
79
81
|
/**
|
|
80
82
|
* @param {import('../../types.js').Logger } log
|
|
81
83
|
*
|
|
82
|
-
* @return { (
|
|
84
|
+
* @return {<E extends IssueOrPullWebhookEventName>(fn: (context: import('probot').Context<E>) => any) => (context: import('probot').Context<E>) => any}
|
|
83
85
|
*/
|
|
84
86
|
this.createWebhookFilter = createWebhookFilter;
|
|
85
87
|
}
|
|
@@ -37,7 +37,7 @@ export default function Search(config, logger, store) {
|
|
|
37
37
|
|
|
38
38
|
const {
|
|
39
39
|
treatBotsAsReviewers = false,
|
|
40
|
-
defaultFilter
|
|
40
|
+
defaultFilter = ''
|
|
41
41
|
} = config;
|
|
42
42
|
|
|
43
43
|
function filterNoop(issue) {
|
|
@@ -63,13 +63,17 @@ export default function Search(config, logger, store) {
|
|
|
63
63
|
*
|
|
64
64
|
* @return { boolean }
|
|
65
65
|
*/
|
|
66
|
-
function includes(actual, pattern, exact) {
|
|
66
|
+
function includes(actual, pattern, exact = false) {
|
|
67
|
+
|
|
68
|
+
if (!pattern) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
67
71
|
|
|
68
72
|
if (exact) {
|
|
69
|
-
return
|
|
73
|
+
return actual === pattern;
|
|
70
74
|
}
|
|
71
75
|
|
|
72
|
-
return
|
|
76
|
+
return actual.toLowerCase().includes(pattern.toLowerCase());
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
function isPull(issue) {
|
|
@@ -445,7 +449,7 @@ export default function Search(config, logger, store) {
|
|
|
445
449
|
* Retrieve a filter function from the given search string.
|
|
446
450
|
*
|
|
447
451
|
* @param {string} search
|
|
448
|
-
* @param {GitHubUser} [user]
|
|
452
|
+
* @param {GitHubUser|null} [user]
|
|
449
453
|
*
|
|
450
454
|
* @return {FilterFn}
|
|
451
455
|
*/
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @typedef { import('probot').Probot } Probot
|
|
3
|
+
* @typedef { import('@octokit/webhooks').EmitterWebhookEventName } WebhookEventName
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A lightweight wrapper around {@link Probot#on} and {@link Probot#onAny}.
|
|
8
|
+
*
|
|
2
9
|
* @constructor
|
|
3
10
|
*
|
|
4
11
|
* @param {import('../../types.js').ProbotApp} app
|
|
@@ -7,8 +14,7 @@
|
|
|
7
14
|
export default function WebhookEvents(app, githubApp) {
|
|
8
15
|
|
|
9
16
|
/**
|
|
10
|
-
* @
|
|
11
|
-
* @param {T} fn
|
|
17
|
+
* @param {(context: import('probot').Context) => any} fn
|
|
12
18
|
*/
|
|
13
19
|
function ifEnabled(fn) {
|
|
14
20
|
|
|
@@ -37,8 +43,9 @@ export default function WebhookEvents(app, githubApp) {
|
|
|
37
43
|
* Register a event lister for a single
|
|
38
44
|
* or a number of webhook events.
|
|
39
45
|
*
|
|
40
|
-
* @
|
|
41
|
-
* @param {
|
|
46
|
+
* @template {WebhookEventName} E
|
|
47
|
+
* @param {E | E[]} events
|
|
48
|
+
* @param {(context: import('probot').Context<E>) => any} fn listener
|
|
42
49
|
*/
|
|
43
50
|
function on(events, fn) {
|
|
44
51
|
app.on(events, ifEnabled(fn));
|
|
@@ -48,7 +55,7 @@ export default function WebhookEvents(app, githubApp) {
|
|
|
48
55
|
* Register an event listener for all
|
|
49
56
|
* webhook events.
|
|
50
57
|
*
|
|
51
|
-
* @param {
|
|
58
|
+
* @param {(context: import('probot').Context) => any} fn
|
|
52
59
|
*/
|
|
53
60
|
function onAny(fn) {
|
|
54
61
|
app.onAny(ifEnabled(fn));
|
package/lib/events.js
CHANGED
|
@@ -139,8 +139,8 @@ Events.prototype.createEvent = function(data) {
|
|
|
139
139
|
* @param {Object} [data] the event object
|
|
140
140
|
* @param {...Object} additionalArgs to be passed to the callback functions
|
|
141
141
|
*
|
|
142
|
-
* @return {Promise<boolean>} the events return value, if
|
|
143
|
-
*
|
|
142
|
+
* @return {Promise<boolean|undefined|any>} the events return value, if
|
|
143
|
+
* specified or false if the default action was prevented by listeners
|
|
144
144
|
*/
|
|
145
145
|
Events.prototype.emit = async function(type, data, ...additionalArgs) {
|
|
146
146
|
|
package/lib/filters.js
CHANGED
|
@@ -214,7 +214,8 @@ export function filterIssue(githubIssue, githubRepository) {
|
|
|
214
214
|
milestone,
|
|
215
215
|
pull_request,
|
|
216
216
|
html_url,
|
|
217
|
-
author_association
|
|
217
|
+
author_association,
|
|
218
|
+
parent_issue_url
|
|
218
219
|
} = githubIssue;
|
|
219
220
|
|
|
220
221
|
// stable ID that is independent from GitHubs internal issue/pr distinction
|
|
@@ -245,7 +246,8 @@ export function filterIssue(githubIssue, githubRepository) {
|
|
|
245
246
|
repository: filterRepository(githubRepository),
|
|
246
247
|
pull_request: !!pull_request,
|
|
247
248
|
html_url,
|
|
248
|
-
author_association
|
|
249
|
+
author_association,
|
|
250
|
+
parent_issue_url: parent_issue_url || null
|
|
249
251
|
};
|
|
250
252
|
|
|
251
253
|
}
|
|
@@ -103,7 +103,8 @@ function validateSetup() {
|
|
|
103
103
|
|
|
104
104
|
const setup = new ManifestCreation();
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
// TODO(nikku): process.env.BASE_URL may not be defined here
|
|
107
|
+
const manifest = JSON.parse(setup.getManifest(setup.pkg, /** @type { string } */ (process.env.BASE_URL)));
|
|
107
108
|
|
|
108
109
|
return [
|
|
109
110
|
!manifest.url && new Error('No <url> configured in app.yml'),
|
package/lib/util/links.js
CHANGED
|
@@ -145,6 +145,22 @@ export function findLinks(issue, types) {
|
|
|
145
145
|
links.push(link);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
// add CHILD_OF link from parent_issue_url (GitHub sub-issues)
|
|
149
|
+
const { parent_issue_url } = issue;
|
|
150
|
+
|
|
151
|
+
if (parent_issue_url) {
|
|
152
|
+
const parentMatch = parent_issue_url.match(/\/repos\/([^/]+)\/([^/]+)\/issues\/(\d+)/);
|
|
153
|
+
|
|
154
|
+
if (parentMatch) {
|
|
155
|
+
links.push({
|
|
156
|
+
type: CHILD_OF,
|
|
157
|
+
owner: parentMatch[1],
|
|
158
|
+
repo: parentMatch[2],
|
|
159
|
+
number: parseInt(parentMatch[3], 10)
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
148
164
|
if (typeof types !== 'undefined') {
|
|
149
165
|
return filterLinks(links, types);
|
|
150
166
|
}
|
package/lib/util/search.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wuffle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.76.0",
|
|
4
4
|
"description": "A multi-repository task board for GitHub issues",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Nico Rehwaldt",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@aws-sdk/client-s3": "^3.1037.0",
|
|
46
46
|
"async-didi": "^1.0.0",
|
|
47
|
-
"body-parser": "^2.
|
|
47
|
+
"body-parser": "^2.3.0",
|
|
48
48
|
"compression": "^1.8.1",
|
|
49
49
|
"express-session": "^1.19.0",
|
|
50
50
|
"fake-tag": "^5.0.0",
|
|
@@ -63,11 +63,11 @@
|
|
|
63
63
|
"@types/mocha": "^10.0.10",
|
|
64
64
|
"@types/sinon": "^21.0.1",
|
|
65
65
|
"chai": "^6.2.2",
|
|
66
|
-
"graphql": "^
|
|
67
|
-
"mocha": "^11.7.
|
|
68
|
-
"nock": "^14.0.
|
|
66
|
+
"graphql": "^17.0.0",
|
|
67
|
+
"mocha": "^11.7.6",
|
|
68
|
+
"nock": "^14.0.15",
|
|
69
69
|
"nodemon": "^3.1.14",
|
|
70
|
-
"npm-run-all2": "^
|
|
70
|
+
"npm-run-all2": "^9.0.2",
|
|
71
71
|
"sinon": "^22.0.0",
|
|
72
72
|
"sinon-chai": "^4.0.0",
|
|
73
73
|
"typescript": "^5.9.3"
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"index.js",
|
|
84
84
|
"wuffle.config.example.js"
|
|
85
85
|
],
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "3a21cf84280395b89d369422bbaf16f2b63c20dd"
|
|
87
87
|
}
|