From 981267b215887a42bb68ece6c9a48b4f55cfe0e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:07:07 +0000 Subject: [PATCH] feat: support combining path and message inputs using $path placeholder Co-authored-by: marocchino <128431+marocchino@users.noreply.github.com> --- __tests__/config.test.ts | 57 ++++++++++++++++++++++++++++++++++++++++ action.yml | 4 +-- src/config.ts | 12 +++++++-- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts index 2861452..0dd6304 100644 --- a/__tests__/config.test.ts +++ b/__tests__/config.test.ts @@ -255,4 +255,61 @@ describe("getBody", () => { }) expect(await config.getBody()).toBe("hello there\n```") }) + + 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 6cd7166..f796b90 100644 --- a/action.yml +++ b/action.yml @@ -52,7 +52,7 @@ 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 prefix: description: "text to prepend to the message (e.g. ` ``` ` to open a code block)" @@ -61,7 +61,7 @@ inputs: description: "text to append to the message (e.g. ` ``` ` to close a code block)" 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 0c17390..e69eb02 100644 --- a/src/config.ts +++ b/src/config.ts @@ -52,6 +52,7 @@ export async function getBody(): Promise { }) const prefixInput = core.getInput("prefix", {required: false}) const suffixInput = core.getInput("suffix", {required: false}) + const messageInput = core.getInput("message", {required: false}) let body: string if (pathInput && pathInput.length > 0) { @@ -60,7 +61,14 @@ export async function getBody(): Promise { followSymbolicLinks, matchDirectories: false, }) - body = (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")) { + body = messageInput.replace(/\$path/g, fileContent) + } else { + body = fileContent + } } catch (error) { if (error instanceof Error) { core.setFailed(error.message) @@ -68,7 +76,7 @@ export async function getBody(): Promise { return "" } } else { - body = core.getInput("message", {required: false}) + body = messageInput } if (prefixInput || suffixInput) {