Wrap --marker argument in HTML comment tags automatically

This commit is contained in:
Mario Campos
2026-10-29 10:34:12 -05:00
parent 19e1ab5452
commit 9359b6b571
4 changed files with 31 additions and 26 deletions

View File

@@ -3,8 +3,8 @@ description: "Create or update a sticky PR comment identified by a caller-provid
inputs:
marker:
description: >
Non-empty marker identifying the comment. Use a unique,
stable HTML comment for each distinct comment.
Non-empty plain-text identifier for the comment. Use a unique, stable value
for each distinct comment; it is automatically wrapped in a hidden HTML comment.
required: true
body:
description: "Full comment markdown."

View File

@@ -295,7 +295,7 @@ jobs:
!startsWith(github.event.pull_request.head.ref, 'update-bundle/')
uses: ./.github/actions/post-comment
with:
marker: "<!-- post-comment:CHANGELOG.md -->"
marker: "post-comment:CHANGELOG.md"
body: |
⚠️ `CHANGELOG.md` has been modified in this PR. Please do not modify `CHANGELOG.md` directly.
Instead, create a change-note file in `unreleased-change-notes/`. The `CHANGELOG.md`
@@ -312,7 +312,7 @@ jobs:
!startsWith(github.event.pull_request.head.ref, 'update-bundle/')
uses: ./.github/actions/post-comment
with:
marker: "<!-- post-comment:CHANGELOG.md -->"
marker: "post-comment:CHANGELOG.md"
body: |
⚠️ `CHANGELOG.md` has been modified in this PR. Please do not modify `CHANGELOG.md` directly.
Instead, create a change-note file in `unreleased-change-notes/`. The `CHANGELOG.md`

View File

