fix: link repair targets to source repo
Resolve repair dashboard shorthand targets through the source repository, preserving explicit pull request URLs and inferring PR links for repair/merge actions.\n\nThanks @stainlu.
This commit is contained in:
parent
7fec36c228
commit
4411b564d4
@ -194,23 +194,49 @@ function runLink(record) {
|
|||||||
return record.run_url ? link(record.run_id ?? "run", record.run_url) : "_none_";
|
return record.run_url ? link(record.run_id ?? "run", record.run_url) : "_none_";
|
||||||
}
|
}
|
||||||
|
|
||||||
function targetLink(action) {
|
function targetLink(record, action) {
|
||||||
const target = String(action.target ?? "");
|
const target = String(action.target ?? "");
|
||||||
const match = target.match(/^https:\/\/github\.com\/([^/]+\/[^/]+)\/(issues|pull)\/(\d+)/);
|
const match = target.match(/^https:\/\/github\.com\/([^/]+\/[^/]+)\/(issues|pull)\/(\d+)/);
|
||||||
if (match) return link(`#${match[3]}`, target);
|
if (match) return link(`#${match[3]}`, target);
|
||||||
|
const shorthand = target.match(/^#(\d+)$/);
|
||||||
|
const repo = String(record.repo ?? "");
|
||||||
|
if (shorthand && /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repo)) {
|
||||||
|
const explicitUrl = githubItemUrlForNumber(action.url, shorthand[1]);
|
||||||
|
if (explicitUrl) return link(target, explicitUrl);
|
||||||
|
const segment = repairActionTargetsPullRequest(action) ? "pull" : "issues";
|
||||||
|
return link(target, `https://github.com/${repo}/${segment}/${shorthand[1]}`);
|
||||||
|
}
|
||||||
return target ? link(target, target) : "";
|
return target ? link(target, target) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function githubItemUrlForNumber(value, number) {
|
||||||
|
const url = String(value ?? "");
|
||||||
|
const match = url.match(/^https:\/\/github\.com\/[^/]+\/[^/]+\/(?:issues|pull)\/(\d+)$/);
|
||||||
|
return match?.[1] === number ? url : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function repairActionTargetsPullRequest(action) {
|
||||||
|
const actionName = String(action.action ?? "");
|
||||||
|
const classification = String(action.classification ?? "");
|
||||||
|
return (
|
||||||
|
actionName.startsWith("merge_") ||
|
||||||
|
actionName.includes("automerge") ||
|
||||||
|
actionName.includes("repair_contributor_branch") ||
|
||||||
|
classification === "canonical" ||
|
||||||
|
classification === "fix_pr"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function inspectionRow(row) {
|
function inspectionRow(row) {
|
||||||
return `| ${clusterLink(row.record)} | ${tableCell(row.state)} | ${truncate(row.reason, 150)} | ${clusterLink(row.record)} | ${runLink(row.record)} |`;
|
return `| ${clusterLink(row.record)} | ${tableCell(row.state)} | ${truncate(row.reason, 150)} | ${clusterLink(row.record)} | ${runLink(row.record)} |`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function fixRow(row) {
|
function fixRow(row) {
|
||||||
const action = row.action;
|
const action = row.action;
|
||||||
return `| ${clusterLink(row.record)} | ${tableCell(action.status)} | ${targetLink(action)} | ${tableCell(action.branch ?? action.pr ?? "")} | ${truncate(action.reason, 150)} | ${runLink(row.record)} |`;
|
return `| ${clusterLink(row.record)} | ${tableCell(action.status)} | ${targetLink(row.record, action)} | ${tableCell(action.branch ?? action.pr ?? "")} | ${truncate(action.reason, 150)} | ${runLink(row.record)} |`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeRow(row) {
|
function closeRow(row) {
|
||||||
const action = row.action;
|
const action = row.action;
|
||||||
return `| ${targetLink(action)} | ${tableCell(action.action)} | ${truncate(action.title ?? "")} | ${formatTimestamp(action.closed_at ?? action.merged_at ?? row.record.published_at)} | ${clusterLink(row.record)} | ${clusterLink(row.record)} | ${runLink(row.record)} |`;
|
return `| ${targetLink(row.record, action)} | ${tableCell(action.action)} | ${truncate(action.title ?? "")} | ${formatTimestamp(action.closed_at ?? action.merged_at ?? row.record.published_at)} | ${clusterLink(row.record)} | ${clusterLink(row.record)} | ${runLink(row.record)} |`;
|
||||||
}
|
}
|
||||||
|
|||||||
63
test/repair-dashboard.test.mjs
Normal file
63
test/repair-dashboard.test.mjs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
import path from "node:path";
|
||||||
|
import test from "node:test";
|
||||||
|
import { renderRepairDashboard } from "../scripts/repair-dashboard.mjs";
|
||||||
|
|
||||||
|
test("repair dashboard links shorthand targets through the source repo", () => {
|
||||||
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "clawsweeper-state-repair-"));
|
||||||
|
const runsDir = path.join(root, "results", "runs");
|
||||||
|
fs.mkdirSync(runsDir, { recursive: true });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(runsDir, "run.json"),
|
||||||
|
JSON.stringify({
|
||||||
|
repo: "openclaw/openclaw",
|
||||||
|
cluster_id: "repair-target-links",
|
||||||
|
run_id: "12345",
|
||||||
|
run_url: "https://github.com/openclaw/clawsweeper/actions/runs/12345",
|
||||||
|
workflow_conclusion: "success",
|
||||||
|
published_at: "2026-05-02T19:00:00.000Z",
|
||||||
|
fix_actions: [
|
||||||
|
{
|
||||||
|
target: "#789",
|
||||||
|
status: "blocked",
|
||||||
|
reason: "needs inspection",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "#901",
|
||||||
|
action: "repair_contributor_branch",
|
||||||
|
status: "blocked",
|
||||||
|
reason: "needs branch repair",
|
||||||
|
url: "https://github.com/openclaw/openclaw/pull/901",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
apply_actions: [
|
||||||
|
{
|
||||||
|
target: "#123",
|
||||||
|
action: "close_duplicate",
|
||||||
|
status: "executed",
|
||||||
|
title: "duplicate report",
|
||||||
|
closed_at: "2026-05-02T19:01:00.000Z",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "https://github.com/openclaw/openclaw/pull/456",
|
||||||
|
action: "close_superseded",
|
||||||
|
status: "executed",
|
||||||
|
title: "superseded PR",
|
||||||
|
closed_at: "2026-05-02T19:00:00.000Z",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
const dashboard = renderRepairDashboard(root);
|
||||||
|
|
||||||
|
assert.match(dashboard, /\[#123\]\(https:\/\/github\.com\/openclaw\/openclaw\/issues\/123\)/);
|
||||||
|
assert.match(dashboard, /\[#789\]\(https:\/\/github\.com\/openclaw\/openclaw\/issues\/789\)/);
|
||||||
|
assert.match(dashboard, /\[#456\]\(https:\/\/github\.com\/openclaw\/openclaw\/pull\/456\)/);
|
||||||
|
assert.match(dashboard, /\[#901\]\(https:\/\/github\.com\/openclaw\/openclaw\/pull\/901\)/);
|
||||||
|
assert.doesNotMatch(dashboard, /\[#123\]\(#123\)/);
|
||||||
|
assert.doesNotMatch(dashboard, /\[#789\]\(#789\)/);
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user