wuffle 0.73.3 → 0.75.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/lib/apps/auth-routes/AuthRoutes.js +16 -14
- package/lib/apps/{automatic-dev-flow.js → automatic-dev-flow/AutomaticDevFlow.js} +12 -13
- package/lib/apps/automatic-dev-flow/index.js +6 -0
- package/lib/apps/background-sync/BackgroundSync.js +41 -83
- package/lib/apps/background-sync/BackgroundSyncBackend.js +140 -0
- package/lib/apps/background-sync/index.js +3 -1
- package/lib/apps/board-api-routes/board-api-routes.js +20 -14
- package/lib/apps/dump-store/s3/S3.js +12 -0
- package/lib/apps/{events-sync.js → events-sync/EventsSync.js} +7 -5
- package/lib/apps/events-sync/index.js +6 -0
- package/lib/apps/github-app/GithubApp.js +69 -6
- 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/log-events.js +4 -4
- package/lib/apps/{reindex-store.js → reindex-store/ReindexStore.js} +6 -4
- package/lib/apps/reindex-store/index.js +6 -0
- 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/index.js +13 -5
- package/lib/probot/CustomProbot.js +2 -1
- package/lib/util/search.js +1 -1
- package/package.json +6 -5
|
@@ -28,6 +28,7 @@ const RequiredEvents = [
|
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* @typedef {import('./types.js').Installation} Installation
|
|
31
|
+
* @typedef {import('../../types.js').Octokit} Octokit
|
|
31
32
|
*/
|
|
32
33
|
|
|
33
34
|
/**
|
|
@@ -53,8 +54,19 @@ export default function GithubApp(config, app, logger, injector) {
|
|
|
53
54
|
|
|
54
55
|
// cached data //////////////////
|
|
55
56
|
|
|
57
|
+
/**
|
|
58
|
+
* @type {Promise<Installation[]> | null}
|
|
59
|
+
*/
|
|
56
60
|
let installations = null;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @type {Promise<Record<string, Installation>> | null}
|
|
64
|
+
*/
|
|
57
65
|
let installationsByLogin = null;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @type {Promise<Record<string, Installation>> | null}
|
|
69
|
+
*/
|
|
58
70
|
let installationsById = null;
|
|
59
71
|
|
|
60
72
|
// reactivity ////////////////////
|
|
@@ -71,12 +83,17 @@ export default function GithubApp(config, app, logger, injector) {
|
|
|
71
83
|
// functionality /////////////////
|
|
72
84
|
|
|
73
85
|
/**
|
|
74
|
-
* @return {Promise<
|
|
86
|
+
* @return {Promise<Octokit>}
|
|
75
87
|
*/
|
|
76
88
|
function getAppScopedClient() {
|
|
77
89
|
return app.auth();
|
|
78
90
|
}
|
|
79
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Return installations
|
|
94
|
+
*
|
|
95
|
+
* @return {Promise<Installation[]>}
|
|
96
|
+
*/
|
|
80
97
|
function getInstallations() {
|
|
81
98
|
installations = installations || fetchInstallations().then(installations => {
|
|
82
99
|
|
|
@@ -90,6 +107,11 @@ export default function GithubApp(config, app, logger, injector) {
|
|
|
90
107
|
return installations;
|
|
91
108
|
}
|
|
92
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Return installations by ID
|
|
112
|
+
*
|
|
113
|
+
* @return {Promise<Record<string, Installation>>}
|
|
114
|
+
*/
|
|
93
115
|
function getInstallationsById() {
|
|
94
116
|
|
|
95
117
|
installationsById = installationsById || getInstallations().then(
|
|
@@ -106,9 +128,9 @@ export default function GithubApp(config, app, logger, injector) {
|
|
|
106
128
|
/**
|
|
107
129
|
* Get an installation for the given id.
|
|
108
130
|
*
|
|
109
|
-
* @param {string} id
|
|
131
|
+
* @param {string|number} id
|
|
110
132
|
*
|
|
111
|
-
* @return {Promise<Installation
|
|
133
|
+
* @return {Promise<Installation | undefined>}
|
|
112
134
|
*/
|
|
113
135
|
function getInstallationById(id) {
|
|
114
136
|
return getInstallationsById().then(byId => {
|
|
@@ -119,7 +141,7 @@ export default function GithubApp(config, app, logger, injector) {
|
|
|
119
141
|
/**
|
|
120
142
|
* Return map of installations, grouped by org / login.
|
|
121
143
|
*
|
|
122
|
-
* @return {Promise<
|
|
144
|
+
* @return {Promise<Record<string, Installation>>}
|
|
123
145
|
*/
|
|
124
146
|
function getInstallationsByLogin() {
|
|
125
147
|
|
|
@@ -155,6 +177,13 @@ export default function GithubApp(config, app, logger, injector) {
|
|
|
155
177
|
});
|
|
156
178
|
}
|
|
157
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Return true if installation is enabled.
|
|
182
|
+
*
|
|
183
|
+
* @param {Installation} installation
|
|
184
|
+
*
|
|
185
|
+
* @return {Promise<boolean>}
|
|
186
|
+
*/
|
|
158
187
|
function isInstallationEnabled(installation) {
|
|
159
188
|
|
|
160
189
|
const {
|
|
@@ -240,10 +269,10 @@ export default function GithubApp(config, app, logger, injector) {
|
|
|
240
269
|
/**
|
|
241
270
|
* Fetch active installations.
|
|
242
271
|
*
|
|
243
|
-
* @return {Promise<
|
|
272
|
+
* @return {Promise<Installation[]>} installations
|
|
244
273
|
*/
|
|
245
274
|
function fetchInstallations() {
|
|
246
|
-
return /** @type Promise<
|
|
275
|
+
return /** @type Promise<Installation[]> */ (getAppScopedClient().then(
|
|
247
276
|
octokit => octokit.paginate(
|
|
248
277
|
octokit.rest.apps.listInstallations,
|
|
249
278
|
{ per_page: 100 }
|
|
@@ -253,13 +282,47 @@ export default function GithubApp(config, app, logger, injector) {
|
|
|
253
282
|
|
|
254
283
|
// api ///////////////////
|
|
255
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Return an application-scoped octokit client.
|
|
287
|
+
*
|
|
288
|
+
* @return {Promise<Octokit>}
|
|
289
|
+
*/
|
|
256
290
|
this.getAppScopedClient = getAppScopedClient;
|
|
257
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Return true if installation is enabled.
|
|
294
|
+
*
|
|
295
|
+
* @param {Installation} installation
|
|
296
|
+
*
|
|
297
|
+
* @return {Promise<boolean>}
|
|
298
|
+
*/
|
|
258
299
|
this.isInstallationEnabled = isInstallationEnabled;
|
|
259
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Get an installation for the given login.
|
|
303
|
+
*
|
|
304
|
+
* This method throws if an installation for the given login does not exist.
|
|
305
|
+
*
|
|
306
|
+
* @param {string} login
|
|
307
|
+
*
|
|
308
|
+
* @return {Promise<Installation>}
|
|
309
|
+
*/
|
|
260
310
|
this.getInstallationByLogin = getInstallationByLogin;
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Get an installation for the given id.
|
|
314
|
+
*
|
|
315
|
+
* @param {string|number} id
|
|
316
|
+
*
|
|
317
|
+
* @return {Promise<Installation | undefined>}
|
|
318
|
+
*/
|
|
261
319
|
this.getInstallationById = getInstallationById;
|
|
262
320
|
|
|
321
|
+
/**
|
|
322
|
+
* Return installations
|
|
323
|
+
*
|
|
324
|
+
* @return {Promise<Installation[]>}
|
|
325
|
+
*/
|
|
263
326
|
this.getInstallations = getInstallations;
|
|
264
327
|
|
|
265
328
|
}
|
|
@@ -3,6 +3,8 @@ import { repoAndOwner, Cache } from '../../util/index.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* This component updates the stored issues based on GitHub events.
|
|
5
5
|
*
|
|
6
|
+
* It also hooks into `BackgroundSync` to fetch checks for a PR.
|
|
7
|
+
*
|
|
6
8
|
* @constructor
|
|
7
9
|
*
|
|
8
10
|
* @param {import('../webhook-events/WebhookEvents.js').default} webhookEvents
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { repoAndOwner } from '../../util/index.js';
|
|
2
1
|
import { filterUser, filterIssue } from '../../filters.js';
|
|
3
|
-
import gql from 'fake-tag';
|
|
4
2
|
|
|
5
3
|
|
|
6
4
|
/**
|
|
7
5
|
* This component updates the stored issues based on GitHub events.
|
|
8
6
|
*
|
|
7
|
+
* It also hooks into `BackgroundSync` to fetch comments for issues/PRs.
|
|
8
|
+
*
|
|
9
9
|
* @constructor
|
|
10
10
|
*
|
|
11
11
|
* @param {import('../webhook-events/WebhookEvents.js').default} webhookEvents
|
|
12
12
|
* @param {import('../../events.js').default} events
|
|
13
|
-
* @param {import('../github-client/GithubClient.js').default} githubClient
|
|
14
13
|
* @param {import('../../store.js').default} store
|
|
15
14
|
* @param {import('../issue-filter/IssueFilter.js').default} issueFilter
|
|
16
15
|
* @param {import('../../types.js').Logger } logger
|
|
16
|
+
* @param {import('./GithubCommentsBackend.js').default} githubCommentsBackend
|
|
17
17
|
*/
|
|
18
|
-
export default function GithubComments(webhookEvents, events,
|
|
18
|
+
export default function GithubComments(webhookEvents, events, store, issueFilter, logger, githubCommentsBackend) {
|
|
19
19
|
|
|
20
20
|
const log = logger.child({
|
|
21
21
|
name: 'wuffle:github-comments'
|
|
@@ -32,81 +32,10 @@ export default function GithubComments(webhookEvents, events, githubClient, stor
|
|
|
32
32
|
} = event;
|
|
33
33
|
|
|
34
34
|
const {
|
|
35
|
-
id
|
|
36
|
-
number
|
|
35
|
+
id
|
|
37
36
|
} = issue;
|
|
38
37
|
|
|
39
|
-
const
|
|
40
|
-
repo,
|
|
41
|
-
owner
|
|
42
|
-
} = repoAndOwner(issue);
|
|
43
|
-
|
|
44
|
-
const github = await githubClient.getOrgScoped(owner);
|
|
45
|
-
|
|
46
|
-
const result = await github.graphql(gql`
|
|
47
|
-
|
|
48
|
-
fragment CommentInfo on IssueComment {
|
|
49
|
-
id: databaseId
|
|
50
|
-
node_id: id
|
|
51
|
-
body: bodyText
|
|
52
|
-
created_at: publishedAt
|
|
53
|
-
authorAssociation,
|
|
54
|
-
html_url: url,
|
|
55
|
-
user: author {
|
|
56
|
-
login
|
|
57
|
-
avatar_url: avatarUrl,
|
|
58
|
-
html_url: url
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
query FetchComments(
|
|
63
|
-
$repo: String!,
|
|
64
|
-
$owner: String!,
|
|
65
|
-
$issue_number: Int!,
|
|
66
|
-
$after: String
|
|
67
|
-
) {
|
|
68
|
-
repository(name: $repo, owner: $owner) {
|
|
69
|
-
issueOrPullRequest(number: $issue_number) {
|
|
70
|
-
...on Issue {
|
|
71
|
-
comments(first: 100, after: $after) {
|
|
72
|
-
edges {
|
|
73
|
-
node {
|
|
74
|
-
...CommentInfo
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
pageInfo {
|
|
78
|
-
endCursor
|
|
79
|
-
hasNextPage
|
|
80
|
-
}
|
|
81
|
-
totalCount
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
...on PullRequest {
|
|
85
|
-
comments(first: 100, after: $after) {
|
|
86
|
-
edges {
|
|
87
|
-
node {
|
|
88
|
-
...CommentInfo
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
pageInfo {
|
|
92
|
-
endCursor
|
|
93
|
-
hasNextPage
|
|
94
|
-
}
|
|
95
|
-
totalCount
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}`,
|
|
101
|
-
{
|
|
102
|
-
owner,
|
|
103
|
-
repo,
|
|
104
|
-
issue_number: number
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const comments = (
|
|
108
|
-
result.repository.issueOrPullRequest.comments.edges.map(e => e.node)
|
|
109
|
-
);
|
|
38
|
+
const comments = await githubCommentsBackend.getIssueComments(issue);
|
|
110
39
|
|
|
111
40
|
await store.queueUpdate({
|
|
112
41
|
id,
|
|
@@ -115,7 +44,9 @@ export default function GithubComments(webhookEvents, events, githubClient, stor
|
|
|
115
44
|
});
|
|
116
45
|
|
|
117
46
|
webhookEvents.on([
|
|
118
|
-
'issue_comment'
|
|
47
|
+
'issue_comment.created',
|
|
48
|
+
'issue_comment.edited',
|
|
49
|
+
'issue_comment.deleted'
|
|
119
50
|
], ifEnabled(async ({ payload }) => {
|
|
120
51
|
const {
|
|
121
52
|
action,
|
|
@@ -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
|