Skip to content

Commit 4aee055

Browse files
authored
Fix issues with Slack integration (#1448)
2 parents dfd8ddf + 537e6e0 commit 4aee055

2 files changed

Lines changed: 54 additions & 16 deletions

File tree

.github/workflows/slack/github.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,17 @@ const parseImages = (body: string): ParsedBody => {
228228
return { text, images }
229229
}
230230

231+
const COMMENT = /<!--[\s\S]*?-->[ \t]*\r?\n?/g
232+
const BODY_LIMIT = 1000
233+
234+
/**
235+
* Truncates text to a character limit, appending an ellipsis
236+
*
237+
* @return string
238+
*/
239+
const truncate = (text: string, limit: number): string =>
240+
text.length > limit ? `${text.slice(0, limit).trimEnd()}…` : text
241+
231242
const SUGGESTION = /```suggestion\r?\n(.*?)```/gs
232243

233244
/**
@@ -248,14 +259,17 @@ const jiraSection = (pr: PullRequest): Block => {
248259
}
249260

250261
/**
251-
* Renders the PR body as a text section plus any inline images
262+
* Renders the PR body as a text section plus any inline images, dropping
263+
* template comments and capping length
252264
*
253265
* @return Block[]
254266
*/
255267
const prBodyBlocks = (body: string | null): Block[] => {
256268
if (!body) return []
257-
const { text, images } = parseImages(body)
258-
return [...(text ? [section(mrkdwn(text))] : []), ...images]
269+
// strip comments first so images inside them don't get extracted
270+
const { text, images } = parseImages(body.replace(COMMENT, ""))
271+
const capped = truncate(text, BODY_LIMIT)
272+
return [...(capped ? [section(mrkdwn(capped))] : []), ...images]
259273
}
260274

261275
/**

.github/workflows/slack/pr_review.ts

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,27 +61,45 @@ const send = async () => {
6161
const repo_owner = github.repository_owner
6262
const repo_name = github.event.repository.name
6363
const pr_number = github.event.pull_request.number
64+
const pr_url = `https://api.github.com/repos/${repo_owner}/${repo_name}/pulls/${pr_number}`
6465

6566
if (github.event_name === "pull_request_review_comment") {
6667
const event = reviewCommentEventFromContext(github)
67-
68+
const review_id = github.event.comment.pull_request_review_id
6869
const reply_to_id = github.event.comment.in_reply_to_id
69-
if (reply_to_id) {
70-
const url = `https://api.github.com/repos/${repo_owner}/${repo_name}/pulls/${pr_number}/comments?per_page=100`
71-
const res = await ghGet(url)
70+
71+
// real review renders this comment in its collapsible
72+
if (review_id) {
73+
const res = await ghGet(`${pr_url}/reviews/${review_id}`)
74+
75+
if (res.ok) {
76+
const review: any = await res.json()
77+
if (review.body || review.state?.toLowerCase() !== "commented")
78+
process.exit(0)
79+
}
80+
}
81+
82+
if (review_id || reply_to_id) {
83+
const res = await ghGet(`${pr_url}/comments?per_page=100`)
7284

7385
if (res.ok) {
7486
const all: any[] = await res.json()
75-
// in_reply_to_id = thread root; want newest reply before mine
87+
const siblings = all.filter(
88+
(c) => c.pull_request_review_id === review_id,
89+
)
90+
// wrapper holds one comment; more = batched review
91+
if (siblings.length > 1) process.exit(0)
92+
// in_reply_to_id = thread root; want newest before mine
7693
const parent = all
7794
.filter(
7895
(c) =>
79-
(c.id === reply_to_id || c.in_reply_to_id === reply_to_id) &&
96+
(c.id === reply_to_id ||
97+
c.in_reply_to_id === reply_to_id) &&
8098
c.id !== github.event.comment.id,
8199
)
82100
.sort((a, b) => a.id - b.id)
83101
.at(-1)
84-
if (parent?.user && parent.body != null) {
102+
if (reply_to_id && parent?.user && parent.body != null) {
85103
event.replyTo = { author: parent.user.login, body: parent.body }
86104
}
87105
}
@@ -99,13 +117,14 @@ const send = async () => {
99117
if (github.event.action !== "dismissed") {
100118
const review_id = github.event.review.id
101119

102-
const url = `https://api.github.com/repos/${repo_owner}/${repo_name}/pulls/${pr_number}/reviews/${review_id}/comments?per_page=100`
103-
const comments_res = await ghGet(url)
120+
const comments_res = await ghGet(
121+
`${pr_url}/reviews/${review_id}/comments?per_page=100`,
122+
)
104123

105124
comments = await comments_res.json()
106125
}
107126

108-
// lone bodyless comment = single/reply wrapper, already sent; batches (>1) kept
127+
// lone bodyless comment = wrapper, already sent; batches kept
109128
if (
110129
github.event.review.state === "commented" &&
111130
!github.event.review.body &&
@@ -116,17 +135,22 @@ const send = async () => {
116135

117136
const event = reviewEventFromContext(github)
118137

119-
// dismiss message isn't in the payload; pull it from the timeline
138+
// dismiss message isn't in the payload
120139
if (github.event.action === "dismissed") {
121140
const url = `https://api.github.com/repos/${repo_owner}/${repo_name}/issues/${pr_number}/timeline?per_page=100`
122141
const res = await ghGet(url)
123142

124143
if (res.ok) {
125144
const events: any[] = await res.json()
126145
const dismissal = events
127-
.filter(e => e.event === "review_dismissed" && e.dismissed_review?.review_id === github.event.review.id)
146+
.filter(
147+
(e) =>
148+
e.event === "review_dismissed" &&
149+
e.dismissed_review?.review_id === github.event.review.id,
150+
)
128151
.at(-1)
129-
if (dismissal?.dismissed_review?.dismissal_message) event.review.body = dismissal.dismissed_review.dismissal_message
152+
if (dismissal?.dismissed_review?.dismissal_message)
153+
event.review.body = dismissal.dismissed_review.dismissal_message
130154
}
131155
}
132156

0 commit comments

Comments
 (0)