Skip to content

Commit c9ce7a7

Browse files
committed
wip! Implement using reactions for confirmation in most cases
1 parent 75485f3 commit c9ce7a7

2 files changed

Lines changed: 143 additions & 65 deletions

File tree

src/Logic.hs

Lines changed: 95 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,79 +1152,116 @@ proceedUntilFixedPoint state = do
11521152
then return state
11531153
else proceedUntilFixedPoint newState
11541154

1155-
-- Describe the status of the pull request.
1156-
describeStatus :: BaseBranch -> PullRequestId -> PullRequest -> ProjectState -> Text
1157-
describeStatus (BaseBranch projectBaseBranchName) prId pr state = case Pr.classifyPullRequest pr of
1158-
PrStatusAwaitingApproval -> "Pull request awaiting approval."
1155+
-- | Feedback on a successfully parsed command.
1156+
data Feedback
1157+
= -- | Leave a comment.
1158+
CommentFeedback Text
1159+
| -- | Leave only a reaction.
1160+
ReactionFeedback ReactableId GithubApi.ReactionContent
1161+
1162+
-- | Determine what kind of feedback to leave based on the status of a PR.
1163+
feedbackOnStatus :: BaseBranch -> PullRequestId -> PullRequest -> ProjectState -> Feedback
1164+
feedbackOnStatus (BaseBranch projectBaseBranchName) prId pr state = case Pr.classifyPullRequest pr of
1165+
PrStatusAwaitingApproval -> CommentFeedback "Pull request awaiting approval."
11591166
PrStatusApproved ->
11601167
let
1161-
Approval (Username approvedBy) _source approvalType _position retriedBy priority = fromJust $ Pr.approval pr
1168+
Approval (Username approvedBy) source approvalType _position retriedBy priority = fromJust $ Pr.approval pr
11621169

