Skip to content

Commit 6b75ea2

Browse files
Section with conflicts (#17)
* conflicts section * refactor Co-authored-by: Alexander <153199358+alexander-baz@users.noreply.github.com> * config setting for check conflicts * refactor check conflict setting * refactor * add check conflicts setting to docs * refactor --------- Co-authored-by: Alexander <153199358+alexander-baz@users.noreply.github.com>
1 parent 2203ceb commit 6b75ea2

File tree

6 files changed

+76
-37
lines changed

6 files changed

+76
-37
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,9 @@ unapproved: # Config for `unapproved` command
7979
onThreadsOpen: false # Whether to tag thread authors and PR author when threads are present
8080
onConflict: false # Whether to tag PR author if there are conflicts
8181
diffs: false # Show changed lines count or not (default - false)
82-
splitByReviewProgress: false # Whether to split the requests into those completely without review and those that under review
82+
splitByReviewProgress: false # Whether to split the requests into those completely without review, those that under review and those with conflicts
8383
requestsPerMessage: 15 # Merge requests count per message
84+
checkConflicts: false # Whether to check PR conflicts
8485
```
8586
8687
Groups in the config are [Gitlab project groups](https://docs.gitlab.com/ee/user/group/). You must specify the group or the project, or both.

api/GitLab.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const _ = require("lodash")
12
const url = require("../utils/url")
23
const network = require("../utils/network")
34

@@ -19,14 +20,14 @@ class GitLab {
1920

2021
project = id => this.__get(this.__getUrl("projects", id))
2122

22-
requests = project => {
23+
requests = (project, { withMergeStatusRecheck }) => {
2324
const query = {
2425
sort: "asc",
2526
per_page: 100,
2627
state: "opened",
2728
scope: "all",
2829
wip: "no",
29-
with_merge_status_recheck: true,
30+
with_merge_status_recheck: withMergeStatusRecheck,
3031
}
3132

3233
const uri = this.__getUrl("projects", project, "merge_requests")

gbot.example.yml

+1
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ unapproved:
3838
diffs: false
3939
splitByReviewProgress: false
4040
requestsPerMessage: 15
41+
checkConflicts: false

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@umbrellio/gbot",
3-
"version": "2.1.0",
3+
"version": "2.1.1",
44
"bin": "gbot",
55
"repository": "git@github.com:umbrellio/gbot.git",
66
"author": "Aleksei Bespalov <nulldefiner@gmail.com>",

tasks/Unapproved.js

+52-27
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const { NetworkError } = require("../utils/errors")
1111
class Unapproved extends BaseCommand {
1212
perform = () => {
1313
return this.projects
14-
.then(projects => Promise.all(projects.map(this.__getUnapprovedRequests)))
14+
.then(projects => Promise.all(projects.map(this.__getApplicableRequests)))
1515
.then(this.__sortRequests)
1616
.then(this.__buildMessages)
1717
.then(this.__logMessages)
@@ -69,7 +69,7 @@ class Unapproved extends BaseCommand {
6969
return this.__buildByReviewProgressMessages(requests, markup)
7070
}
7171

72-
return this.__buildGeneralRequestsMessages(requests, markup)
72+
return this.__buildGeneralRequestsMessages("unapproved", requests, markup)
7373
}
7474

7575
__buildEmptyListMessage = markup => {
@@ -84,36 +84,50 @@ class Unapproved extends BaseCommand {
8484

8585
__buildByReviewProgressMessages = (requests, markup) => {
8686
const messages = []
87-
const [toReviewRequests, underReviewRequests] = _.partition(requests, req => (
88-
req.approvals_left > 0 && !this.__isRequestUnderReview(req)
89-
))
87+
const [toReviewRequests, underReviewRequests, reviewedWithConflicts] = _.values(
88+
_.groupBy(requests, req => {
89+
const isUnderReview = this.__isRequestUnderReview(req)
90+
91+
switch (true) {
92+
case req.approvals_left > 0 && !isUnderReview:
93+
return 0 // To review
94+
case isUnderReview:
95+
return 1 // Under review
96+
default:
97+
return 2 // Reviewed with conflicts
98+
}
99+
}),
100+
)
90101

91102
const makeSection = _.flow(
92103
markup.makeBold,
93104
markup.makeText,
94105
markup.makePrimaryInfo,
95106
)
96107

97-
const toReviewSection = makeSection("Unapproved")
98-
const underReviewSection = makeSection("Under review")
99-
100-
const toReviewMessages = this.__buildGeneralRequestsMessages(toReviewRequests, markup)
101-
const underReviewMessages = this.__buildGeneralRequestsMessages(underReviewRequests, markup)
108+
const sections = [
109+
{ type: "unapproved", name: "Unapproved", requests: toReviewRequests },
110+
{ type: "under_review", name: "Under review", requests: underReviewRequests },
111+
{ type: "conflicts", name: "With conflicts", requests: reviewedWithConflicts },
112+
]
102113

103-
toReviewMessages.forEach((chunk, idx) => {
104-
messages.push(idx === 0 ? [toReviewSection, ...chunk] : chunk)
105-
})
114+
sections.forEach(settings => {
115+
const section = makeSection(settings.name)
116+
const sectionMessages = this.__buildGeneralRequestsMessages(
117+
settings.type, settings.requests, markup,
118+
)
106119

107-
underReviewMessages.forEach((chunk, idx) => {
108-
messages.push(idx === 0 ? [underReviewSection, ...chunk] : chunk)
120+
sectionMessages.forEach((chunk, idx) => {
121+
messages.push(idx === 0 ? [section, ...chunk] : chunk)
122+
})
109123
})
110124

111125
return messages
112126
}
113127

114-
__buildGeneralRequestsMessages = (requests, markup) => (
128+
__buildGeneralRequestsMessages = (type, requests, markup) => (
115129
this.__chunkRequests(requests).map(chunk => (
116-
chunk.map(this.__buildRequestDescription).map(markup.addDivider)
130+
chunk.map(request => this.__buildRequestDescription(type, request)).map(markup.addDivider)
117131
))
118132
)
119133

@@ -123,21 +137,25 @@ class Unapproved extends BaseCommand {
123137
return _.chunk(requests, requestsPerMessage)
124138
}
125139

126-
__buildRequestDescription = request =>
127-
new UnapprovedRequestDescription(request, this.config).build()
140+
__buildRequestDescription = (type, request) =>
141+
new UnapprovedRequestDescription(type, request, this.config).build()
128142

129143
__sortRequests = requests => requests
130144
.flat().sort((a, b) => new Date(a.updated_at) - new Date(b.updated_at))
131145

132-
__getUnapprovedRequests = project => this.__getExtendedRequests(project.id)
146+
__getApplicableRequests = project => this.__getExtendedRequests(project.id)
133147
.then(requests => requests.filter(req => {
134148
const isCompleted = !req.work_in_progress
135149
const isUnapproved = req.approvals_left > 0
136150
const isUnderReview = this.__isRequestUnderReview(req)
137151
const hasPathsChanges = this.__hasPathsChanges(req.changes, project.paths)
152+
const checkConflicts = this.__getConfigSetting("unapproved.checkConflicts", false)
138153
const hasConflicts = req.has_conflicts
154+
const isApplicable = checkConflicts
155+
? isUnapproved || isUnderReview || hasConflicts
156+
: isUnapproved || isUnderReview
139157

140-
return isCompleted && hasPathsChanges && (isUnapproved || isUnderReview || hasConflicts)
158+
return isCompleted && hasPathsChanges && isApplicable
141159
}))
142160

143161
__isRequestUnderReview = req => req.discussions
@@ -156,12 +174,19 @@ class Unapproved extends BaseCommand {
156174
))
157175
}
158176

159-
__getExtendedRequests = projectId => this.gitlab
160-
.project(projectId)
161-
.then(project => this.gitlab.requests(project.id).then(requests => {
162-
const promises = requests.map(request => this.__getExtendedRequest(project, request))
163-
return Promise.all(promises)
164-
}))
177+
__getExtendedRequests = projectId => {
178+
const checkConflicts = this.__getConfigSetting("unapproved.checkConflicts", false)
179+
180+
return this.gitlab
181+
.project(projectId)
182+
.then(project => this.gitlab
183+
.requests(project.id, { withMergeStatusRecheck: checkConflicts })
184+
.then(requests => {
185+
const promises = requests.map(request => this.__getExtendedRequest(project, request))
186+
return Promise.all(promises)
187+
}),
188+
)
189+
}
165190

166191
__getExtendedRequest = (project, request) => Promise.resolve(request)
167192
.then(req => this.__appendApprovals(project, req))

tasks/unapproved/UnapprovedRequestDescription.js

+17-6
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@ const stringUtils = require("../../utils/strings")
55
const markupUtils = require("../../utils/markup")
66

77
class UnapprovedRequestDescription {
8-
constructor (request, config) {
9-
this.config = config
8+
constructor (type, request, config) {
9+
this.type = type
1010
this.request = request
11+
this.config = config
1112
}
1213

1314
build = () => {
1415
const markup = markupUtils[this.config.messenger.markup]
1516
const tagAuthor = this.__getConfigSetting("unapproved.tag.author", false)
1617
const tagOnThreadsOpen = this.__getConfigSetting("unapproved.tag.onThreadsOpen", false)
1718
const tagOnConflict = this.__getConfigSetting("unapproved.tag.onConflict", false)
19+
const checkConflicts = this.__getConfigSetting("unapproved.checkConflicts", false)
1820

1921
const { author } = this.request
2022

@@ -23,8 +25,11 @@ class UnapprovedRequestDescription {
2325
const projectLink = markup.makeLink(this.request.project.name, this.request.project.web_url)
2426
const unresolvedAuthors = this.__unresolvedAuthorsString(markup)
2527
const tagAuthorOnThread = tagOnThreadsOpen && unresolvedAuthors.length > 0
28+
const tagAuthorInPrimaryMessage = this.type === "conflicts"
29+
? tagOnConflict
30+
: tagAuthor || tagAuthorOnThread
2631
const authorString = this.__authorString(
27-
markup, author.username, { tag: tagAuthor || tagAuthorOnThread },
32+
markup, author.username, { tag: tagAuthorInPrimaryMessage },
2833
)
2934
const approvedBy = this.__approvedByString(markup)
3035
const optionalDiff = this.__optionalDiffString()
@@ -39,7 +44,10 @@ class UnapprovedRequestDescription {
3944
]
4045
const requestMessageText = _.compact(requestMessageParts).join(" ")
4146
const primaryMessage = markup.makePrimaryInfo(
42-
markup.makeText(requestMessageText, { withMentions: false }),
47+
markup.makeText(
48+
requestMessageText,
49+
{ withMentions: this.type === "conflicts" && tagOnConflict },
50+
),
4351
)
4452
const secondaryMessageParts = []
4553

@@ -57,14 +65,17 @@ class UnapprovedRequestDescription {
5765
secondaryMessageParts.push(msg)
5866
}
5967

60-
if (hasConflicts) {
68+
if (checkConflicts && hasConflicts) {
69+
const authorString = this.__authorString(markup, author.username, { tag: tagOnConflict })
6170
const text = `conflicts: ${authorString}`
6271
const msg = markup.makeText(text, { withMentions: tagOnConflict })
6372

6473
secondaryMessageParts.push(msg)
6574
}
6675

67-
const secondaryMessage = markup.makeAdditionalInfo(secondaryMessageParts)
76+
const secondaryMessage = markup.makeAdditionalInfo(
77+
this.type === "conflicts" ? [] : secondaryMessageParts,
78+
)
6879
return markup.composeBody(primaryMessage, secondaryMessage)
6980
}
7081

0 commit comments

Comments
 (0)