@@ -20,7 +20,7 @@ const validFlags = [
"--body",
"hello",
"--marker",
"<!-- marker -->",
"marker",
"--issue-id",
"42",
"--repository",
@@ -48,7 +48,7 @@ describe("parseOptions", async () => {
const options = parseOptions(validFlags);
assert.deepEqual(options, {
body: "hello",
marker: "<!-- marker -->",
marker: "marker",
issueId: 42,
repository: { owner: "github", repo: "codeql-action" },
});
@@ -136,7 +136,7 @@ describe("findExistingComment", async () => {
client,
{ owner: "owner", repo: "repo" },
1,
"<!-- marker -->",
"marker",
);
assert.equal(id, 2);
});
@@ -147,7 +147,7 @@ describe("findExistingComment", async () => {
client,
{ owner: "owner", repo: "repo" },
1,
"<!-- marker -->",
"marker",
);
assert.equal(id, undefined);
});
@@ -156,7 +156,7 @@ describe("findExistingComment", async () => {
/** A baseline set of `Options`, for use in `setComment` tests. */
const baseOptions: Options = {
body: "hello",
marker: "<!-- marker -->",
marker: "marker",
issueId: 42,
repository: { owner: "owner", repo: "repo" },
};
@@ -166,16 +166,16 @@ function assertCommentContent(
marker: string,
body: string,
): void {
assert.ok(comment.body?.startsWith(marker));
assert.ok(comment.body?.startsWith(`<!-- ${marker} -->`));
assert.ok(comment.body?.endsWith(body));
}
describe("setComment", async () => {
await it("uses the supplied marker without affecting other comments", async () => {
const marker = "<!-- custom-warning -->";
const marker = "custom-warning";
const { client, comments } = fakeClient([
{ id: 1, body: `${baseOptions.marker}\nunrelated warning` },
{ id: 2, body: `${marker}\nold warning` },
{ id: 1, body: "<!-- marker -->\nunrelated warning" },
{ id: 2, body: "<!-- custom-warning -->\nold warning" },
]);
const options = parseOptions(withFlag("--marker", marker));
@@ -185,7 +185,7 @@ describe("setComment", async () => {
assert.equal(comments.length, 2);
assert.deepEqual(comments[0], {
id: 1,
body: `${baseOptions.marker}\nunrelated warning`,
body: "<!-- marker -->\nunrelated warning",
});
assert.equal(comments[1].id, 2);
assertCommentContent(comments[1], marker, options.body);
@@ -196,7 +196,7 @@ describe("setComment", async () => {
// No comment has the marker before posting.
assert.equal(comments.length, 0);
assert.ok(
!comments.some(({ body }) => body?.startsWith(baseOptions.marker)),
!comments.some(({ body }) => body?.startsWith("<!-- marker -->")),
);
await setComment(client, baseOptions);
@@ -214,7 +214,7 @@ describe("setComment", async () => {
const { client, comments } = fakeClient(originalComments);
// Existing comments do not match the marker.
assert.ok(
!comments.some(({ body }) => body?.startsWith(baseOptions.marker)),
!comments.some(({ body }) => body?.startsWith("<!-- marker -->")),
);
await setComment(client, baseOptions);
@@ -226,7 +226,7 @@ describe("setComment", async () => {
originalComments,
);
const markedComments = comments.filter(({ body }) =>
body?.startsWith(baseOptions.marker),
body?.startsWith("<!-- marker -->"),
);
// Exactly one comment contains the expected marker and body.
assert.equal(markedComments.length, 1);
@@ -241,7 +241,7 @@ describe("setComment", async () => {
await it("updates the existing comment when one is found", async () => {
const { client, comments } = fakeClient([
{ id: 7, body: `${baseOptions.marker}\nold` },
{ id: 7, body: "<!-- marker -->\nold" },
]);
await setComment(client, baseOptions);
@@ -254,7 +254,7 @@ describe("setComment", async () => {
await it("replaces a warning with the resolved state using the same marker", async () => {
const { client, comments } = fakeClient([
{ id: 9, body: `${baseOptions.marker}\nold` },
{ id: 9, body: "<!-- marker -->\nold" },
]);
const body =
"`CHANGELOG.md` is not modified in this PR. No changelog action is needed.";

View File

@@ -3,14 +3,14 @@
/*
Creates or updates a pull request comment identified by a marker. The marker
is supplied via `--marker` (or the `.github/actions/post-comment` action's `marker` input)
and must be non-empty. The marker is prepended to `--body` and used to find
the comment on later runs.
and must be non-empty. Supply a plain-text identifier; the CLI wraps it in a
hidden HTML comment, prepends it to `--body`, and uses it to find the comment on later runs.
Example:
```sh
GH_TOKEN=<token> npx tsx ./pr-checks/post-comment.ts \
--body "The changelog needs updating." \
--marker "<!-- my-bot -->" \
--marker "my-bot" \
--issue-id 123 \
--repository github/codeql-action
```
@@ -27,7 +27,7 @@ import { type ApiClient, getApiClient, resolveToken } from "./api-client";
export interface Options {
/** The full comment body. */
body: string;
/** The hidden HTML-comment marker used to find a pre-existing comment. */
/** Plain-text identifier wrapped in a hidden HTML comment. */
marker: string;
/** The issue/pull request number to post/update the comment on. */
issueId: number;
@@ -35,6 +35,10 @@ export interface Options {
repository: RepositoryNwo;
}
function hiddenMarker(marker: string): string {
return `<!-- ${marker} -->`;
}
/** Parses CLI arguments supplied by the composite action into `Options`. */
export function parseOptions(argv?: string[]): Options {
const { values } = parseArgs({
@@ -72,17 +76,18 @@ export async function findExistingComment(
issueNumber: number,
marker: string,
): Promise<number | undefined> {
const formattedMarker = hiddenMarker(marker);
const comments = await client.paginate(client.rest.issues.listComments, {
...repository,
issue_number: issueNumber,
per_page: 100,
});
const markedComments = comments.filter((comment) =>
comment.body?.startsWith(marker),
comment.body?.startsWith(formattedMarker),
);
if (markedComments.length > 1) {
console.warn(
`Found ${markedComments.length} comments with marker "${marker}". ` +
`Found ${markedComments.length} comments with marker "${formattedMarker}". ` +
"Updating the first one and leaving the others unchanged." +
"Use a unique marker for each distinct managed comment to avoid this situation.",
);
@@ -98,7 +103,7 @@ export async function setComment(
options: Options,
): Promise<void> {
const { issueId, marker, repository } = options;
const body = `${marker}\n${options.body}`;
const body = `${hiddenMarker(marker)}\n${options.body}`;
const existingCommentId = await findExistingComment(
client,