11631170
approvalCommand = Pr.displayMergeCommand (Approve approvalType)
11641171
retriedByMsg = case retriedBy of
11651172
Just user -> format " (retried by @{})" [user]
11661173
Nothing -> mempty
1167-
queuePositionMsg = case Pr.getQueuePosition prId state of
1174+
queuePosition = Pr.getQueuePosition prId state
1175+
queuePositionMsg = case queuePosition of
11681176
0 -> "rebasing now"
11691177
1 -> "waiting for rebase behind one pull request"
11701178
n -> format "waiting for rebase behind {} pull requests" [n]
11711179
priorityMsg = case priority of
11721180
Normal -> mempty
11731181
High -> " with high priority"
1174-
in format "Pull request approved for {}{} by @{}{}, {}." [approvalCommand, priorityMsg, approvedBy, retriedByMsg, queuePositionMsg]
1182+
in
1183+
case (queuePosition, source) of
1184+
(0, Just reactable) -> ReactionFeedback reactable GithubApi.PlusOne
1185+
_ ->
1186+
CommentFeedback $
1187+
format
1188+
"Pull request approved for {}{} by @{}{}, {}."
1189+
[approvalCommand, priorityMsg, approvedBy, retriedByMsg, queuePositionMsg]
11751190
PrStatusOutdated ->
11761191
let BaseBranch baseBranchName = Pr.baseBranch pr
1177-
in format "Push to {} detected, rebasing again." [baseBranchName]
1178-
PrStatusBuildPending -> let Sha sha = fromJust $ Pr.integrationSha pr
1179-
train = takeWhile (/= prId) $ Pr.unfailedIntegratedPullRequests state
1180-
len = length train
1181-
prs = if len == 1 then "PR" else "PRs"
1182-
in case train of
1183-
[] -> Text.concat ["Rebased as ", sha, ", waiting for CI …"]
1184-
(_:_) -> Text.concat [ "Speculatively rebased as ", sha
1185-
, " behind ", Text.pack $ show len
1186-
, " other ", prs
1187-
, ", waiting for CI …"
1188-
]
1189-
PrStatusBuildStarted url -> Text.concat ["[CI job :yellow_circle:](", url, ") started."]
1190-
PrStatusAwaitingPromotion -> "The PR is waiting to be pushed to the target branch"
1191-
PrStatusIntegrated -> "The build succeeded."
1192+
in CommentFeedback $ format "Push to {} detected, rebasing again." [baseBranchName]
1193+
PrStatusBuildPending ->
1194+
let Sha sha = fromJust $ Pr.integrationSha pr
1195+
train = takeWhile (/= prId) $ Pr.unfailedIntegratedPullRequests state
1196+
len = length train
1197+
prs = if len == 1 then "PR" else "PRs"
1198+
in CommentFeedback $ case train of
1199+
[] -> Text.concat ["Rebased as ", sha, ", waiting for CI …"]
1200+
(_ : _) ->
1201+
Text.concat
1202+
[ "Speculatively rebased as "
1203+
, sha
1204+
, " behind "
1205+
, Text.pack $ show len
1206+
, " other "
1207+
, prs
1208+
, ", waiting for CI …"
1209+
]
1210+
PrStatusBuildStarted url -> CommentFeedback $ Text.concat ["[CI job :yellow_circle:](", url, ") started."]
1211+
PrStatusAwaitingPromotion -> CommentFeedback $ "The PR is waiting to be pushed to the target branch"
1212+
PrStatusIntegrated -> CommentFeedback $ "The build succeeded."
11921213
PrStatusIncorrectBaseBranch ->
11931214
let BaseBranch baseBranchName = Pr.baseBranch pr
1194-
in format "Merge rejected: the base branch of this pull request must be set to {}. It is currently set to {}."
1195-
[projectBaseBranchName, baseBranchName]
1196-
PrStatusWrongFixups -> "Pull request cannot be integrated as it contains fixup commits that do not belong to any other commits."
1197-
PrStatusEmptyRebase -> "Empty rebase. \
1198-
\ Have the changes already been merged into the target branch? \
1199-
\ Aborting."
1215+
in CommentFeedback $
1216+
format
1217+
"Merge rejected: the base branch of this pull request must be set to {}. It is currently set to {}."
1218+
[projectBaseBranchName, baseBranchName]
1219+
PrStatusWrongFixups ->
1220+
CommentFeedback "Pull request cannot be integrated as it contains fixup commits that do not belong to any other commits."
1221+
PrStatusEmptyRebase ->
1222+
CommentFeedback
1223+
"Empty rebase. \
1224+
\ Have the changes already been merged into the target branch? \
1225+
\ Aborting."
12001226
PrStatusFailedConflict ->
12011227
let
12021228
BaseBranch targetBranchName = Pr.baseBranch pr
12031229
Branch prBranchName = Pr.branch pr
1204-
in Text.concat
1205-
[ "Failed to rebase, please rebase manually using\n\n"
1206-
, " git fetch && git rebase --interactive --autosquash origin/"
1207-
, targetBranchName
1208-
, " "
1209-
, prBranchName
1210-
]
1230+
in
1231+
CommentFeedback $
1232+
Text.concat
1233+
[ "Failed to rebase, please rebase manually using\n\n"
1234+
, " git fetch && git rebase --interactive --autosquash origin/"
1235+
, targetBranchName
1236+
, " "
1237+
, prBranchName
1238+
]
12111239
-- The following is not actually shown to the user
12121240
-- as it is never set with needsFeedback=True,
12131241
-- but here in case we decide to show it.
1214-
PrStatusSpeculativeConflict -> "Failed to speculatively rebase. \
1215-
\ I will retry rebasing automatically when the queue clears."
1216-
PrStatusFailedBuild url -> case Pr.unfailedIntegratedPullRequestsBefore pr state of
1217-
-- On Fridays the retry command is also `retry on friday`. We currently
1218-
-- don't have that information here. Is that worth including?
1219-
[] -> format "The {}.\n\n\
1220-
\If this is the result of a flaky test, \
1221-
\then tag me again with the `retry` command. \
1222-
\Otherwise, push a new commit and tag me again."
1223-
[markdownLink "build failed :x:" url]
1224-
trainBefore -> format "Speculative {}. \
1225-
\ I will automatically retry after getting build results for {}."
1226-
[ markdownLink "build failed :x:" url
1227-
, prettyPullRequestIds trainBefore ]
1242+
PrStatusSpeculativeConflict ->
1243+
CommentFeedback
1244+
"Failed to speculatively rebase. \
1245+
\ I will retry rebasing automatically when the queue clears."
1246+
PrStatusFailedBuild url ->
1247+
CommentFeedback $
1248+
case Pr.unfailedIntegratedPullRequestsBefore pr state of
1249+
-- On Fridays the retry command is also `retry on friday`. We currently
1250+
-- don't have that information here. Is that worth including?
1251+
[] ->
1252+
format
1253+
"The {}.\n\n\
1254+
\If this is the result of a flaky test, \
1255+
\then tag me again with the `retry` command. \
1256+
\Otherwise, push a new commit and tag me again."
1257+
[markdownLink "build failed :x:" url]
1258+
trainBefore ->
1259+
format
1260+
"Speculative {}. \
1261+
\ I will automatically retry after getting build results for {}."
1262+
[ markdownLink "build failed :x:" url
1263+
, prettyPullRequestIds trainBefore
1264+
]
12281265

