diff --git a/README.md b/README.md index 581ad8c..78d7fa7 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,22 @@ with: path: path-to-comment-contents.txt ``` +### Embed file content inside a message template + +Use `{path}` as a placeholder in the `message` to insert file content at that position. + +````yaml +- name: Run Test + run: rake test > result.txt +- uses: marocchino/sticky-pull-request-comment@v3 + with: + path: result.txt + message: | + ``` + {path} + ``` +```` + ### Delete the previous comment and add a comment at the end ```yaml @@ -220,11 +236,11 @@ For more detailed information about permissions, you can read from the link belo ### `message` -**Optional** Comment message +**Optional** Comment message. When used together with `path`, use `{path}` as a placeholder in the message where the file content should be inserted. ### `path` -**Optional** Path to file containing comment message +**Optional** Path to file containing comment message. When `message` is also provided and contains `{path}`, the file content is embedded at that placeholder position. ### `number` diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts index b44a433..7c25634 100644 --- a/__tests__/config.test.ts +++ b/__tests__/config.test.ts @@ -210,4 +210,61 @@ describe("getBody", () => { expect(await config.getBody()).toBe("") expect(core.setFailed).toHaveBeenCalledWith("glob error") }) + + test("embeds file content in message when {path} placeholder is used", async () => { + const {config, core} = await loadConfig() + vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/result"]) + vi.mocked(core.getInput).mockImplementation(name => { + if (name === "message") return "```\n{path}\n```" + return "" + }) + mockGlobCreate.mockResolvedValue({ + glob: vi.fn().mockResolvedValue([resolve("__tests__/assets/result")]), + }) + expect(await config.getBody()).toBe("```\nhi there\n\n```") + }) + + test("replaces all {path} occurrences in message with file content", async () => { + const {config, core} = await loadConfig() + vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/result"]) + vi.mocked(core.getInput).mockImplementation(name => { + if (name === "message") return "{path}\n---\n{path}" + return "" + }) + mockGlobCreate.mockResolvedValue({ + glob: vi.fn().mockResolvedValue([resolve("__tests__/assets/result")]), + }) + expect(await config.getBody()).toBe("hi there\n\n---\nhi there\n") + }) + + test("uses file content as body when path is provided but message has no {path} placeholder", async () => { + const {config, core} = await loadConfig() + vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/result"]) + vi.mocked(core.getInput).mockImplementation(name => { + if (name === "message") return "no placeholder here" + return "" + }) + mockGlobCreate.mockResolvedValue({ + glob: vi.fn().mockResolvedValue([resolve("__tests__/assets/result")]), + }) + expect(await config.getBody()).toBe("hi there\n") + }) + + test("embeds multiple files content in message when {path} placeholder is used", async () => { + const {config, core} = await loadConfig() + vi.mocked(core.getMultilineInput).mockReturnValue(["__tests__/assets/*"]) + vi.mocked(core.getInput).mockImplementation(name => { + if (name === "message") return "```\n{path}\n```" + return "" + }) + mockGlobCreate.mockResolvedValue({ + glob: vi + .fn() + .mockResolvedValue([ + resolve("__tests__/assets/result"), + resolve("__tests__/assets/result2"), + ]), + }) + expect(await config.getBody()).toBe("```\nhi there\n\nhey there\n\n```") + }) }) diff --git a/action.yml b/action.yml index d14ce06..064c6cd 100644 --- a/action.yml +++ b/action.yml @@ -52,10 +52,10 @@ inputs: default: "OUTDATED" required: false message: - description: "comment message" + description: "comment message. When used together with path, use `{path}` as a placeholder in the message where the file content should be inserted." required: false path: - description: "glob path to file(s) containing comment message" + description: "glob path to file(s) containing comment message. When message is also provided and contains `{path}`, the file content is embedded at that placeholder position." required: false ignore_empty: description: "Indicates whether to ignore missing or empty messages" diff --git a/src/config.ts b/src/config.ts index bc71ddf..24ab94c 100644 --- a/src/config.ts +++ b/src/config.ts @@ -50,20 +50,27 @@ export async function getBody(): Promise { const followSymbolicLinks = core.getBooleanInput("follow_symbolic_links", { required: true, }) + const messageInput = core.getInput("message", {required: false}) + if (pathInput && pathInput.length > 0) { try { const globber = await create(pathInput.join("\n"), { followSymbolicLinks, matchDirectories: false, }) - return (await globber.glob()).map(path => readFileSync(path, "utf-8")).join("\n") + const fileContent = (await globber.glob()) + .map(path => readFileSync(path, "utf-8")) + .join("\n") + if (messageInput && messageInput.includes("{path}")) { + return messageInput.replace(/\{path\}/g, fileContent) + } + return fileContent } catch (error) { if (error instanceof Error) { core.setFailed(error.message) } return "" } - } else { - return core.getInput("message", {required: false}) } + return messageInput }