Skip to content

Commit

Permalink
feat: send notifications for closed, merged and reopened PRs
Browse files Browse the repository at this point in the history
  • Loading branch information
christophehurpeau committed Dec 20, 2020
1 parent 084496c commit 4cfc6a0
Show file tree
Hide file tree
Showing 18 changed files with 724 additions and 283 deletions.
319 changes: 217 additions & 102 deletions dist/index-node12-dev.cjs.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index-node12-dev.cjs.js.map

Large diffs are not rendered by default.

319 changes: 217 additions & 102 deletions dist/index-node12.cjs.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index-node12.cjs.js.map

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/app/org-settings.tsx
Expand Up @@ -13,6 +13,9 @@ import Layout from '../views/Layout';
import { getUser } from './auth';

const dmMessages: Record<MessageCategory, string> = {
'pr-lifecycle': 'Your PR is closed, merged, reopened',
'pr-lifecycle-follow':
"Someone closed, merged, reopened a PR you're reviewing",
'pr-review': 'You are assigned to a review, someone reviewed your PR',
'pr-review-follow': "Someone reviewed a PR you're also reviewing",
'pr-comment': 'Someone commented on your PR',
Expand Down
9 changes: 6 additions & 3 deletions src/context/accountContext.ts
@@ -1,5 +1,6 @@
import { Lock } from 'lock';
import type { Context } from 'probot';
import type { PullRequestWithDecentData } from 'events/pr-handlers/utils/PullRequestData';
import type { Config } from '../accountConfigs';
import type { Org, User, AccountEmbed, AccountType } from '../mongo';
import { ExcludesFalsy } from '../utils/Excludes';
Expand All @@ -24,7 +25,7 @@ export interface AccountContext<
getTeamsForLogin: (githubLogin: string) => TeamNames[];
approveShouldWait: (
reviewerGroup: GroupNames | undefined,
requestedReviewers: any[],
pullRequest: PullRequestWithDecentData,
{
includesReviewerGroup,
includesWaitForGroups,
Expand Down Expand Up @@ -119,15 +120,17 @@ const initAccountContext = async (

approveShouldWait: (
reviewerGroup,
requestedReviewers,
pullRequest,
{ includesReviewerGroup, includesWaitForGroups },
): boolean => {
if (!reviewerGroup) return false;

const requestedReviewerGroups = getReviewerGroups(
requestedReviewers.map((request) => request.login),
pullRequest.requested_reviewers.map((request) => request.login),
);

// TODO pullRequest.requested_teams

// contains another request of a reviewer in the same group
if (
includesReviewerGroup &&
Expand Down
2 changes: 2 additions & 0 deletions src/dm/MessageCategory.ts
@@ -1,4 +1,6 @@
export type MessageCategory =
| 'pr-lifecycle'
| 'pr-lifecycle-follow'
| 'pr-review'
| 'pr-review-follow'
| 'pr-comment'
Expand Down
2 changes: 2 additions & 0 deletions src/dm/defaultDmSettings.ts
@@ -1,6 +1,8 @@
import type { MessageCategory } from './MessageCategory';

export const defaultDmSettings: Record<MessageCategory, boolean> = {
'pr-lifecycle': true,
'pr-lifecycle-follow': true,
'pr-review': true,
'pr-review-follow': true,
'pr-comment': true,
Expand Down
4 changes: 1 addition & 3 deletions src/events/pr-handlers/actions/editOpenedPR.ts
Expand Up @@ -38,12 +38,10 @@ export const editOpenedPR = async <
pullRequest: PullRequestWithDecentData,
context: Context<E>,
repoContext: RepoContext,
reviewflowPrContext: ReviewflowPrContext | null,
reviewflowPrContext: ReviewflowPrContext,
shouldUpdateCommentBodyInfos: boolean,
previousSha?: string,
): Promise<void> => {
if (reviewflowPrContext === null) return;

const title = repoContext.config.trimTitle
? cleanTitle(pullRequest.title)
: pullRequest.title;
Expand Down
68 changes: 67 additions & 1 deletion src/events/pr-handlers/closed.ts
@@ -1,8 +1,12 @@
import type { Probot } from 'probot';
import type { AccountInfo } from 'context/getOrCreateAccount';
import type { AppContext } from '../../context/AppContext';
import * as slackUtils from '../../slack/utils';
import { updateReviewStatus } from './actions/updateReviewStatus';
import { parseOptions } from './actions/utils/body/parseBody';
import { createPullRequestHandler } from './utils/createPullRequestHandler';
import { getReviewersAndReviewStates } from './utils/getReviewersAndReviewStates';
import { getRolesFromPullRequestAndReviewers } from './utils/getRolesFromPullRequestAndReviewers';

export default function closed(app: Probot, appContext: AppContext): void {
app.on(
Expand All @@ -11,7 +15,9 @@ export default function closed(app: Probot, appContext: AppContext): void {
appContext,
(payload) => payload.pull_request,
async (pullRequest, context, repoContext, reviewflowPrContext) => {
if (!repoContext.shouldIgnore && reviewflowPrContext) {
/* if repo is not ignored */
if (reviewflowPrContext) {
/* update status, update automerge queue, delete branch */
const repo = context.payload.repository;

if (pullRequest.merged) {
Expand Down Expand Up @@ -49,11 +55,71 @@ export default function closed(app: Probot, appContext: AppContext): void {
}
}

/* update slack home */
if (pullRequest.requested_reviewers) {
pullRequest.requested_reviewers.forEach((requestedReviewer) => {
repoContext.slack.updateHome(requestedReviewer.login);
});
}

if (pullRequest.assignees) {
pullRequest.assignees.forEach((assignee) => {
repoContext.slack.updateHome(assignee.login);
});
}

/* send notifications to assignees and followers */
const { reviewers } = await getReviewersAndReviewStates(
context,
repoContext,
);
const {
owner,
assignees,
followers,
} = getRolesFromPullRequestAndReviewers(pullRequest, reviewers);

const senderMention = repoContext.slack.mention(
context.payload.sender.login,
);
const ownerMention = repoContext.slack.mention(owner.login);
const prLink = slackUtils.createPrLink(pullRequest, repoContext);

const createMessage = (to: AccountInfo): string => {
const ownerPart = slackUtils.createOwnerPart(
ownerMention,
pullRequest,
to,
);

return pullRequest.merged
? `:rocket: ${senderMention} merged ${ownerPart} ${prLink}`
: `:wastebasket: ${senderMention} closed ${ownerPart} ${prLink}`;
};

assignees.map((assignee) => {
if (context.payload.sender.id === assignee.id) return;
return repoContext.slack.postMessage(
'pr-lifecycle',
assignee.id,
assignee.login,
{
text: createMessage(assignee),
},
);
});

followers.map((follower) => {
if (context.payload.sender.id === follower.id) return;
return repoContext.slack.postMessage(
'pr-lifecycle-follow',
follower.id,
follower.login,
{
text: createMessage(follower),
},
);
});
},
),
);
Expand Down
85 changes: 71 additions & 14 deletions src/events/pr-handlers/reopened.ts
@@ -1,16 +1,19 @@
import type { Probot } from 'probot';
import type { AccountInfo } from 'context/getOrCreateAccount';
import type { AppContext } from '../../context/AppContext';
import * as slackUtils from '../../slack/utils';
import { editOpenedPR } from './actions/editOpenedPR';
import { updateReviewStatus } from './actions/updateReviewStatus';
import { createPullRequestHandler } from './utils/createPullRequestHandler';
import { getReviewersAndReviewStates } from './utils/getReviewersAndReviewStates';
import { getRolesFromPullRequestAndReviewers } from './utils/getRolesFromPullRequestAndReviewers';

export default function closed(app: Probot, appContext: AppContext): void {
export default function reopened(app: Probot, appContext: AppContext): void {
app.on(
'pull_request.reopened',
createPullRequestHandler(
appContext,
(payload, context, repoContext) => {
if (repoContext.shouldIgnore) return null;
return payload.pull_request;
},
async (
Expand All @@ -19,19 +22,73 @@ export default function closed(app: Probot, appContext: AppContext): void {
repoContext,
reviewflowPrContext,
): Promise<void> => {
await Promise.all([
updateReviewStatus(pullRequest, context, repoContext, 'dev', {
add: ['needsReview'],
remove: ['approved'],
}),
editOpenedPR(
/* if repo is not ignored */
if (reviewflowPrContext) {
await Promise.all([
updateReviewStatus(pullRequest, context, repoContext, 'dev', {
add: ['needsReview'],
remove: ['approved'],
}),
editOpenedPR(
pullRequest,
context,
repoContext,
reviewflowPrContext,
true,
),
]);
}

/* send notifications to assignees and followers */
const { reviewers } = await getReviewersAndReviewStates(
context,
repoContext,
);
const {
owner,
assignees,
followers,
} = getRolesFromPullRequestAndReviewers(pullRequest, reviewers);

const senderMention = repoContext.slack.mention(
context.payload.sender.login,
);
const ownerMention = repoContext.slack.mention(owner.login);
const prLink = slackUtils.createPrLink(pullRequest, repoContext);

const createMessage = (to: AccountInfo): string => {
const ownerPart = slackUtils.createOwnerPart(
ownerMention,
pullRequest,
context,
repoContext,
reviewflowPrContext,
true,
),
]);
to,
);

return `:recycle: ${senderMention} reopened ${ownerPart} ${prLink}`;
};

assignees.map((assignee) => {
if (context.payload.sender.id === assignee.id) return;
return repoContext.slack.postMessage(
'pr-lifecycle',
assignee.id,
assignee.login,
{
text: createMessage(assignee),
},
);
});

followers.map((follower) => {
if (context.payload.sender.id === follower.id) return;
return repoContext.slack.postMessage(
'pr-lifecycle-follow',
follower.id,
follower.login,
{
text: createMessage(follower),
},
);
});
},
),
);
Expand Down
9 changes: 5 additions & 4 deletions src/events/pr-handlers/reviewDismissed.ts
Expand Up @@ -22,7 +22,7 @@ export default function reviewDismissed(
reviewflowPrContext,
): Promise<void> => {
const sender = context.payload.sender;
const reviewer = (context.payload as any).review.user;
const reviewer = context.payload.review.user;
const reviewerGroup = repoContext.getReviewerGroup(reviewer.login);

if (
Expand All @@ -36,12 +36,13 @@ export default function reviewDismissed(
context,
repoContext,
);

const hasChangesRequestedInReviews =
reviewStates[reviewerGroup].changesRequested !== 0;
const hasApprovals = reviewStates[reviewerGroup].approved !== 0;
const hasRequestedReviewsForGroup = repoContext.approveShouldWait(
reviewerGroup,
updatedPr.requested_reviewers,
updatedPr,
{ includesReviewerGroup: true },
);

Expand Down Expand Up @@ -90,7 +91,7 @@ export default function reviewDismissed(
assignee.id,
assignee.login,
{
text: `:skull: ${repoContext.slack.mention(
text: `:recycle: ${repoContext.slack.mention(
reviewer.login,
)} dismissed his review on ${slackUtils.createPrLink(
pullRequest,
Expand All @@ -105,7 +106,7 @@ export default function reviewDismissed(
reviewer.id,
reviewer.login,
{
text: `:skull: ${repoContext.slack.mention(
text: `:recycle: ${repoContext.slack.mention(
sender.login,
)} dismissed your review on ${slackUtils.createPrLink(
pullRequest,
Expand Down
2 changes: 1 addition & 1 deletion src/events/pr-handlers/reviewRequestRemoved.ts
Expand Up @@ -32,7 +32,7 @@ export default function reviewRequestRemoved(
) {
const hasRequestedReviewsForGroup = repoContext.approveShouldWait(
reviewerGroup,
pullRequest.requested_reviewers,
pullRequest,
{
includesReviewerGroup: true,
},
Expand Down

0 comments on commit 4cfc6a0

Please sign in to comment.