Skip to content

Commit

Permalink
ran yarn fix
Browse files Browse the repository at this point in the history
  • Loading branch information
Bullrich committed Sep 26, 2023
1 parent 1892082 commit da4b77e
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 84 deletions.
132 changes: 79 additions & 53 deletions src/bot.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Issue, IssueComment } from "@octokit/webhooks-types";

import { CommentsApi } from "./github/comments";
import { Merger } from "./github/merger";
import { ActionLogger } from "./github/types";
import { CommentsApi } from "./github/comments";

const BOT_COMMAND = "/bot";

Expand All @@ -18,64 +19,89 @@ For more information see the [documentation](https://github.com/paritytech/auto-
`;

export class Bot {
constructor(private readonly comment: IssueComment, private readonly pr: Issue, private readonly logger: ActionLogger, private readonly commentsApi: CommentsApi) { }
constructor(
private readonly comment: IssueComment,
private readonly pr: Issue,
private readonly logger: ActionLogger,
private readonly commentsApi: CommentsApi,
) {}

async canTriggerBot():Promise<boolean> {
this.logger.debug("Evaluating if user can trigger the bot");
const author = this.pr.user.id;
if (this.comment.user.id === author) {
this.logger.debug("Author of comment is also author of PR")
return true;
}
this.logger.debug("Author of comment is not the author of the PR");

return await this.commentsApi.userBelongsToOrg(this.comment.user.login);
/** Verifies if the author is the also the author of the PR or a member of the org */
async canTriggerBot(): Promise<boolean> {
this.logger.debug("Evaluating if user can trigger the bot");
const author = this.pr.user.id;
if (this.comment.user.id === author) {
this.logger.debug("Author of comment is also author of PR");
return true;
}
this.logger.debug("Author of comment is not the author of the PR");

return await this.commentsApi.userBelongsToOrg(this.comment.user.login);
}

async run(merger: Merger): Promise<void> {
this.logger.info("Running action on comment: " + this.comment.html_url);
if (!this.comment.body.startsWith(BOT_COMMAND)) {
this.logger.info(`Ignoring comment ${this.comment.html_url} as it does not start with '${BOT_COMMAND}'`);
return;
}
async run(merger: Merger): Promise<void> {
this.logger.info("Running action on comment: " + this.comment.html_url);
if (!this.comment.body.startsWith(BOT_COMMAND)) {
this.logger.info(
`Ignoring comment ${this.comment.html_url} as it does not start with '${BOT_COMMAND}'`,
);
return;
}

if (!await this.canTriggerBot()) {
const { login } = this.comment.user;
const org = this.commentsApi.pullData.owner;
this.logger.warn("User is not allowed to trigger the bot. " + `He is not the author of the PR and does not *publicly* belong to the org: https://github.com/orgs/${org}/people`);
await this.commentsApi.reactToComment(this.comment.id, "-1");
await this.commentsApi.comment("## Auto-Merge-Bot\n" + `User @${login} is not the author of the PR and does not [*publicly* belong to the org \`${org}\`](https://github.com/orgs/${org}/people).\n\n` +
"Only author or *public* org members can trigger the bot.");
return;
}
this.logger.debug("User can trigger bot");
if (this.pr.state === "closed") {
this.logger.info("Ignoring PR as it is closed");
return;
}

const [_, command] = this.comment.body.split(" ");
try {
switch (command as Command) {
case "merge":
await this.commentsApi.reactToComment(this.comment.id, "+1");
await merger.enableAutoMerge();
await this.commentsApi.comment("Enabled `auto-merge` in Pull Request");
break;
case "cancel":
await this.commentsApi.reactToComment(this.comment.id, "+1");
await merger.disableAutoMerge();
await this.commentsApi.comment("Disabled `auto-merge` in Pull Request");
break;
case "help":
await this.commentsApi.comment('## Auto-Merge-Bot\n' + botCommands);
break;
default: {
await this.commentsApi.reactToComment(this.comment.id, "confused");
await this.commentsApi.comment('## Auto-Merge-Bot\n' + `Command \`${command}\` not recognized.\n\n` + botCommands);
}
}
} catch (e) {
this.logger.error(e as Error);
throw e;
}
if (!(await this.canTriggerBot())) {
const { login } = this.comment.user;
const org = this.commentsApi.pullData.owner;
this.logger.warn(
"User is not allowed to trigger the bot. " +
`He is not the author of the PR and does not *publicly* belong to the org: https://github.com/orgs/${org}/people`,
);
await this.commentsApi.reactToComment(this.comment.id, "-1");
await this.commentsApi.comment(
"## Auto-Merge-Bot\n" +
`User @${login} is not the author of the PR and does not [*publicly* belong to the org \`${org}\`](https://github.com/orgs/${org}/people).\n\n` +
"Only author or *public* org members can trigger the bot.",
);
return;
}
this.logger.debug("User can trigger bot");

