damccorm commented on a change in pull request #16950: URL: https://github.com/apache/beam/pull/16950#discussion_r815472970
########## File path: scripts/ci/pr-bot/processPrUpdate.ts ########## @@ -0,0 +1,198 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const github = require("@actions/github"); +const commentStrings = require("./shared/commentStrings"); +const { processCommand } = require("./shared/userCommand"); +const { addPrComment, nextActionReviewers } = require("./shared/githubUtils"); +const { PersistentState } = require("./shared/persistentState"); +const { ReviewerConfig } = require("./shared/reviewerConfig"); +const { PATH_TO_CONFIG_FILE } = require("./shared/constants"); + +async function areReviewersAssigned( + pullNumber: number, + stateClient: typeof PersistentState +): Promise<boolean> { + const prState = await stateClient.getPrState(pullNumber); + return Object.values(prState.reviewersAssignedForLabels).length > 0; +} + +async function processPrComment( + payload, + stateClient: typeof PersistentState, + reviewerConfig: typeof ReviewerConfig +) { + const commentContents = payload.comment.body; + const commentAuthor = payload.sender.login; + console.log(commentContents); + if ( + await processCommand( + payload, + commentAuthor, + commentContents, + stateClient, + reviewerConfig + ) + ) { + // If we've processed a command, don't worry about trying to change the attention set. + // This is not a meaningful push or comment from the author. + console.log("Processed command"); + return; + } + + // If comment was from the author, we should shift attention back to the reviewers. + console.log( + "No command to be processed, checking if we should shift attention to reviewers" + ); + const pullAuthor = + payload.issue?.user?.login || payload.pull_request?.user?.login; + if (pullAuthor == commentAuthor) { + await setNextActionReviewers(payload, stateClient); + } else { + console.log( + `Comment was from ${commentAuthor}, not author: ${pullAuthor}. No action to take.` + ); + } +} + +// On approval from a reviewer we have assigned, assign committer if one not already assigned +async function processPrReview( + payload, + stateClient: typeof PersistentState, + reviewerConfig: typeof ReviewerConfig +) { + if (payload.review.state != "approved") { + return; + } + + const pullNumber = payload.issue?.number || payload.pull_request?.number; + if (!(await areReviewersAssigned(pullNumber, stateClient))) { + return; + } + + let prState = await stateClient.getPrState(pullNumber); + // TODO(damccorm) - also check if the author is a committer, if they are don't auto-assign a committer + if (await prState.isAnyAssignedReviewerCommitter()) { + return; + } + + let labelOfReviewer = prState.getLabelForReviewer(payload.sender.login); + if (labelOfReviewer) { + let reviewersState = await stateClient.getReviewersForLabelState( + labelOfReviewer + ); + let availableReviewers = + reviewerConfig.getReviewersForLabel(labelOfReviewer); + let chosenCommitter = await reviewersState.assignNextCommitter( + availableReviewers + ); + prState.reviewersAssignedForLabels[labelOfReviewer] = chosenCommitter; + + // Set next action to committer + await addPrComment( + pullNumber, + commentStrings.assignCommitter(chosenCommitter) + ); + const existingLabels = + payload.issue?.labels || payload.pull_request?.labels; + await nextActionReviewers(pullNumber, existingLabels); + prState.nextAction = "Reviewers"; + + // Persist state + await stateClient.writePrState(pullNumber, prState); + await stateClient.writeReviewersForLabelState( + labelOfReviewer, + reviewersState + ); + } +} + +// On pr push or author comment, we should put the attention set back on the reviewers +async function setNextActionReviewers( + payload, + stateClient: typeof PersistentState +) { + const pullNumber = payload.issue?.number || payload.pull_request?.number; + if (!(await areReviewersAssigned(pullNumber, stateClient))) { + console.log("No reviewers assigned, dont need to manipulate attention set"); + return; + } + const existingLabels = payload.issue?.labels || payload.pull_request?.labels; + await nextActionReviewers(pullNumber, existingLabels); + let prState = await stateClient.getPrState(pullNumber); + prState.nextAction = "Reviewers"; + await stateClient.writePrState(pullNumber, prState); +} + +async function processPrUpdate() { + const reviewerConfig = new ReviewerConfig(PATH_TO_CONFIG_FILE); + const context = github.context; + console.log("Event context:"); + console.log(context); + const payload = context.payload; + + // TODO(damccorm) - remove this when we roll out to more than go + const existingLabels = payload.issue?.labels || payload.pull_request?.labels; + if (!existingLabels.find((label) => label.name.toLowerCase() == "go")) { Review comment: Since its only used twice (both in temporary code that won't last more than a month) I'm not inclined to pull go out into a shared constant ########## File path: scripts/ci/pr-bot/processPrUpdate.ts ########## @@ -0,0 +1,198 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const github = require("@actions/github"); +const commentStrings = require("./shared/commentStrings"); +const { processCommand } = require("./shared/userCommand"); +const { addPrComment, nextActionReviewers } = require("./shared/githubUtils"); +const { PersistentState } = require("./shared/persistentState"); +const { ReviewerConfig } = require("./shared/reviewerConfig"); +const { PATH_TO_CONFIG_FILE } = require("./shared/constants"); + +async function areReviewersAssigned( + pullNumber: number, + stateClient: typeof PersistentState +): Promise<boolean> { + const prState = await stateClient.getPrState(pullNumber); + return Object.values(prState.reviewersAssignedForLabels).length > 0; +} + +async function processPrComment( + payload, + stateClient: typeof PersistentState, + reviewerConfig: typeof ReviewerConfig +) { + const commentContents = payload.comment.body; + const commentAuthor = payload.sender.login; + console.log(commentContents); + if ( + await processCommand( + payload, + commentAuthor, + commentContents, + stateClient, + reviewerConfig + ) + ) { + // If we've processed a command, don't worry about trying to change the attention set. + // This is not a meaningful push or comment from the author. + console.log("Processed command"); + return; + } + + // If comment was from the author, we should shift attention back to the reviewers. + console.log( + "No command to be processed, checking if we should shift attention to reviewers" + ); + const pullAuthor = + payload.issue?.user?.login || payload.pull_request?.user?.login; + if (pullAuthor == commentAuthor) { + await setNextActionReviewers(payload, stateClient); + } else { + console.log( + `Comment was from ${commentAuthor}, not author: ${pullAuthor}. No action to take.` + ); + } +} + +// On approval from a reviewer we have assigned, assign committer if one not already assigned +async function processPrReview( + payload, + stateClient: typeof PersistentState, + reviewerConfig: typeof ReviewerConfig +) { + if (payload.review.state != "approved") { + return; + } + + const pullNumber = payload.issue?.number || payload.pull_request?.number; + if (!(await areReviewersAssigned(pullNumber, stateClient))) { + return; + } + + let prState = await stateClient.getPrState(pullNumber); + // TODO(damccorm) - also check if the author is a committer, if they are don't auto-assign a committer + if (await prState.isAnyAssignedReviewerCommitter()) { + return; + } + + let labelOfReviewer = prState.getLabelForReviewer(payload.sender.login); + if (labelOfReviewer) { + let reviewersState = await stateClient.getReviewersForLabelState( + labelOfReviewer + ); + let availableReviewers = + reviewerConfig.getReviewersForLabel(labelOfReviewer); + let chosenCommitter = await reviewersState.assignNextCommitter( + availableReviewers + ); + prState.reviewersAssignedForLabels[labelOfReviewer] = chosenCommitter; + + // Set next action to committer + await addPrComment( + pullNumber, + commentStrings.assignCommitter(chosenCommitter) + ); + const existingLabels = + payload.issue?.labels || payload.pull_request?.labels; + await nextActionReviewers(pullNumber, existingLabels); + prState.nextAction = "Reviewers"; + + // Persist state + await stateClient.writePrState(pullNumber, prState); + await stateClient.writeReviewersForLabelState( + labelOfReviewer, + reviewersState + ); + } +} + +// On pr push or author comment, we should put the attention set back on the reviewers +async function setNextActionReviewers( + payload, + stateClient: typeof PersistentState +) { + const pullNumber = payload.issue?.number || payload.pull_request?.number; + if (!(await areReviewersAssigned(pullNumber, stateClient))) { + console.log("No reviewers assigned, dont need to manipulate attention set"); + return; + } + const existingLabels = payload.issue?.labels || payload.pull_request?.labels; + await nextActionReviewers(pullNumber, existingLabels); + let prState = await stateClient.getPrState(pullNumber); + prState.nextAction = "Reviewers"; + await stateClient.writePrState(pullNumber, prState); +} + +async function processPrUpdate() { + const reviewerConfig = new ReviewerConfig(PATH_TO_CONFIG_FILE); + const context = github.context; + console.log("Event context:"); + console.log(context); + const payload = context.payload; + + // TODO(damccorm) - remove this when we roll out to more than go + const existingLabels = payload.issue?.labels || payload.pull_request?.labels; + if (!existingLabels.find((label) => label.name.toLowerCase() == "go")) { + console.log("Does not contain the go label - skipping"); + return; + } + + if (!payload.issue?.pull_request && !payload.pull_request) { + console.log("Issue, not pull request - returning"); + return; + } + const pullNumber = payload.issue?.number || payload.pull_request?.number; + + let stateClient = new PersistentState(); + let prState = await stateClient.getPrState(pullNumber); + if (prState.stopReviewerNotifications) { + console.log("Notifications have been paused for this pull - skipping"); + return; + } + + switch (github.context.eventName) { + case "pull_request_review_comment": + case "issue_comment": + console.log("Processing comment event"); + if (payload.action != "created") { + console.log("Comment wasnt just created, skipping"); + return; + } + await processPrComment(payload, stateClient, reviewerConfig); + break; + case "pull_request_review": + console.log("Processing PR review event"); + await processPrReview(payload, stateClient, reviewerConfig); + break; + case "pull_request_target": + if (payload.action == "synchronize") { + console.log("Processing synchronize action"); + await setNextActionReviewers(payload, stateClient); + } + // TODO(damccorm) - it would be good to eventually handle the following events here, even though they're not part of the normal workflow + // review requested, assigned, label added, label removed + break; + default: + console.log("Not a PR comment, push, or review, doing nothing"); + } +} + +processPrUpdate(); + +export {}; Review comment: Addressed in a similar comment above ########## File path: scripts/ci/pr-bot/processNewPrs.ts ########## @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const github = require("./shared/githubUtils"); +const { getChecksStatus } = require("./shared/checks"); +const commentStrings = require("./shared/commentStrings"); +const { ReviewerConfig } = require("./shared/reviewerConfig"); +const { PersistentState } = require("./shared/persistentState"); +const { Pr } = require("./shared/pr"); +const { REPO_OWNER, REPO, PATH_TO_CONFIG_FILE } = require("./shared/constants"); +import { CheckStatus } from "./shared/checks"; + +// Returns true if the pr needs to be processed or false otherwise. +// We don't need to process PRs that: +// 1) Have WIP in their name +// 2) Are less than 20 minutes old +// 3) Are draft prs +// 4) Are closed +// 5) Have already been processed +// 6) Have notifications stopped +// 7) The pr doesn't contain the go label (temporary). TODO(damccorm) - remove this when we're ready to roll this out to everyone. +// unless we're supposed to remind the user after tests pass +// (in which case that's all we need to do). +function needsProcessed(pull, prState: typeof Pr): boolean { + if (!pull.labels.find((label) => label.name.toLowerCase() == "go")) { + console.log( + `Skipping PR ${pull.number} because it doesn't contain the go label` + ); + return false; + } + if (prState.remindAfterTestsPass && prState.remindAfterTestsPass.length > 0) { + return true; + } + if (pull.title.toLowerCase().indexOf("wip") >= 0) { + console.log(`Skipping PR ${pull.number} because it is a WIP`); + return false; + } + let timeCutoff = new Date(new Date().getTime() - 20 * 60000); + if (new Date(pull.created_at) > timeCutoff) { + console.log( + `Skipping PR ${pull.number} because it was created less than 20 minutes ago` + ); + return false; + } + if (pull.state.toLowerCase() != "open") { + console.log(`Skipping PR ${pull.number} because it is closed`); + return false; + } + if (pull.draft) { + console.log(`Skipping PR ${pull.number} because it is a draft`); + return false; + } + if (Object.keys(prState.reviewersAssignedForLabels).length > 0) { + console.log( + `Skipping PR ${pull.number} because it already has been assigned` + ); + return false; + } + if (prState.stopReviewerNotifications) { + console.log( + `Skipping PR ${pull.number} because reviewer notifications have been stopped` + ); + return false; + } + + return true; +} + +// If the checks passed in via checkstate have completed, notifies the users who have configured notifications. +async function remindIfChecksCompleted( + pull, + stateClient: typeof PersistentState, + checkState: CheckStatus, + prState: typeof Pr +) { + console.log( + `Notifying reviewers if checks for PR ${pull.number} have completed, then returning` + ); + if (checkState.completed) { + if (checkState.succeeded) { + await github.addPrComment( + pull.number, + commentStrings.allChecksPassed(prState.remindAfterTestsPass) + ); + } else { + await github.addPrComment( + pull.number, + commentStrings.someChecksFailing(prState.remindAfterTestsPass) + ); + } + prState.remindAfterTestsPass = []; + await stateClient.writePrState(pull.number, prState); + } +} + +// If we haven't already, let the author know checks are failing. +async function notifyChecksFailed( + pull, + stateClient: typeof PersistentState, + prState: typeof Pr +) { + console.log( + `Checks are failing for PR ${pull.number}. Commenting if we haven't already and skipping.` + ); + if (!prState.commentedAboutFailingChecks) { + await github.addPrComment( + pull.number, + commentStrings.failingChecksCantAssign() + ); + } + prState.commentedAboutFailingChecks = true; + await stateClient.writePrState(pull.number, prState); +} + +// Performs all the business logic of processing a new pull request, including: +// 1) Checking if it needs processed +// 2) Reminding reviewers if checks have completed (if they've subscribed to that) +// 3) Picking/assigning reviewers +// 4) Adding "Next Action: Reviewers label" +// 5) Storing the state of the pull request/reviewers in a dedicated branch. +async function processPull( + pull, + reviewerConfig: typeof ReviewerConfig, + stateClient: typeof PersistentState +) { + let prState = await stateClient.getPrState(pull.number); + if (!needsProcessed(pull, prState)) { + return; + } + + let checkState = await getChecksStatus(REPO_OWNER, REPO, pull.head.sha); + + if (prState.remindAfterTestsPass && prState.remindAfterTestsPass.length > 0) { + return await remindIfChecksCompleted( + pull, + stateClient, + checkState, + prState + ); + } + + if (!checkState.succeeded) { + return await notifyChecksFailed(pull, stateClient, prState); + } + prState.commentedAboutFailingChecks = false; + + // Pick reviewers to assign. Store them in reviewerStateToUpdate and update the prState object with those reviewers (and their associated labels) + let reviewerStateToUpdate = {}; + const reviewersForLabels: { [key: string]: string[] } = + reviewerConfig.getReviewersForLabels(pull.labels, [pull.user.login]); + var labels = Object.keys(reviewersForLabels); + if (!labels || labels.length === 0) { + return; + } + for (let i = 0; i < labels.length; i++) { + let label = labels[i]; + let availableReviewers = reviewersForLabels[label]; + let reviewersState = await stateClient.getReviewersForLabelState(label); + let chosenReviewer = reviewersState.assignNextReviewer(availableReviewers); + reviewerStateToUpdate[label] = reviewersState; + prState.reviewersAssignedForLabels[label] = chosenReviewer; + } + + console.log(`Assigning reviewers for PR ${pull.number}`); + await github.addPrComment( + pull.number, + commentStrings.assignReviewer(prState.reviewersAssignedForLabels) + ); + + github.nextActionReviewers(pull.number, pull.labels); + prState.nextAction = "Reviewers"; + + await stateClient.writePrState(pull.number, prState); + let labelsToUpdate = Object.keys(reviewerStateToUpdate); + for (let i = 0; i < labelsToUpdate.length; i++) { + let label = labelsToUpdate[i]; + await stateClient.writeReviewersForLabelState( + label, + reviewerStateToUpdate[label] + ); + } +} + +async function processNewPrs() { + const githubClient = github.getGitHubClient(); + let reviewerConfig = new ReviewerConfig(PATH_TO_CONFIG_FILE); + let stateClient = new PersistentState(); + + let openPulls = await githubClient.paginate( + "GET /repos/{owner}/{repo}/pulls", + { + owner: REPO_OWNER, + repo: REPO, + } + ); + + for (let i = 0; i < openPulls.length; i++) { + let pull = openPulls[i]; + await processPull(pull, reviewerConfig, stateClient); + } +} + +processNewPrs(); + +export {}; Review comment: This is the standard pattern used for all first party GitHub actions, since this is in that realm I'd prefer to adopt their approach. > Hiding execution under an import could make debugging more difficult later. I'm not sure I understand the concern here - what is more difficult? If anything, it requires one fewer layer of indirection ########## File path: scripts/ci/pr-bot/processNewPrs.ts ########## @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const github = require("./shared/githubUtils"); +const { getChecksStatus } = require("./shared/checks"); +const commentStrings = require("./shared/commentStrings"); +const { ReviewerConfig } = require("./shared/reviewerConfig"); +const { PersistentState } = require("./shared/persistentState"); +const { Pr } = require("./shared/pr"); +const { REPO_OWNER, REPO, PATH_TO_CONFIG_FILE } = require("./shared/constants"); +import { CheckStatus } from "./shared/checks"; + +// Returns true if the pr needs to be processed or false otherwise. +// We don't need to process PRs that: +// 1) Have WIP in their name +// 2) Are less than 20 minutes old +// 3) Are draft prs +// 4) Are closed +// 5) Have already been processed +// 6) Have notifications stopped +// 7) The pr doesn't contain the go label (temporary). TODO(damccorm) - remove this when we're ready to roll this out to everyone. +// unless we're supposed to remind the user after tests pass +// (in which case that's all we need to do). +function needsProcessed(pull, prState: typeof Pr): boolean { + if (!pull.labels.find((label) => label.name.toLowerCase() == "go")) { + console.log( + `Skipping PR ${pull.number} because it doesn't contain the go label` + ); + return false; + } + if (prState.remindAfterTestsPass && prState.remindAfterTestsPass.length > 0) { Review comment: That has the potential to coallese to `null > 0`, which I don't think is actually what we want. ########## File path: scripts/ci/pr-bot/processNewPrs.ts ########## @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const github = require("./shared/githubUtils"); +const { getChecksStatus } = require("./shared/checks"); +const commentStrings = require("./shared/commentStrings"); +const { ReviewerConfig } = require("./shared/reviewerConfig"); +const { PersistentState } = require("./shared/persistentState"); +const { Pr } = require("./shared/pr"); +const { REPO_OWNER, REPO, PATH_TO_CONFIG_FILE } = require("./shared/constants"); +import { CheckStatus } from "./shared/checks"; + +// Returns true if the pr needs to be processed or false otherwise. +// We don't need to process PRs that: +// 1) Have WIP in their name +// 2) Are less than 20 minutes old +// 3) Are draft prs +// 4) Are closed +// 5) Have already been processed +// 6) Have notifications stopped +// 7) The pr doesn't contain the go label (temporary). TODO(damccorm) - remove this when we're ready to roll this out to everyone. +// unless we're supposed to remind the user after tests pass +// (in which case that's all we need to do). +function needsProcessed(pull, prState: typeof Pr): boolean { + if (!pull.labels.find((label) => label.name.toLowerCase() == "go")) { Review comment: > Use triple equals for strict string comparison. Updated! > Single quotation marks are more standard than double. That being said, be consistent with whatever is more common in the repo. Prettier actually autoformats to double quotes (I personally prefer single 😄) ########## File path: scripts/ci/pr-bot/processNewPrs.ts ########## @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const github = require("./shared/githubUtils"); +const { getChecksStatus } = require("./shared/checks"); +const commentStrings = require("./shared/commentStrings"); +const { ReviewerConfig } = require("./shared/reviewerConfig"); +const { PersistentState } = require("./shared/persistentState"); +const { Pr } = require("./shared/pr"); +const { REPO_OWNER, REPO, PATH_TO_CONFIG_FILE } = require("./shared/constants"); +import { CheckStatus } from "./shared/checks"; + +// Returns true if the pr needs to be processed or false otherwise. +// We don't need to process PRs that: +// 1) Have WIP in their name +// 2) Are less than 20 minutes old +// 3) Are draft prs +// 4) Are closed +// 5) Have already been processed +// 6) Have notifications stopped +// 7) The pr doesn't contain the go label (temporary). TODO(damccorm) - remove this when we're ready to roll this out to everyone. +// unless we're supposed to remind the user after tests pass +// (in which case that's all we need to do). +function needsProcessed(pull, prState: typeof Pr): boolean { Review comment: Sure, updated (here and elsewhere) ########## File path: scripts/ci/pr-bot/.gitignore ########## @@ -0,0 +1,41 @@ +lib-cov +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.gz +*.swp + +pids +logs +results +tmp + +# Build +public/css/main.css Review comment: You're right, we don't need that - removed -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
