diff --git a/.gitignore b/.gitignore index c466de2..44ed79b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ __tests__/runner/* lib/* +dist/src/ rollup.config.js # ^^^ compiled output of rollup.config.ts (generated by --configPlugin at build time) # comment out in distribution branches diff --git a/README.md b/README.md index 581ad8c..ca6cc28 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 `{{{content}}}` 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: | + ``` + {{{content}}} + ``` +```` + ### 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 `{{{content}}}` 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 `{{{content}}}`, the file content is embedded at that placeholder position. ### `number` diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts index b44a433..cee11df 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 {{{content}}} 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{{{content}}}\n```" + return "" + }) + mockGlobCreate.mockResolvedValue({ + glob: vi.fn().mockResolvedValue([resolve("__tests__/assets/result")]), + }) + expect(await config.getBody()).toBe("```\nhi there\n\n```") + }) + + test("replaces {{{content}}} 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 "{{{content}}}\n---\n{{{content}}}" + return "" + }) + mockGlobCreate.mockResolvedValue({ + glob: vi.fn().mockResolvedValue([resolve("__tests__/assets/result")]), + }) + expect(await config.getBody()).toBe("hi there\n\n---\n{{{content}}}") + }) + + test("uses message as body when path is provided but message has no {{{content}}} 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("no placeholder here") + }) + + test("embeds multiple files content in message when {{{content}}} 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{{{content}}}\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..8bfec43 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 `{{{content}}}` 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 `{{{content}}}`, 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..d1a4d1d 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) { + return messageInput.replace("{{{content}}}", fileContent) + } + return fileContent } catch (error) { if (error instanceof Error) { core.setFailed(error.message) } return "" } - } else { - return core.getInput("message", {required: false}) } + return messageInput }