This commit is contained in:
Copilot 2026-04-06 16:02:55 +09:00 committed by GitHub
commit d170965535
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 87 additions and 7 deletions

View file

@ -112,6 +112,22 @@ with:
path: path-to-comment-contents.txt 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 ### Delete the previous comment and add a comment at the end
```yaml ```yaml
@ -220,11 +236,11 @@ For more detailed information about permissions, you can read from the link belo
### `message` ### `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` ### `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` ### `number`

View file

@ -210,4 +210,61 @@ describe("getBody", () => {
expect(await config.getBody()).toBe("") expect(await config.getBody()).toBe("")
expect(core.setFailed).toHaveBeenCalledWith("glob error") 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```")
})
}) })

View file

@ -52,10 +52,10 @@ 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
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

@ -50,20 +50,27 @@ export async function getBody(): Promise<string> {
const followSymbolicLinks = core.getBooleanInput("follow_symbolic_links", { const followSymbolicLinks = core.getBooleanInput("follow_symbolic_links", {
required: true, required: true,
}) })
const messageInput = core.getInput("message", {required: false})
if (pathInput && pathInput.length > 0) { if (pathInput && pathInput.length > 0) {
try { try {
const globber = await create(pathInput.join("\n"), { const globber = await create(pathInput.join("\n"), {
followSymbolicLinks, followSymbolicLinks,
matchDirectories: false, 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) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
core.setFailed(error.message) core.setFailed(error.message)
} }
return "" return ""
} }
} else {
return core.getInput("message", {required: false})
} }
return messageInput
} }