diff --git a/README.md b/README.md index 581ad8c..b5707e4 100644 --- a/README.md +++ b/README.md @@ -91,19 +91,6 @@ If for some reason, triggering on pr is not possible, you can use push. This message is from a push. ``` -### Override pull request number - -Use `number_force` to comment on a different pull request than the one that triggered the event. - -```yaml -- uses: marocchino/sticky-pull-request-comment@v2 - with: - number_force: 123 - message: | - This comment will be posted to PR #123, - regardless of which PR triggered this workflow. -``` - ### Read comment from a file ```yaml @@ -230,10 +217,6 @@ For more detailed information about permissions, you can read from the link belo **Optional** Pull request number for push event. Note that this has a **lower priority** than the number of a pull_request event. -### `number_force` - -**Optional** Pull request number for any event. Note that this has the **highest priority** and will override the number from a pull_request event. - ### `owner` **Optional** Another repository owner, If not set, the current repository owner is used by default. Note that when you trying changing a repo, be aware that `GITHUB_TOKEN` should also have permission for that repository. diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts index b44a433..9ea14fa 100644 --- a/__tests__/config.test.ts +++ b/__tests__/config.test.ts @@ -1,213 +1,430 @@ -import {afterEach, describe, expect, test, vi} from "vitest" -import {resolve} from "node:path" +import { beforeEach, afterEach, test, expect, vi, describe } from 'vitest' -vi.mock("@actions/core", () => ({ - getInput: vi.fn().mockReturnValue(""), - getBooleanInput: vi.fn().mockReturnValue(false), - getMultilineInput: vi.fn().mockReturnValue([]), - setFailed: vi.fn(), -})) - -const mockContext = vi.hoisted(() => ({ - repo: {owner: "marocchino", repo: "sticky-pull-request-comment"}, - payload: {} as Record, -})) - -vi.mock("@actions/github", () => ({ - context: mockContext, -})) - -const mockGlobCreate = vi.hoisted(() => vi.fn()) - -vi.mock("@actions/glob", () => ({ - create: mockGlobCreate, -})) - -afterEach(() => { - mockContext.payload = {} - mockContext.repo = {owner: "marocchino", repo: "sticky-pull-request-comment"} - mockGlobCreate.mockReset() -}) - -async function loadConfig( - setup?: (mocks: {core: typeof import("@actions/core")}) => void, -) { - vi.resetModules() - const core = await import("@actions/core") - // vi.resetModules clears the config module cache but not mock instances, - // so reset core back to default values before each test. - vi.mocked(core.getInput).mockReturnValue("") - vi.mocked(core.getBooleanInput).mockReturnValue(false) - vi.mocked(core.getMultilineInput).mockReturnValue([]) - setup?.({core}) - const config = await import("../src/config") - return {config, core} +const mockConfig = { + pullRequestNumber: 123, + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false, + getBody: vi.fn().mockResolvedValue("") } -describe("pullRequestNumber", () => { - test("number_force takes highest priority", async () => { - mockContext.payload = {pull_request: {number: 789}} - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getInput).mockImplementation(name => { - if (name === "number_force") return "456" - if (name === "number") return "123" - return "" - }) - }) - expect(config.pullRequestNumber).toBe(456) - }) - - test("falls back to context.payload.pull_request.number", async () => { - mockContext.payload = {pull_request: {number: 789}} - const {config} = await loadConfig() - expect(config.pullRequestNumber).toBe(789) - }) - - test("falls back to number input", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getInput).mockImplementation(name => (name === "number" ? "123" : "")) - }) - expect(config.pullRequestNumber).toBe(123) - }) +vi.mock('../src/config', () => { + return mockConfig }) -describe("repo", () => { - test("defaults to context.repo", async () => { - const {config} = await loadConfig() - expect(config.repo).toEqual({owner: "marocchino", repo: "sticky-pull-request-comment"}) - }) +beforeEach(() => { + // Set up default environment variables for each test + process.env["GITHUB_REPOSITORY"] = "marocchino/stick-pull-request-comment" + process.env["INPUT_NUMBER"] = "123" + process.env["INPUT_APPEND"] = "false" + process.env["INPUT_RECREATE"] = "false" + process.env["INPUT_DELETE"] = "false" + process.env["INPUT_ONLY_CREATE"] = "false" + process.env["INPUT_ONLY_UPDATE"] = "false" + process.env["INPUT_HIDE"] = "false" + process.env["INPUT_HIDE_AND_RECREATE"] = "false" + process.env["INPUT_HIDE_CLASSIFY"] = "OUTDATED" + process.env["INPUT_HIDE_DETAILS"] = "false" + process.env["INPUT_GITHUB_TOKEN"] = "some-token" + process.env["INPUT_IGNORE_EMPTY"] = "false" + process.env["INPUT_SKIP_UNCHANGED"] = "false" + process.env["INPUT_FOLLOW_SYMBOLIC_LINKS"] = "false" + + // 모킹된 값 초기화 + mockConfig.pullRequestNumber = 123 + mockConfig.repo = {owner: "marocchino", repo: "stick-pull-request-comment"} + mockConfig.header = "" + mockConfig.append = false + mockConfig.recreate = false + mockConfig.deleteOldComment = false + mockConfig.hideOldComment = false + mockConfig.hideAndRecreate = false + mockConfig.hideClassify = "OUTDATED" + mockConfig.hideDetails = false + mockConfig.githubToken = "some-token" + mockConfig.ignoreEmpty = false + mockConfig.skipUnchanged = false + mockConfig.getBody.mockResolvedValue("") +}) - test("uses owner and repo inputs when provided", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getInput).mockImplementation(name => { - if (name === "owner") return "jin" - if (name === "repo") return "other" - return "" - }) - }) - expect(config.repo).toEqual({owner: "jin", repo: "other"}) +afterEach(() => { + vi.resetModules() + delete process.env["GITHUB_REPOSITORY"] + delete process.env["INPUT_OWNER"] + delete process.env["INPUT_REPO"] + delete process.env["INPUT_HEADER"] + delete process.env["INPUT_MESSAGE"] + delete process.env["INPUT_NUMBER"] + delete process.env["INPUT_APPEND"] + delete process.env["INPUT_RECREATE"] + delete process.env["INPUT_DELETE"] + delete process.env["INPUT_ONLY_CREATE"] + delete process.env["INPUT_ONLY_UPDATE"] + delete process.env["INPUT_HIDE"] + delete process.env["INPUT_HIDE_AND_RECREATE"] + delete process.env["INPUT_HIDE_CLASSIFY"] + delete process.env["INPUT_HIDE_DETAILS"] + delete process.env["INPUT_GITHUB_TOKEN"] + delete process.env["INPUT_PATH"] + delete process.env["INPUT_IGNORE_EMPTY"] + delete process.env["INPUT_SKIP_UNCHANGED"] + delete process.env["INPUT_FOLLOW_SYMBOLIC_LINKS"] +}) + +test("repo", async () => { + process.env["INPUT_OWNER"] = "jin" + process.env["INPUT_REPO"] = "other" + + mockConfig.repo = {owner: "jin", repo: "other"} + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "jin", repo: "other"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false }) + expect(await config.getBody()).toEqual("") }) test("header", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getInput).mockImplementation(name => (name === "header" ? "my-header" : "")) + process.env["INPUT_HEADER"] = "header" + mockConfig.header = "header" + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "header", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false }) - expect(config.header).toBe("my-header") + expect(await config.getBody()).toEqual("") }) test("append", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getBooleanInput).mockImplementation(name => name === "append") + process.env["INPUT_APPEND"] = "true" + mockConfig.append = true + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: true, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false }) - expect(config.append).toBe(true) + expect(await config.getBody()).toEqual("") }) test("recreate", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getBooleanInput).mockImplementation(name => name === "recreate") + process.env["INPUT_RECREATE"] = "true" + mockConfig.recreate = true + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: true, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false }) - expect(config.recreate).toBe(true) + expect(await config.getBody()).toEqual("") }) -test("deleteOldComment", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getBooleanInput).mockImplementation(name => name === "delete") +test("delete", async () => { + process.env["INPUT_DELETE"] = "true" + mockConfig.deleteOldComment = true + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: true, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false }) - expect(config.deleteOldComment).toBe(true) + expect(await config.getBody()).toEqual("") }) test("hideOldComment", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getBooleanInput).mockImplementation(name => name === "hide") + process.env["INPUT_HIDE"] = "true" + mockConfig.hideOldComment = true + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: true, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false }) - expect(config.hideOldComment).toBe(true) + expect(await config.getBody()).toEqual("") }) test("hideAndRecreate", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getBooleanInput).mockImplementation(name => name === "hide_and_recreate") + process.env["INPUT_HIDE_AND_RECREATE"] = "true" + mockConfig.hideAndRecreate = true + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: true, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false }) - expect(config.hideAndRecreate).toBe(true) + expect(await config.getBody()).toEqual("") }) test("hideClassify", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getInput).mockImplementation(name => (name === "hide_classify" ? "OFF_TOPIC" : "")) + process.env["INPUT_HIDE_CLASSIFY"] = "OFF_TOPIC" + mockConfig.hideClassify = "OFF_TOPIC" + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OFF_TOPIC", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false }) - expect(config.hideClassify).toBe("OFF_TOPIC") + expect(await config.getBody()).toEqual("") }) test("hideDetails", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getBooleanInput).mockImplementation(name => name === "hide_details") + process.env["INPUT_HIDE_DETAILS"] = "true" + mockConfig.hideDetails = true + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: true, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false }) - expect(config.hideDetails).toBe(true) + expect(await config.getBody()).toEqual("") }) -test("ignoreEmpty", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getBooleanInput).mockImplementation(name => name === "ignore_empty") - }) - expect(config.ignoreEmpty).toBe(true) -}) - -test("skipUnchanged", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getBooleanInput).mockImplementation(name => name === "skip_unchanged") - }) - expect(config.skipUnchanged).toBe(true) -}) - -test("githubToken", async () => { - const {config} = await loadConfig(({core}) => { - vi.mocked(core.getInput).mockImplementation(name => (name === "GITHUB_TOKEN" ? "my-token" : "")) - }) - expect(config.githubToken).toBe("my-token") -}) - -describe("getBody", () => { - test("returns message when no path is provided", async () => { - const {config, core} = await loadConfig() - vi.mocked(core.getInput).mockImplementation(name => (name === "message" ? "hello there" : "")) - expect(await config.getBody()).toBe("hello there") - }) - - test("returns file content when path exists", async () => { - const {config, core} = await loadConfig() - vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/result"]) - mockGlobCreate.mockResolvedValue({ - glob: vi.fn().mockResolvedValue([resolve("__tests__/assets/result")]), +describe("path", () => { + test("when exists return content of a file", async () => { + process.env["INPUT_PATH"] = "./__tests__/assets/result" + mockConfig.getBody.mockResolvedValue("hi there\n") + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false }) - expect(await config.getBody()).toBe("hi there\n") + expect(await config.getBody()).toEqual("hi there\n") }) - test("glob matches multiple files", async () => { - const {config, core} = await loadConfig() - vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/*"]) - mockGlobCreate.mockResolvedValue({ - glob: vi - .fn() - .mockResolvedValue([ - resolve("__tests__/assets/result"), - resolve("__tests__/assets/result2"), - ]), + test("glob match files", async () => { + process.env["INPUT_PATH"] = "./__tests__/assets/*" + mockConfig.getBody.mockResolvedValue("hi there\n\nhey there\n") + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false }) - expect(await config.getBody()).toBe("hi there\n\nhey there\n") + expect(await config.getBody()).toEqual("hi there\n\nhey there\n") }) - test("returns empty string when path matches no files", async () => { - const {config, core} = await loadConfig() - vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/not_exists"]) - mockGlobCreate.mockResolvedValue({glob: vi.fn().mockResolvedValue([])}) - expect(await config.getBody()).toBe("") - }) - - test("returns empty string and calls setFailed when glob throws", async () => { - const {config, core} = await loadConfig() - vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/result"]) - mockGlobCreate.mockRejectedValue(new Error("glob error")) - expect(await config.getBody()).toBe("") - expect(core.setFailed).toHaveBeenCalledWith("glob error") + test("when not exists return null string", async () => { + process.env["INPUT_PATH"] = "./__tests__/assets/not_exists" + mockConfig.getBody.mockResolvedValue("") + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false + }) + expect(await config.getBody()).toEqual("") }) }) + +test("message", async () => { + process.env["INPUT_MESSAGE"] = "hello there" + mockConfig.getBody.mockResolvedValue("hello there") + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: false + }) + expect(await config.getBody()).toEqual("hello there") +}) + +test("ignore_empty", async () => { + process.env["INPUT_IGNORE_EMPTY"] = "true" + mockConfig.ignoreEmpty = true + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: true, + skipUnchanged: false + }) + expect(await config.getBody()).toEqual("") +}) + +test("skip_unchanged", async () => { + process.env["INPUT_SKIP_UNCHANGED"] = "true" + mockConfig.skipUnchanged = true + + const config = await import('../src/config') + expect(config).toMatchObject({ + pullRequestNumber: expect.any(Number), + repo: {owner: "marocchino", repo: "stick-pull-request-comment"}, + header: "", + append: false, + recreate: false, + deleteOldComment: false, + hideOldComment: false, + hideAndRecreate: false, + hideClassify: "OUTDATED", + hideDetails: false, + githubToken: "some-token", + ignoreEmpty: false, + skipUnchanged: true + }) + expect(await config.getBody()).toEqual("") +}) diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 1fd3be7..8ddc0af 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -115,9 +115,8 @@ describe("run", () => { mockConfig.recreate = true const {core} = await runMain() expect(core.setFailed).toHaveBeenCalledWith( - "delete and recreate cannot be set to true simultaneously", + "delete and recreate cannot be both set to true", ) - expect(mockConfig.getBody).not.toHaveBeenCalled() }) test("fails when deleteOldComment and onlyCreateComment are both true", async () => { @@ -125,9 +124,8 @@ describe("run", () => { mockConfig.onlyCreateComment = true const {core} = await runMain() expect(core.setFailed).toHaveBeenCalledWith( - "delete and only_create cannot be set to true simultaneously", + "delete and only_create cannot be both set to true", ) - expect(mockConfig.getBody).not.toHaveBeenCalled() }) test("fails when deleteOldComment and hideOldComment are both true", async () => { @@ -135,9 +133,8 @@ describe("run", () => { mockConfig.hideOldComment = true const {core} = await runMain() expect(core.setFailed).toHaveBeenCalledWith( - "delete and hide cannot be set to true simultaneously", + "delete and hide cannot be both set to true", ) - expect(mockConfig.getBody).not.toHaveBeenCalled() }) test("fails when onlyCreateComment and onlyUpdateComment are both true", async () => { @@ -145,9 +142,8 @@ describe("run", () => { mockConfig.onlyUpdateComment = true const {core} = await runMain() expect(core.setFailed).toHaveBeenCalledWith( - "only_create and only_update cannot be set to true simultaneously", + "only_create and only_update cannot be both set to true", ) - expect(mockConfig.getBody).not.toHaveBeenCalled() }) test("fails when hideOldComment and hideAndRecreate are both true", async () => { @@ -155,19 +151,8 @@ describe("run", () => { mockConfig.hideAndRecreate = true const {core} = await runMain() expect(core.setFailed).toHaveBeenCalledWith( - "hide and hide_and_recreate cannot be set to true simultaneously", + "hide and hide_and_recreate cannot be both set to true", ) - expect(mockConfig.getBody).not.toHaveBeenCalled() - }) - - test("fails when deleteOldComment and hideAndRecreate are both true", async () => { - mockConfig.deleteOldComment = true - mockConfig.hideAndRecreate = true - const {core} = await runMain() - expect(core.setFailed).toHaveBeenCalledWith( - "delete and hide_and_recreate cannot be set to true simultaneously", - ) - expect(mockConfig.getBody).not.toHaveBeenCalled() }) test("deletes previous comment when deleteOldComment is true and previous comment exists", async () => { diff --git a/__tests__/validate.test.ts b/__tests__/validate.test.ts deleted file mode 100644 index 0ece200..0000000 --- a/__tests__/validate.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import {describe, expect, test} from "vitest" -import {validateBody, validateExclusiveModes} from "../src/validate" - -describe("validateBody", () => { - test("throws when body is empty and neither delete nor hide is set", () => { - expect(() => validateBody("", false, false)).toThrow( - "Either message or path input is required", - ) - }) - - test("does not throw when body is provided", () => { - expect(() => validateBody("some body", false, false)).not.toThrow() - }) - - test("does not throw when body is empty but deleteOldComment is true", () => { - expect(() => validateBody("", true, false)).not.toThrow() - }) - - test("does not throw when body is empty but hideOldComment is true", () => { - expect(() => validateBody("", false, true)).not.toThrow() - }) -}) - -describe("validateExclusiveModes", () => { - test("does not throw when no modes are enabled", () => { - expect(() => validateExclusiveModes(false, false, false, false, false, false)).not.toThrow() - }) - - test("does not throw when exactly one mode is enabled", () => { - expect(() => validateExclusiveModes(true, false, false, false, false, false)).not.toThrow() - expect(() => validateExclusiveModes(false, true, false, false, false, false)).not.toThrow() - expect(() => validateExclusiveModes(false, false, true, false, false, false)).not.toThrow() - expect(() => validateExclusiveModes(false, false, false, true, false, false)).not.toThrow() - expect(() => validateExclusiveModes(false, false, false, false, true, false)).not.toThrow() - expect(() => validateExclusiveModes(false, false, false, false, false, true)).not.toThrow() - }) - - test("throws when delete and recreate are both true", () => { - expect(() => validateExclusiveModes(true, true, false, false, false, false)).toThrow( - "delete and recreate cannot be set to true simultaneously", - ) - }) - - test("throws when delete and only_create are both true", () => { - expect(() => validateExclusiveModes(true, false, true, false, false, false)).toThrow( - "delete and only_create cannot be set to true simultaneously", - ) - }) - - test("throws when delete and only_update are both true", () => { - expect(() => validateExclusiveModes(true, false, false, true, false, false)).toThrow( - "delete and only_update cannot be set to true simultaneously", - ) - }) - - test("throws when delete and hide are both true", () => { - expect(() => validateExclusiveModes(true, false, false, false, true, false)).toThrow( - "delete and hide cannot be set to true simultaneously", - ) - }) - - test("throws when delete and hide_and_recreate are both true", () => { - expect(() => validateExclusiveModes(true, false, false, false, false, true)).toThrow( - "delete and hide_and_recreate cannot be set to true simultaneously", - ) - }) - - test("throws when only_create and only_update are both true", () => { - expect(() => validateExclusiveModes(false, false, true, true, false, false)).toThrow( - "only_create and only_update cannot be set to true simultaneously", - ) - }) - - test("throws when hide and hide_and_recreate are both true", () => { - expect(() => validateExclusiveModes(false, false, false, false, true, true)).toThrow( - "hide and hide_and_recreate cannot be set to true simultaneously", - ) - }) - - test("uses Oxford comma when three or more modes are enabled", () => { - expect(() => validateExclusiveModes(true, true, true, false, false, false)).toThrow( - "delete, recreate, and only_create cannot be set to true simultaneously", - ) - }) -}) diff --git a/action.yml b/action.yml index d14ce06..bb0b430 100644 --- a/action.yml +++ b/action.yml @@ -72,9 +72,6 @@ inputs: number: description: "pull request number for push event" required: false - number_force: - description: "pull request number for any event" - required: false owner: description: "Another repo owner, If not set, the current repo owner is used by default. Note that when you trying changing a repo, be aware that GITHUB_TOKEN should also have permission for that repository." required: false diff --git a/biome.json b/biome.json index 6d70561..04932cc 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.4.7/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.6/schema.json", "files": { "includes": ["src/**/*.ts"] }, diff --git a/package-lock.json b/package-lock.json index d198f40..edf34e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@octokit/graphql-schema": "^15.26.1" }, "devDependencies": { - "@biomejs/biome": "2.4.7", + "@biomejs/biome": "2.4.6", "@rollup/plugin-commonjs": "^29.0.2", "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-typescript": "^12.3.0", @@ -98,9 +98,9 @@ "license": "MIT" }, "node_modules/@biomejs/biome": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.7.tgz", - "integrity": "sha512-vXrgcmNGZ4lpdwZSpMf1hWw1aWS6B+SyeSYKTLrNsiUsAdSRN0J4d/7mF3ogJFbIwFFSOL3wT92Zzxia/d5/ng==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.6.tgz", + "integrity": "sha512-QnHe81PMslpy3mnpL8DnO2M4S4ZnYPkjlGCLWBZT/3R9M6b5daArWMMtEfP52/n174RKnwRIf3oT8+wc9ihSfQ==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -114,20 +114,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.4.7", - "@biomejs/cli-darwin-x64": "2.4.7", - "@biomejs/cli-linux-arm64": "2.4.7", - "@biomejs/cli-linux-arm64-musl": "2.4.7", - "@biomejs/cli-linux-x64": "2.4.7", - "@biomejs/cli-linux-x64-musl": "2.4.7", - "@biomejs/cli-win32-arm64": "2.4.7", - "@biomejs/cli-win32-x64": "2.4.7" + "@biomejs/cli-darwin-arm64": "2.4.6", + "@biomejs/cli-darwin-x64": "2.4.6", + "@biomejs/cli-linux-arm64": "2.4.6", + "@biomejs/cli-linux-arm64-musl": "2.4.6", + "@biomejs/cli-linux-x64": "2.4.6", + "@biomejs/cli-linux-x64-musl": "2.4.6", + "@biomejs/cli-win32-arm64": "2.4.6", + "@biomejs/cli-win32-x64": "2.4.6" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.7.tgz", - "integrity": "sha512-Oo0cF5mHzmvDmTXw8XSjhCia8K6YrZnk7aCS54+/HxyMdZMruMO3nfpDsrlar/EQWe41r1qrwKiCa2QDYHDzWA==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.6.tgz", + "integrity": "sha512-NW18GSyxr+8sJIqgoGwVp5Zqm4SALH4b4gftIA0n62PTuBs6G2tHlwNAOj0Vq0KKSs7Sf88VjjmHh0O36EnzrQ==", "cpu": [ "arm64" ], @@ -142,9 +142,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.7.tgz", - "integrity": "sha512-I+cOG3sd/7HdFtvDSnF9QQPrWguUH7zrkIMMykM3PtfWU9soTcS2yRb9Myq6MHmzbeCT08D1UmY+BaiMl5CcoQ==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.6.tgz", + "integrity": "sha512-4uiE/9tuI7cnjtY9b07RgS7gGyYOAfIAGeVJWEfeCnAarOAS7qVmuRyX6d7JTKw28/mt+rUzMasYeZ+0R/U1Mw==", "cpu": [ "x64" ], @@ -159,9 +159,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.7.tgz", - "integrity": "sha512-om6FugwmibzfP/6ALj5WRDVSND4H2G9X0nkI1HZpp2ySf9lW2j0X68oQSaHEnls6666oy4KDsc5RFjT4m0kV0w==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.6.tgz", + "integrity": "sha512-kMLaI7OF5GN1Q8Doymjro1P8rVEoy7BKQALNz6fiR8IC1WKduoNyteBtJlHT7ASIL0Cx2jR6VUOBIbcB1B8pew==", "cpu": [ "arm64" ], @@ -176,9 +176,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.7.tgz", - "integrity": "sha512-I2NvM9KPb09jWml93O2/5WMfNR7Lee5Latag1JThDRMURVhPX74p9UDnyTw3Ae6cE1DgXfw7sqQgX7rkvpc0vw==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.6.tgz", + "integrity": "sha512-F/JdB7eN22txiTqHM5KhIVt0jVkzZwVYrdTR1O3Y4auBOQcXxHK4dxULf4z43QyZI5tsnQJrRBHZy7wwtL+B3A==", "cpu": [ "arm64" ], @@ -193,9 +193,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.7.tgz", - "integrity": "sha512-bV8/uo2Tj+gumnk4sUdkerWyCPRabaZdv88IpbmDWARQQoA/Q0YaqPz1a+LSEDIL7OfrnPi9Hq1Llz4ZIGyIQQ==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.6.tgz", + "integrity": "sha512-oHXmUFEoH8Lql1xfc3QkFLiC1hGR7qedv5eKNlC185or+o4/4HiaU7vYODAH3peRCfsuLr1g6v2fK9dFFOYdyw==", "cpu": [ "x64" ], @@ -210,9 +210,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.7.tgz", - "integrity": "sha512-00kx4YrBMU8374zd2wHuRV5wseh0rom5HqRND+vDldJPrWwQw+mzd/d8byI9hPx926CG+vWzq6AeiT7Yi5y59g==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.6.tgz", + "integrity": "sha512-C9s98IPDu7DYarjlZNuzJKTjVHN03RUnmHV5htvqsx6vEUXCDSJ59DNwjKVD5XYoSS4N+BYhq3RTBAL8X6svEg==", "cpu": [ "x64" ], @@ -227,9 +227,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.7.tgz", - "integrity": "sha512-hOUHBMlFCvDhu3WCq6vaBoG0dp0LkWxSEnEEsxxXvOa9TfT6ZBnbh72A/xBM7CBYB7WgwqboetzFEVDnMxelyw==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.6.tgz", + "integrity": "sha512-xzThn87Pf3YrOGTEODFGONmqXpTwUNxovQb72iaUOdcw8sBSY3+3WD8Hm9IhMYLnPi0n32s3L3NWU6+eSjfqFg==", "cpu": [ "arm64" ], @@ -244,9 +244,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.7.tgz", - "integrity": "sha512-qEpGjSkPC3qX4ycbMUthXvi9CkRq7kZpkqMY1OyhmYlYLnANnooDQ7hDerM8+0NJ+DZKVnsIc07h30XOpt7LtQ==", + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.6.tgz", + "integrity": "sha512-7++XhnsPlr1HDbor5amovPjOH6vsrFOCdp93iKXhFn6bcMUI6soodj3WWKfgEO6JosKU1W5n3uky3WW9RlRjTg==", "cpu": [ "x64" ], diff --git a/package.json b/package.json index 91bb598..37eb9fb 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@octokit/graphql-schema": "^15.26.1" }, "devDependencies": { - "@biomejs/biome": "2.4.7", + "@biomejs/biome": "2.4.6", "@rollup/plugin-commonjs": "^29.0.2", "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-typescript": "^12.3.0", diff --git a/src/config.ts b/src/config.ts index bc71ddf..615a7e6 100644 --- a/src/config.ts +++ b/src/config.ts @@ -5,9 +5,7 @@ import {create} from "@actions/glob" import type {ReportedContentClassifiers} from "@octokit/graphql-schema" export const pullRequestNumber = - +core.getInput("number_force", {required: false}) || - context?.payload?.pull_request?.number || - +core.getInput("number", {required: false}) + context?.payload?.pull_request?.number || +core.getInput("number", {required: false}) export const repo = buildRepo() export const header = core.getInput("header", {required: false}) diff --git a/src/main.ts b/src/main.ts index 65b165e..790bda7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -27,7 +27,6 @@ import { repo, skipUnchanged, } from "./config" -import {validateBody, validateExclusiveModes} from "./validate" async function run(): Promise { if (Number.isNaN(pullRequestNumber) || pullRequestNumber < 1) { @@ -36,15 +35,6 @@ async function run(): Promise { } try { - validateExclusiveModes( - deleteOldComment, - recreate, - onlyCreateComment, - onlyUpdateComment, - hideOldComment, - hideAndRecreate, - ) - const body = await getBody() if (!body && ignoreEmpty) { @@ -52,7 +42,29 @@ async function run(): Promise { return } - validateBody(body, deleteOldComment, hideOldComment) + if (!deleteOldComment && !hideOldComment && !body) { + throw new Error("Either message or path input is required") + } + + if (deleteOldComment && recreate) { + throw new Error("delete and recreate cannot be both set to true") + } + + if (deleteOldComment && onlyCreateComment) { + throw new Error("delete and only_create cannot be both set to true") + } + + if (deleteOldComment && hideOldComment) { + throw new Error("delete and hide cannot be both set to true") + } + + if (onlyCreateComment && onlyUpdateComment) { + throw new Error("only_create and only_update cannot be both set to true") + } + + if (hideOldComment && hideAndRecreate) { + throw new Error("hide and hide_and_recreate cannot be both set to true") + } const octokit = github.getOctokit(githubToken) const previous = await findPreviousComment(octokit, repo, pullRequestNumber, header) diff --git a/src/validate.ts b/src/validate.ts deleted file mode 100644 index 9db2372..0000000 --- a/src/validate.ts +++ /dev/null @@ -1,35 +0,0 @@ -export function validateBody( - body: string, - deleteOldComment: boolean, - hideOldComment: boolean, -): void { - if (!deleteOldComment && !hideOldComment && !body) { - throw new Error("Either message or path input is required") - } -} - -export function validateExclusiveModes( - deleteOldComment: boolean, - recreate: boolean, - onlyCreateComment: boolean, - onlyUpdateComment: boolean, - hideOldComment: boolean, - hideAndRecreate: boolean, -): void { - const exclusiveModes: [string, boolean][] = [ - ["delete", deleteOldComment], - ["recreate", recreate], - ["only_create", onlyCreateComment], - ["only_update", onlyUpdateComment], - ["hide", hideOldComment], - ["hide_and_recreate", hideAndRecreate], - ] - const enabledModes = exclusiveModes.filter(([, flag]) => flag).map(([name]) => name) - if (enabledModes.length > 1) { - const last = enabledModes[enabledModes.length - 1] - const rest = enabledModes.slice(0, -1) - const joined = - enabledModes.length === 2 ? `${rest[0]} and ${last}` : `${rest.join(", ")}, and ${last}` - throw new Error(`${joined} cannot be set to true simultaneously`) - } -}