12291266
-- Leave a comment with the feedback from 'describeStatus' and set the
12301267
-- 'needsFeedback' flag to 'False'.
@@ -1235,11 +1272,15 @@ leaveFeedback
12351272
-> Eff es ProjectState
12361273
leaveFeedback (prId, pr) state = do
12371274
projectBaseBranch <- getBaseBranch
1238-
let message = describeStatus projectBaseBranch prId pr state
1239-
-- Hoff shouldn't reply to any of its own feedback messages. This can happen
1240-
-- if external automation causes the bot to issue a merge command to itself.
1241-
-- In that case the bot may tag itself when the merge gets approved.
1242-
() <- leaveComment prId $ hoffIgnoreComment <> message
1275+
case feedbackOnStatus projectBaseBranch prId pr state of
1276+
CommentFeedback message ->
1277+
-- Hoff shouldn't reply to any of its own feedback messages. This can happen
1278+
-- if external automation causes the bot to issue a merge command to itself.
1279+
-- In that case the bot may tag itself when the merge gets approved.
1280+
leaveComment prId $ hoffIgnoreComment <> message
1281+
ReactionFeedback reactable reaction ->
1282+
addReaction reactable reaction
1283+
12431284
pure $ Pr.setNeedsFeedback prId False state
12441285

12451286
-- Run 'leaveFeedback' on all pull requests that need feedback.

tests/Spec.hs

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,8 @@ main = hspec $ do
767767

768768
it "handles merge command in body of pull request" $ do
769769
let
770-
event = PullRequestOpened (PullRequestId 1) (Branch "p") masterBranch (Sha "e0f") "title" "deckard" (Just "@bot merge")
770+
prId = PullRequestId 1
771+
event = PullRequestOpened prId (Branch "p") masterBranch (Sha "e0f") "title" "deckard" (Just "@bot merge")
771772
-- For this test, we assume all integrations and pushes succeed.
772773
results = defaultResults
773774
{ resultIntegrate = [Right (Sha "b71")] }
@@ -776,10 +777,10 @@ main = hspec $ do
776777

