feat: support combining path and message inputs using $path placeholder

Co-authored-by: marocchino <128431+marocchino@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-03-13 13:07:07 +00:00
parent 5b837e833d
commit 981267b215
3 changed files with 69 additions and 4 deletions

View file

@ -255,4 +255,61 @@ describe("getBody", () => {
}) })
expect(await config.getBody()).toBe("hello there\n```") 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```")
})
}) })

View file

@ -52,7 +52,7 @@ inputs:
default: "OUTDATED" default: "OUTDATED"
required: false required: false
message: 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 required: false
prefix: prefix:
description: "text to prepend to the message (e.g. ` ``` ` to open a code block)" 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)" description: "text to append to the message (e.g. ` ``` ` to close a code block)"
required: false required: false
path: 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 required: false
ignore_empty: ignore_empty:
description: "Indicates whether to ignore missing or empty messages" description: "Indicates whether to ignore missing or empty messages"

View file

@ -52,6 +52,7 @@ export async function getBody(): Promise<string> {
}) })
const prefixInput = core.getInput("prefix", {required: false}) const prefixInput = core.getInput("prefix", {required: false})
const suffixInput = core.getInput("suffix", {required: false}) const suffixInput = core.getInput("suffix", {required: false})
const messageInput = core.getInput("message", {required: false})
let body: string let body: string
if (pathInput && pathInput.length > 0) { if (pathInput && pathInput.length > 0) {
@ -60,7 +61,14 @@ export async function getBody(): Promise<string> {
followSymbolicLinks, followSymbolicLinks,
matchDirectories: false, 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) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
core.setFailed(error.message) core.setFailed(error.message)
@ -68,7 +76,7 @@ export async function getBody(): Promise<string> {
return "" return ""
} }
} else { } else {
body = core.getInput("message", {required: false}) body = messageInput
} }
if (prefixInput || suffixInput) { if (prefixInput || suffixInput) {