Skip to content

Commit f8701b1

Browse files
authored
Update comment-memory rendering to use six-backtick code regions (#28115)
1 parent 6dce746 commit f8701b1

5 files changed

Lines changed: 65 additions & 9 deletions

File tree

actions/setup/js/comment_memory.cjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const { buildWorkflowRunUrl } = require("./workflow_metadata_helpers.cjs");
1414
const { getTrackerID } = require("./get_tracker_id.cjs");
1515
const { generateHistoryUrl } = require("./generate_history_link.cjs");
1616
const { enforceCommentLimits } = require("./comment_limit_helpers.cjs");
17-
const { COMMENT_MEMORY_TAG, COMMENT_MEMORY_MAX_SCAN_PAGES } = require("./comment_memory_helpers.cjs");
17+
const { COMMENT_MEMORY_TAG, COMMENT_MEMORY_MAX_SCAN_PAGES, COMMENT_MEMORY_CODE_FENCE } = require("./comment_memory_helpers.cjs");
1818
// Require provenance marker to avoid accidentally updating user-authored comments
1919
// that happen to contain a matching comment-memory tag.
2020
const MANAGED_COMMENT_PROVENANCE_MARKER = "<!-- gh-aw-agentic-workflow:";
@@ -45,7 +45,7 @@ function buildManagedMemoryBody(rawBody, memoryID, options) {
4545
const openingTag = `<${COMMENT_MEMORY_TAG} id="${memoryID}">`;
4646
const closingTag = `</${COMMENT_MEMORY_TAG}>`;
4747
core.info(`comment_memory: building managed body for memory_id='${memoryID}'`);
48-
let body = `${MANAGED_COMMENT_HEADER}\n\n${openingTag}\n${sanitizeContent(rawBody)}\n${closingTag}`;
48+
let body = `${MANAGED_COMMENT_HEADER}\n\n${openingTag}\n${COMMENT_MEMORY_CODE_FENCE}\n${sanitizeContent(rawBody)}\n${COMMENT_MEMORY_CODE_FENCE}\n${closingTag}`;
4949

5050
const tracker = getTrackerID("markdown");
5151
if (tracker) {

actions/setup/js/comment_memory.test.cjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ describe("comment_memory", () => {
4545
});
4646
expect(body).toContain("### Comment Memory");
4747
expect(body).toContain('<gh-aw-comment-memory id="default">');
48+
expect(body).toContain("``````");
4849
expect(body).toContain("Hello world");
4950
expect(body).toContain("</gh-aw-comment-memory>");
5051
});
@@ -63,6 +64,7 @@ describe("comment_memory", () => {
6364
});
6465
expect(body).toContain("### Comment Memory");
6566
expect(body).toContain('<gh-aw-comment-memory id="session">');
67+
expect(body).toContain("``````");
6668
expect(body).toContain("Persist me");
6769
expect(body).toContain("> [!NOTE]");
6870
expect(body).toContain("> <summary>What this comment does</summary>");

actions/setup/js/comment_memory_helpers.cjs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,23 @@ const COMMENT_MEMORY_MAX_SCAN_PAGES = 50;
1111
const COMMENT_MEMORY_MAX_SCAN_EMPTY_PAGES = 5;
1212
const COMMENT_MEMORY_PROMPT_START_MARKER = "<!-- gh-aw-comment-memory-prompt:start -->";
1313
const COMMENT_MEMORY_PROMPT_END_MARKER = "<!-- gh-aw-comment-memory-prompt:end -->";
14+
const COMMENT_MEMORY_CODE_FENCE = "``````";
15+
const ESCAPED_COMMENT_MEMORY_CODE_FENCE = COMMENT_MEMORY_CODE_FENCE.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
16+
17+
function stripCommentMemoryCodeFence(content) {
18+
const trimmed = typeof content === "string" ? content.trim() : "";
19+
if (trimmed.length === 0) {
20+
return "";
21+
}
22+
if (!trimmed.startsWith(COMMENT_MEMORY_CODE_FENCE)) {
23+
return trimmed;
24+
}
25+
const match = trimmed.match(new RegExp(`^${ESCAPED_COMMENT_MEMORY_CODE_FENCE}[^\\n]*\\n([\\s\\S]*)\\n${ESCAPED_COMMENT_MEMORY_CODE_FENCE}$`));
26+
if (!match) {
27+
return trimmed;
28+
}
29+
return match[1].trim();
30+
}
1431

1532
function isSafeMemoryId(memoryId) {
1633
if (typeof memoryId !== "string" || memoryId.length === 0 || memoryId.length > MAX_MEMORY_ID_LENGTH) {
@@ -57,7 +74,7 @@ function extractCommentMemoryEntries(commentBody, warn = () => {}) {
5774
if (isSafeMemoryId(memoryId)) {
5875
entries.push({
5976
memoryId,
60-
content: (commentBody.slice(contentStart, closeStart) || "").trim(),
77+
content: stripCommentMemoryCodeFence(commentBody.slice(contentStart, closeStart)),
6178
});
6279
} else {
6380
warn(`skipping unsafe memory_id '${memoryId}'`);
@@ -99,7 +116,9 @@ module.exports = {
99116
COMMENT_MEMORY_MAX_SCAN_EMPTY_PAGES,
100117
COMMENT_MEMORY_PROMPT_START_MARKER,
101118
COMMENT_MEMORY_PROMPT_END_MARKER,
119+
COMMENT_MEMORY_CODE_FENCE,
102120
isSafeMemoryId,
121+
stripCommentMemoryCodeFence,
103122
extractCommentMemoryEntries,
104123
listCommentMemoryFiles,
105124
resolveCommentMemoryConfig,

actions/setup/js/comment_memory_helpers.test.cjs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,47 @@
11
import { describe, it, expect, vi } from "vitest";
2-
import { extractCommentMemoryEntries, isSafeMemoryId } from "./comment_memory_helpers.cjs";
2+
import { extractCommentMemoryEntries, isSafeMemoryId, stripCommentMemoryCodeFence } from "./comment_memory_helpers.cjs";
33

44
describe("comment_memory_helpers", () => {
55
it("extracts managed memory entries", () => {
6+
const entries = extractCommentMemoryEntries('<gh-aw-comment-memory id="default">\n``````\nhello\n``````\n</gh-aw-comment-memory>');
7+
expect(entries).toEqual([{ memoryId: "default", content: "hello" }]);
8+
});
9+
10+
it("supports legacy memory entries without code fence markers", () => {
611
const entries = extractCommentMemoryEntries('<gh-aw-comment-memory id="default">\nhello\n</gh-aw-comment-memory>');
712
expect(entries).toEqual([{ memoryId: "default", content: "hello" }]);
813
});
914

15+
it("keeps fenced text unchanged when trailing content exists after closing fence", () => {
16+
const content = "``````\nhello\n``````\ntrailing";
17+
expect(stripCommentMemoryCodeFence(content)).toBe(content);
18+
});
19+
20+
it("keeps fenced text unchanged when closing fence is missing", () => {
21+
const content = "``````\nhello";
22+
expect(stripCommentMemoryCodeFence(content)).toBe(content);
23+
});
24+
25+
it("keeps malformed fenced text unchanged", () => {
26+
const content = "``````hello\n``````";
27+
expect(stripCommentMemoryCodeFence(content)).toBe(content);
28+
});
29+
30+
it("strips valid fenced text with extra newlines before content", () => {
31+
const content = "``````\n\nhello\n``````";
32+
expect(stripCommentMemoryCodeFence(content)).toBe("hello");
33+
});
34+
35+
it("strips valid fenced text when content contains six-backtick lines", () => {
36+
const content = "``````\nline 1\n``````\nline 2\n``````";
37+
expect(stripCommentMemoryCodeFence(content)).toBe("line 1\n``````\nline 2");
38+
});
39+
40+
it("keeps fenced text unchanged when closing fence has no leading newline", () => {
41+
const content = "``````\nhello``````";
42+
expect(stripCommentMemoryCodeFence(content)).toBe(content);
43+
});
44+
1045
it("rejects unsafe memory IDs", () => {
1146
const warning = vi.fn();
1247
const entries = extractCommentMemoryEntries('<gh-aw-comment-memory id="../bad">\nhello\n</gh-aw-comment-memory>', warning);

actions/setup/js/setup_comment_memory_files.test.cjs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describe("setup_comment_memory_files", () => {
3535

3636
it("extracts memory entries from managed comment body", async () => {
3737
const module = await import("./setup_comment_memory_files.cjs");
38-
const entries = module.extractCommentMemoryEntries('<gh-aw-comment-memory id="default">\nhello\n</gh-aw-comment-memory>');
38+
const entries = module.extractCommentMemoryEntries('<gh-aw-comment-memory id="default">\n``````\nhello\n``````\n</gh-aw-comment-memory>');
3939
expect(entries).toEqual([{ memoryId: "default", content: "hello" }]);
4040
});
4141

@@ -47,7 +47,7 @@ describe("setup_comment_memory_files", () => {
4747
listComments: vi.fn().mockResolvedValue({
4848
data: [
4949
{
50-
body: '<gh-aw-comment-memory id="default">\nSaved memory\n</gh-aw-comment-memory>\nfooter',
50+
body: '<gh-aw-comment-memory id="default">\n``````\nSaved memory\n``````\n</gh-aw-comment-memory>\nfooter',
5151
},
5252
],
5353
}),
@@ -76,7 +76,7 @@ describe("setup_comment_memory_files", () => {
7676
});
7777
}
7878
if (page === 6) {
79-
return Promise.resolve({ data: [{ body: '<gh-aw-comment-memory id="default">\nLate memory\n</gh-aw-comment-memory>' }] });
79+
return Promise.resolve({ data: [{ body: '<gh-aw-comment-memory id="default">\n``````\nLate memory\n``````\n</gh-aw-comment-memory>' }] });
8080
}
8181
return Promise.resolve({ data: [] });
8282
});
@@ -156,7 +156,7 @@ describe("setup_comment_memory_files", () => {
156156
})
157157
);
158158
const listComments = vi.fn().mockResolvedValue({
159-
data: [{ body: '<gh-aw-comment-memory id="default">\nCross repo memory\n</gh-aw-comment-memory>' }],
159+
data: [{ body: '<gh-aw-comment-memory id="default">\n``````\nCross repo memory\n``````\n</gh-aw-comment-memory>' }],
160160
});
161161
global.github = {
162162
rest: {
@@ -192,7 +192,7 @@ describe("setup_comment_memory_files", () => {
192192
})
193193
);
194194
const listComments = vi.fn().mockResolvedValue({
195-
data: [{ body: '<gh-aw-comment-memory id="default">\nSame repo memory\n</gh-aw-comment-memory>' }],
195+
data: [{ body: '<gh-aw-comment-memory id="default">\n``````\nSame repo memory\n``````\n</gh-aw-comment-memory>' }],
196196
});
197197
global.github = {
198198
rest: {

0 commit comments

Comments
 (0)