777778
actions `shouldBe`
778779
[ AIsReviewer "deckard"
779-
, ALeaveComment (PullRequestId 1) "<!-- Hoff: ignore -->\nPull request approved for merge by @deckard, rebasing now."
780+
, AAddReaction (OnPullRequest prId) GithubApi.PlusOne
780781
, ATryIntegrate "Merge #1: title\n\nApproved-by: deckard\nPriority: Normal\nAuto-deploy: false\n"
781-
(PullRequestId 1, Branch "refs/pull/1/head", Sha "e0f") [] False
782-
, ALeaveComment (PullRequestId 1) "<!-- Hoff: ignore -->\nRebased as b71, waiting for CI …"
782+
(prId, Branch "refs/pull/1/head", Sha "e0f") [] False
783+
, ALeaveComment prId "<!-- Hoff: ignore -->\nRebased as b71, waiting for CI …"
783784
]
784785
classifiedPullRequestIds state' `shouldBe` ClassifiedPullRequestIds
785786
{ building = [PullRequestId 1]
@@ -790,10 +791,11 @@ main = hspec $ do
790791

791792
it "does not handle merge command in body of reopened pull request" $ do
792793
let
794+
prId = PullRequestId 1
793795
events =
794-
[ PullRequestOpened (PullRequestId 1) (Branch "p") masterBranch (Sha "e0f") "title" "deckard" (Just "@bot merge")
795-
, PullRequestClosed (PullRequestId 1)
796-
, PullRequestOpened (PullRequestId 1) (Branch "p") masterBranch (Sha "e0f") "title" "deckard" Nothing
796+
[ PullRequestOpened prId (Branch "p") masterBranch (Sha "e0f") "title" "deckard" (Just "@bot merge")
797+
, PullRequestClosed prId
798+
, PullRequestOpened prId (Branch "p") masterBranch (Sha "e0f") "title" "deckard" Nothing
797799
]
798800
-- For this test, we assume all integrations and pushes succeed.
799801
results = defaultResults
@@ -803,11 +805,11 @@ main = hspec $ do
803805

804806
actions `shouldBe`
805807
[ AIsReviewer "deckard"
806-
, ALeaveComment (PullRequestId 1) "<!-- Hoff: ignore -->\nPull request approved for merge by @deckard, rebasing now."
808+
, AAddReaction (OnPullRequest prId) GithubApi.PlusOne
807809
, ATryIntegrate "Merge #1: title\n\nApproved-by: deckard\nPriority: Normal\nAuto-deploy: false\n"
808-
(PullRequestId 1, Branch "refs/pull/1/head", Sha "e0f") [] False
809-
, ALeaveComment (PullRequestId 1) "<!-- Hoff: ignore -->\nRebased as b71, waiting for CI …"
810-
, ALeaveComment (PullRequestId 1) "Abandoning this pull request because it was closed."
810+
(prId, Branch "refs/pull/1/head", Sha "e0f") [] False
811+
, ALeaveComment prId "<!-- Hoff: ignore -->\nRebased as b71, waiting for CI …"
812+
, ALeaveComment prId "Abandoning this pull request because it was closed."
811813
, ACleanupTestBranch (PullRequestId 1)
812814
]
813815
classifiedPullRequestIds state' `shouldBe` ClassifiedPullRequestIds
@@ -934,6 +936,41 @@ main = hspec $ do
934936
fromJust (Project.lookupPullRequest prId state') `shouldSatisfy`
935937
(\pr -> (Project.approval pr >>= Project.approvalSource) == Just (OnPullRequest prId))
936938

939+
it "adds a reaction to a 'merge' command in the common case" $ do
940+
let
941+
prId = PullRequestId 1
942+
commentId = CommentId 42
943+
state = singlePullRequestState prId (Branch "p") masterBranch (Sha "abc1234") "tyrell"
944+
945+
event = CommentAdded prId "deckard" (Just commentId) "@bot merge"
946+
947+
results = defaultResults { resultIntegrate = [Right (Sha "def2345")] }
948+
(_state', actions) = runActionCustom results $ handleEventTest event state
949+
950+
actions `shouldContain` [AAddReaction (OnIssueComment commentId) GithubApi.PlusOne]
951+
952+
it "adds a reaction to a 'retry' command in the common case" $ do
953+
let
954+
prId = PullRequestId 1
955+
mergeCommentId = CommentId 42
956+
retryCommentId = CommentId 72
957+
state = singlePullRequestState prId (Branch "p") masterBranch (Sha "1b1") "tyrell"
958+
959+
events =
960+
[ CommentAdded prId "deckard" (Just mergeCommentId) "@bot merge"
961+
, BuildStatusChanged (Sha "1b3") "default" (Project.BuildFailed (Just "url"))
962+
, CommentAdded prId "deckard" (Just retryCommentId) "@bot retry"
963+
]
964+
965+
results = defaultResults { resultIntegrate = [Right (Sha "1b3"), Right (Sha "00f")] }
966+
(_state', actions) = runActionCustom results $ handleEventsTest events state
967+
968+
actions `shouldContain` [AAddReaction (OnIssueComment retryCommentId) GithubApi.PlusOne]
969+
970+
it "falls back to an ordinary comment if there are other PRs ahead in the queue" $ do
971+
-- WIP(daan)
972+
pending
973+
937974
it "recognizes 'merge and deploy' commands as the proper ApprovedFor value" $ do
938975
let
939976
prId = PullRequestId 1

0 commit comments

Comments
 (0)