Read declared issue versions from visible release-link text

A version supplied as a Markdown release link currently becomes needs-version-info even though its visible label states the version. Read only that label and ordinary inline formatting; never mine destinations, image text or literal Markdown for a version. Keep current-report checks, silent metadata deltas and Community labels unchanged.

Contract-Neutral: Recognise visible inline version values within the existing issue metadata contract; no product runtime, API or label ownership changes.
Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-10-23 23:49:45 +01:00
parent acd353f30f
commit d972257ce1
2 changed files with 76 additions and 1 deletions
+19 -1
View File
@@ -139,8 +139,26 @@ function normalizeVersion(value) {
return match ? match[1] : null;
}
function inlineVersionText(tokens) {
return (tokens || []).map((token) => {
// A release link's label is evidence; its destination and title are not.
// Unwrap only ordinary inline formatting, not images, struck-out values
// or HTML. Code spans keep their literal text rather than parsing links
// or comments inside them.
if (["link", "strong", "em"].includes(token.type)) {
return inlineVersionText(token.tokens);
}
if (["codespan", "escape"].includes(token.type)) return token.text;
return token.raw || "";
}).join("");
}
function normalizeLegacyVersionValue(value) {
const visible = String(value).trim().replace(/^[`*_]+/, "");
const raw = String(value).trim();
const blocks = lexer(raw, { gfm: true });
const text = blocks.length === 1 && blocks[0].type === "paragraph"
? inlineVersionText(blocks[0].tokens) : raw;
const visible = text.trim().replace(/^[`*_]+/, "");
// Failed-install forms explicitly accept an attempted server archive. Its
// packaging suffix is not part of the release version. Recognise only the
// server archive names; companion binaries and signatures cannot establish
@@ -1774,3 +1774,60 @@ for (const [filename, version] of [
assert.equal(calls.getIssue.length, 2);
});
}
test("declared versions use visible release-link text, including nested inline formatting", () => {
const { extractPulseVersion } = triage.internals;
for (const [value, expected] of [
["[v6.5.0](https://github.com/rcourtman/Pulse/releases/tag/v6.4.5)", "6.5.0"],
["**[6.5.0](https://github.com/rcourtman/Pulse/releases/tag/v6.5.0)**", "6.5.0"],
["_[`v6.5.0`](https://github.com/rcourtman/Pulse/releases/tag/v6.5.0)_", "6.5.0"],
["[6\\.5\\.0](https://github.com/rcourtman/Pulse/releases/tag/v6.5.0)", "6.5.0"],
["[pulse-v6.6.0-beta.3-linux-amd64.tar.gz](https://github.com/rcourtman/Pulse/releases/tag/v6.6.0-beta.3)", "6.6.0-beta.3"],
]) {
for (const body of [
`### Pulse version\n${value}\n### Agent version\n6.4.5`,
`Pulse version: ${value}`,
`- **Pulse version**: ${value}`,
]) {
for (const newline of ["\n", "\r\n"]) {
assert.equal(extractPulseVersion("Upgrade from v6.4.5", body.replaceAll("\n", newline)), expected, body);
}
}
}
});
test("link targets, titles, images and literal Markdown cannot invent a declared version", () => {
const { extractPulseVersion } = triage.internals;
for (const value of [
"[unknown](https://github.com/rcourtman/Pulse/releases/tag/v6.5.0)",
'[unknown](https://github.com/rcourtman/Pulse "6.5.0")',
"![6.5.0](https://github.com/rcourtman/Pulse/releases/tag/v6.5.0)",
"~~[6.5.0](https://github.com/rcourtman/Pulse/releases/tag/v6.5.0)~~",
"`[6.5.0](https://github.com/rcourtman/Pulse/releases/tag/v6.5.0)`",
"<span>6.5.0</span>",
"[release](https://github.com/rcourtman/Pulse/releases/tag/v6.5.0) 6.4.5",
"[6.5.0](unterminated",
]) {
assert.equal(extractPulseVersion("Upgrade from v6.4.5",
`### Pulse version\n${value}\n### Agent version\n6.5.0`), null, value);
}
});
test("the label consumer accepts a linked version without fetching links or contacting the reporter", async () => {
for (const [visible, expected] of [["**`v6.5.0`**", "6.5.0"], ["unknown", null]]) {
const { github, calls } = createGithub();
const issue = { number: 2922, title: "[Bug]: Upgrade from v6.4.5",
body: `### Pulse version\n[${visible}](https://github.com/rcourtman/Pulse/releases/tag/v6.6.0-rc.5)\n### Agent version\n6.4.5`,
labels: ["bug", "affects-6.4.5", "needs-version-info", "needs-human", "release-blocker",
"needs-decomposition", "needs-retest-on-latest"].map(name => ({ name })) };
await syncLabels({ github, context: createContext({ issue }), core: createCore() });
assert.deepEqual(calls.addLabels.map(call => call.labels), expected ? [["affects-6.5.0"]] : []);
assert.deepEqual(calls.removeLabel.map(call => call.name), expected
? ["affects-6.4.5", "needs-version-info"] : ["affects-6.4.5"]);
assert.deepEqual(calls.createLabel.map(call => call.name), expected ? ["affects-6.5.0"] : ["needs-version-info"]);
assert.deepEqual(calls.createComment, []);
assert.deepEqual(calls.getLatestRelease, []);
assert.deepEqual(calls.paginate, []);
assert.equal(calls.getIssue.length, 2);
}
});