diff --git a/.github/workflows/auto-merge-bot.yml b/.github/workflows/auto-merge-bot.yml index adb259d..820cf4c 100644 --- a/.github/workflows/auto-merge-bot.yml +++ b/.github/workflows/auto-merge-bot.yml @@ -9,7 +9,7 @@ jobs: set-auto-merge: runs-on: ubuntu-latest # Important! This forces the job to run only on Pull Requests - if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/bot') }} + if: ${{ github.event.issue.pull_request && startsWith(github.event.comment.body, '/merge') }} steps: - name: Set auto merge uses: paritytech/auto-merge-bot@main diff --git a/README.md b/README.md index 2083074..7383f01 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ You can find all the inputs in [the action file](./action.yml), but let's walk t - **Optional**: Defaults to `SQUASH`. - Available types are `MERGE`, `REBASE` and `SQUASH`. - Make sure that the type of merge you selected is available in the repository merge options. +- `SILENT`: If the bot should be silent and not comment when enabling/disabling auto-merge. + - **Optional**: Defaults to `false`. ## Usage diff --git a/action.yml b/action.yml index 35c3047..16f4821 100644 --- a/action.yml +++ b/action.yml @@ -12,6 +12,9 @@ inputs: required: false description: The merge method to use. Must be one of MERGE, SQUASH or REBASE. default: SQUASH + SILENT: + required: false + description: If true, the bot will not post a comment on the PR. outputs: repo: description: 'The name of the repo in owner/repo pattern' diff --git a/package.json b/package.json index 247da51..bd617cf 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@types/jest": "^29.5.5", "@vercel/ncc": "^0.38.0", "jest": "^29.7.0", + "jest-mock-extended": "^3.0.5", "ts-jest": "^29.1.1", "typescript": "^5.2.2" } diff --git a/src/bot.ts b/src/bot.ts index 6334786..6cd0e57 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -91,7 +91,10 @@ export class Bot { break; // `/merge help` case "help": - await this.commentsApi.comment("## Auto-Merge-Bot\n" + botCommands); + await this.commentsApi.comment( + "## Auto-Merge-Bot\n" + botCommands, + true, + ); break; // `/merge anything else` default: { @@ -100,6 +103,7 @@ export class Bot { "## Auto-Merge-Bot\n" + `Command \`${command}\` not recognized.\n\n` + botCommands, + true, ); } } diff --git a/src/github/comments.ts b/src/github/comments.ts index d2a279a..311e179 100644 --- a/src/github/comments.ts +++ b/src/github/comments.ts @@ -6,14 +6,26 @@ export class CommentsApi { private readonly api: GitHubClient, private readonly logger: ActionLogger, public readonly pullData: { repo: string; owner: string; number: number }, + private readonly silentMode: boolean = false, ) {} - async comment(message: string): Promise { - await this.api.rest.issues.createComment({ - ...this.pullData, - body: message, - issue_number: this.pullData.number, - }); + /** + * Logs and writes a comments. Will only log if silentMode is true + * @param message Message to write in the comment + * @param overrideSilentMode If silent mode should be overriden + */ + async comment( + message: string, + overrideSilentMode: boolean = false, + ): Promise { + this.logger.info("Commenting: " + message); + if (!this.silentMode || overrideSilentMode) { + await this.api.rest.issues.createComment({ + ...this.pullData, + body: message, + issue_number: this.pullData.number, + }); + } } async reactToComment( diff --git a/src/index.ts b/src/index.ts index 912eebc..bfd9844 100644 --- a/src/index.ts +++ b/src/index.ts @@ -52,14 +52,27 @@ const getMergeMethod = (): PullRequestMergeMethod => { return method; }; +const silentMode = getInput("SILENT", { required: false }) === "true"; + +logger.info( + `Silent mode is ${ + silentMode ? "enabled" : "disabled. Bot will comment actions" + }`, +); + if (context.payload.comment) { const token = getInput("GITHUB_TOKEN", { required: true }); const comment = context.payload.comment as unknown as IssueComment; const issue = context.payload.issue as unknown as Issue; - const commentsApi = new CommentsApi(getOctokit(token), logger, { - ...repo, - number: issue.number, - }); + const commentsApi = new CommentsApi( + getOctokit(token), + logger, + { + ...repo, + number: issue.number, + }, + silentMode, + ); const gql = getOctokit(token).graphql.defaults({ headers: { authorization: `token ${token}` }, }) as graphql; diff --git a/src/test/comments.test.ts b/src/test/comments.test.ts new file mode 100644 index 0000000..9ac1e72 --- /dev/null +++ b/src/test/comments.test.ts @@ -0,0 +1,73 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +import { DeepMockProxy, mock, mockDeep, MockProxy } from "jest-mock-extended"; + +import { CommentsApi } from "../github/comments"; +import { ActionLogger, GitHubClient } from "../github/types"; + +describe("Comments", () => { + let comments: CommentsApi; + let api: DeepMockProxy; + let logger: MockProxy; + const repo = { repo: "example", owner: "me", number: 123 }; + beforeEach(() => { + api = mockDeep(); + logger = mock(); + comments = new CommentsApi(api, logger, repo); + }); + + describe("Comment action", () => { + test("Should comment", async () => { + await comments.comment("Hello"); + expect(api.rest.issues.createComment).toHaveBeenCalledWith({ + body: "Hello", + ...repo, + issue_number: repo.number, + }); + }); + + test("Should log when commenting", async () => { + await comments.comment("Log this"); + expect(logger.info).toHaveBeenCalledWith("Commenting: Log this"); + }); + + test("Should not comment on silent mode", async () => { + comments = new CommentsApi(api, logger, repo, true); + await comments.comment("Example"); + expect(api.rest.issues.createComment).not.toHaveBeenCalled(); + }); + + test("Should log while in silent mode", async () => { + comments = new CommentsApi(api, logger, repo, true); + await comments.comment("Bye"); + expect(logger.info).toHaveBeenCalledWith("Commenting: Bye"); + }); + + test("Should override silent mode", async () => { + comments = new CommentsApi(api, logger, repo, true); + await comments.comment("Overrider", true); + expect(api.rest.issues.createComment).toHaveBeenCalledWith({ + body: "Overrider", + ...repo, + issue_number: repo.number, + }); + }); + }); + + describe("userBelongsToOrg", () => { + test("Should return false when http call fails", async () => { + api.rest.orgs.checkPublicMembershipForUser.mockRejectedValue( + "This is an error", + ); + const userBelongs = await comments.userBelongsToOrg("example"); + expect(userBelongs).toBeFalsy(); + }); + + test("Should return false when http call returns 204", async () => { + api.rest.orgs.checkPublicMembershipForUser.mockResolvedValue({ + status: 204, + } as never); + const userBelongs = await comments.userBelongsToOrg("example"); + expect(userBelongs).toBeTruthy(); + }); + }); +}); diff --git a/src/test/index.ts b/src/test/index.ts deleted file mode 100644 index 3a327af..0000000 --- a/src/test/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -test("adds 1 + 2 to equal 3", () => { - expect(1 + 2).toBe(3); -}); diff --git a/yarn.lock b/yarn.lock index 7932878..0020cfc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2697,6 +2697,13 @@ jest-message-util@^29.7.0: slash "^3.0.0" stack-utils "^2.0.3" +jest-mock-extended@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/jest-mock-extended/-/jest-mock-extended-3.0.5.tgz#ebf208e363f4f1db603b81fb005c4055b7c1c8b7" + integrity sha512-/eHdaNPUAXe7f65gHH5urc8SbRVWjYxBqmCgax2uqOBJy8UUcCBMN1upj1eZ8y/i+IqpyEm4Kq0VKss/GCCTdw== + dependencies: + ts-essentials "^7.0.3" + jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" @@ -3781,6 +3788,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +ts-essentials@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" + integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== + ts-jest@^29.1.1: version "29.1.1" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b"