const [_, command] = this.comment.body.split(" ");
try {
switch (command as Command) {
case "merge":
await this.commentsApi.reactToComment(this.comment.id, "+1");
await merger.enableAutoMerge();
await this.commentsApi.comment(
"Enabled `auto-merge` in Pull Request",
);
break;
case "cancel":
await this.commentsApi.reactToComment(this.comment.id, "+1");
await merger.disableAutoMerge();
await this.commentsApi.comment(
"Disabled `auto-merge` in Pull Request",
);
break;
case "help":
await this.commentsApi.comment("## Auto-Merge-Bot\n" + botCommands);
break;
default: {
await this.commentsApi.reactToComment(this.comment.id, "confused");
await this.commentsApi.comment(
"## Auto-Merge-Bot\n" +
`Command \`${command}\` not recognized.\n\n` +
botCommands,
);
}
}
} catch (e) {
this.logger.error(e as Error);
throw e;
}
}
}
34 changes: 24 additions & 10 deletions src/github/comments.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,45 @@
import { PullRequest } from "@octokit/webhooks-types";

import { ActionLogger, GitHubClient } from "./types";

/** API class that uses the default token to access the data from the pull request and the repository */
export class CommentsApi {
constructor(
private readonly api: GitHubClient,
private readonly logger: ActionLogger,
public readonly pullData: { repo: string, owner: string, number: number }
) { }
public readonly pullData: { repo: string; owner: string; number: number },
) {}

async comment(message: string) {
await this.api.rest.issues.createComment({ ...this.pullData, body: message, issue_number: this.pullData.number });
await this.api.rest.issues.createComment({
...this.pullData,
body: message,
issue_number: this.pullData.number,
});
}

async reactToComment(commentId: number, reaction: "+1" | "-1" | "confused"): Promise<void> {
await this.api.rest.reactions.createForIssueComment({ ...this.pullData, comment_id: commentId, content: reaction });
async reactToComment(
commentId: number,
reaction: "+1" | "-1" | "confused",
): Promise<void> {
await this.api.rest.reactions.createForIssueComment({
...this.pullData,
comment_id: commentId,
content: reaction,
});
}

async userBelongsToOrg(username: string): Promise<boolean> {
const org = this.pullData.owner;
this.logger.debug(`Checking if user ${username} belongs to ${org} as a public user.`);
this.logger.debug(
`Checking if user ${username} belongs to ${org} as a public user.`,
);
// If the user does not belong to the org, this will throw an http error
try {
const { status } = await this.api.rest.orgs.checkPublicMembershipForUser({org, username});
const { status } = await this.api.rest.orgs.checkPublicMembershipForUser({
org,
username,
});
return status === 204;
} catch (error){
} catch (error) {
this.logger.warn(error as Error);
return false;
}
Expand Down
47 changes: 26 additions & 21 deletions src/github/merger.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { graphql } from "@octokit/graphql";
import { ActionLogger } from "./types";
import { PullRequestMergeMethod } from "@octokit/graphql-schema";

import { ActionLogger } from "./types";

// https://docs.github.com/en/graphql/reference/mutations#enablepullrequestautomerge
export const ENABLE_AUTO_MERGE = `
Expand All @@ -22,24 +22,29 @@ mutation($prId: ID!) {
export type MergeMethod = "SQUASH" | "MERGE" | "REBASE";

export class Merger {
constructor(private readonly nodeId: string, private readonly gql: typeof graphql, private readonly logger: ActionLogger, private readonly mergeMethod: PullRequestMergeMethod) {

}

async enableAutoMerge() {
const mergeRequest = await this.gql<{ enablePullRequestAutoMerge: { clientMutationId: unknown } }>(ENABLE_AUTO_MERGE,
{
prId: this.nodeId,
mergeMethod: this.mergeMethod
});
this.logger.info("Succesfully enabled auto-merge");
}

async disableAutoMerge() {
const mergeRequest = await this.gql<{ disablePullRequestAutoMerge: { clientMutationId: unknown } }>(DISABLE_AUTO_MERGE,
{
prId: this.nodeId
});
this.logger.info("Succesfully disabled auto-merge");
}
constructor(
private readonly nodeId: string,
private readonly gql: typeof graphql,
private readonly logger: ActionLogger,
private readonly mergeMethod: PullRequestMergeMethod,
) {}

async enableAutoMerge() {
const mergeRequest = await this.gql<{
enablePullRequestAutoMerge: { clientMutationId: unknown };
}>(ENABLE_AUTO_MERGE, {
prId: this.nodeId,
mergeMethod: this.mergeMethod,
});
this.logger.info("Succesfully enabled auto-merge");
}

async disableAutoMerge() {
const mergeRequest = await this.gql<{
disablePullRequestAutoMerge: { clientMutationId: unknown };
}>(DISABLE_AUTO_MERGE, {
prId: this.nodeId,
});
this.logger.info("Succesfully disabled auto-merge");
}
}

0 comments on commit da4b77e

Please sign in to comment.