


































@@ -1,9 +1,7 @@
11#!/usr/bin/env node
2-import { execFileSync, spawnSync } from "node:child_process";
2+import { execFileSync } from "node:child_process";
33import {
4-copyFileSync,
54existsSync,
6-mkdirSync,
75mkdtempSync,
86readFileSync,
97rmSync,
@@ -121,86 +119,12 @@ export function loadEvidenceManifest(manifestPath) {
121119};
122120}
123121124-function encodePathForUrl(input) {
125-return input
126-.split("/")
127-.filter(Boolean)
128-.map((part) => encodeURIComponent(part))
129-.join("/");
130-}
131-132-function artifactUrl(rawBase, artifact) {
133-return `${rawBase}/${encodePathForUrl(artifact.targetPath)}`;
134-}
135-136-function byLane(artifacts, kind) {
137-const lanes = new Map();
138-for (const artifact of artifacts) {
139-if (artifact.kind !== kind) {
140-continue;
141-}
142-lanes.set(artifact.lane, artifact);
143-}
144-return lanes;
145-}
146-147-function findPair(artifacts, kind, leftLane, rightLane) {
148-const lanes = byLane(artifacts, kind);
149-const left = lanes.get(leftLane);
150-const right = lanes.get(rightLane);
151-return left && right ? { left, right } : null;
152-}
153-154-function renderPairTable({ pair, rawBase }) {
155-const { left, right } = pair;
156-if (!left || !right) {
157-return "";
158-}
159-return [
160-'<table width="100%">',
161-" <thead>",
162-" <tr>",
163-` <th width="50%">${left.label}</th>`,
164-` <th width="50%">${right.label}</th>`,
165-" </tr>",
166-" </thead>",
167-" <tbody>",
168-" <tr>",
169-` <td width="50%" align="center"><img src="${artifactUrl(rawBase, left)}" width="100%" alt="${left.alt ?? left.label}"></td>`,
170-` <td width="50%" align="center"><img src="${artifactUrl(rawBase, right)}" width="100%" alt="${right.alt ?? right.label}"></td>`,
171-" </tr>",
172-" </tbody>",
173-"</table>",
174-"",
175-].join("\n");
176-}
177-178-function renderSingleImageTables({ artifacts, rawBase, pairedKeys }) {
179-const renderedPairs = new Set(pairedKeys);
180-return artifacts
181-.filter(
182-(artifact) => artifact.inline && !renderedPairs.has(`${artifact.kind}:${artifact.lane}`),
183-)
184-.map((artifact) => {
185-const width = Math.min(Number(artifact.width ?? 720) || 720, 900);
186-return [
187-`**${artifact.label}**`,
188-"",
189-`<img src="${artifactUrl(rawBase, artifact)}" width="${width}" alt="${artifact.alt ?? artifact.label}">`,
190-"",
191-].join("\n");
192-})
193-.join("\n");
194-}
195-196-function renderLinkList({ artifacts, kind, rawBase, title }) {
197-const links = artifacts
198-.filter((artifact) => artifact.kind === kind)
199-.map((artifact) => `- [${artifact.label}](${artifactUrl(rawBase, artifact)})`);
122+function renderArtifactFileList(artifacts) {
123+const links = artifacts.map((artifact) => `- ${artifact.label}: \`${artifact.targetPath}\``);
200124if (links.length === 0) {
201125return "";
202126}
203-return [`${title}:`, ...links, ""].join("\n");
127+return ["Artifact files:", ...links, ""].join("\n");
204128}
205129206130function laneLine(label, lane) {
@@ -220,27 +144,15 @@ function laneLine(label, lane) {
220144}
221145222146export function renderEvidenceComment({
223- artifactRoot,
224147artifactUrl: actionsArtifactUrl,
225148 manifest,
226149 marker,
227- rawBase,
228150 requestSource,
229151 runUrl,
230- treeUrl,
231152}) {
232153const comparison = manifest.comparison ?? {};
233154const baseline = comparison.baseline;
234155const candidate = comparison.candidate;
235-const pairs = [
236-findPair(manifest.artifacts, "timeline", "baseline", "candidate"),
237-findPair(manifest.artifacts, "desktopScreenshot", "baseline", "candidate"),
238-findPair(manifest.artifacts, "motionPreview", "baseline", "candidate"),
239-].filter(Boolean);
240-const pairedKeys = pairs.flatMap((pair) => [
241-`${pair.left.kind}:${pair.left.lane}`,
242-`${pair.right.kind}:${pair.right.lane}`,
243-]);
244156const lines = [
245157marker,
246158`## ${manifest.title}`,
@@ -270,39 +182,8 @@ export function renderEvidenceComment({
270182lines.push(`- Overall: \`${comparison.pass}\``);
271183}
272184lines.push("");
273-274-const pairedSections = pairs.map((pair) => renderPairTable({ pair, rawBase }));
275-276-lines.push(...pairedSections);
277-const singleTables = renderSingleImageTables({
278-artifacts: manifest.artifacts,
279- pairedKeys,
280- rawBase,
281-});
282-if (singleTables) {
283-lines.push(singleTables);
284-}
285-const motionClips = renderLinkList({
286-artifacts: manifest.artifacts,
287-kind: "motionClip",
288- rawBase,
289-title: "Motion-trimmed clips",
290-});
291-if (motionClips) {
292-lines.push(motionClips);
293-}
294-const fullVideos = renderLinkList({
295-artifacts: manifest.artifacts,
296-kind: "fullVideo",
297- rawBase,
298-title: "Full videos",
299-});
300-if (fullVideos) {
301-lines.push(fullVideos);
302-}
303-lines.push(
304-`Raw QA files: ${treeUrl ?? `https://github.com/${process.env.GITHUB_REPOSITORY}/tree/qa-artifacts/${artifactRoot}`}`,
305-);
185+lines.push(renderArtifactFileList(manifest.artifacts));
186+lines.push(`Raw QA files: ${actionsArtifactUrl}`);
306187return `${lines.join("\n").replace(/\n{3,}/gu, "\n\n")}\n`;
307188}
308189@@ -314,77 +195,6 @@ function run(command, args, options = {}) {
314195});
315196}
316197317-function runStatus(command, args, options = {}) {
318-const result = spawnSync(command, args, {
319-stdio: "ignore",
320- ...options,
321-});
322-if (result.error) {
323-throw result.error;
324-}
325-return result.status ?? 1;
326-}
327-328-function publishArtifactFiles({ artifactRoot, ghToken, manifest, repo }) {
329-const worktree = mkdtempSync(path.join(tmpdir(), "mantis-qa-artifacts-"));
330-const safeArtifactRoot = normalizeTargetPath(artifactRoot);
331-try {
332-run("git", ["init", "--quiet", worktree]);
333-run("git", ["-C", worktree, "config", "user.name", "github-actions[bot]"]);
334-run("git", [
335-"-C",
336-worktree,
337-"config",
338-"user.email",
339-"41898282+github-actions[bot]@users.noreply.github.com",
340-]);
341-run("git", [
342-"-C",
343-worktree,
344-"remote",
345-"add",
346-"origin",
347-`https://x-access-token:${ghToken}@github.com/${repo}.git`,
348-]);
349-try {
350-run("git", ["-C", worktree, "fetch", "--quiet", "origin", "qa-artifacts"]);
351-run("git", ["-C", worktree, "checkout", "--quiet", "-B", "qa-artifacts", "FETCH_HEAD"]);
352-} catch {
353-run("git", ["-C", worktree, "checkout", "--quiet", "--orphan", "qa-artifacts"]);
354-}
355-356-const destinationRoot = path.join(worktree, safeArtifactRoot);
357-for (const artifact of manifest.artifacts) {
358-const destination = assertInside(
359-destinationRoot,
360-path.resolve(destinationRoot, artifact.targetPath),
361-`Artifact target ${artifact.targetPath}`,
362-);
363-mkdirSync(path.dirname(destination), { recursive: true });
364-copyFileSync(artifact.source, destination);
365-}
366-367-run("git", ["-C", worktree, "add", safeArtifactRoot]);
368-const hasChanges = runStatus("git", ["-C", worktree, "diff", "--cached", "--quiet"]) !== 0;
369-if (hasChanges) {
370-run("git", [
371-"-C",
372-worktree,
373-"commit",
374-"--quiet",
375-"-m",
376-`qa: publish Mantis evidence for ${manifest.id}`,
377-]);
378-run("git", ["-C", worktree, "push", "--quiet", "origin", "HEAD:qa-artifacts"]);
379-} else {
380-console.log("No QA evidence artifact changes to publish.");
381-}
382-} finally {
383-rmSync(worktree, { force: true, recursive: true });
384-}
385-return safeArtifactRoot;
386-}
387-388198function upsertPrComment({ body, marker, prNumber, repo }) {
389199run("gh", ["api", `repos/${repo}/pulls/${prNumber}`, "--jq", ".number"]);
390200const commentId = run("gh", [
@@ -446,25 +256,18 @@ export function publishEvidence(rawArgs = process.argv.slice(2)) {
446256if (!ghToken) {
447257throw new Error("Missing GH_TOKEN or GITHUB_TOKEN.");
448258}
259+if (!args.artifact_url) {
260+throw new Error("Missing --artifact-url. Mantis evidence must use Actions artifacts, not Git.");
261+}
449262450263const manifest = loadEvidenceManifest(args.manifest);
451-const artifactRoot = publishArtifactFiles({
452-artifactRoot: args.artifact_root,
453- ghToken,
454- manifest,
455- repo,
456-});
457-const rawBase = `https://raw.githubusercontent.com/${repo}/qa-artifacts/${encodePathForUrl(artifactRoot)}`;
458-const treeUrl = `https://github.com/${repo}/tree/qa-artifacts/${encodePathForUrl(artifactRoot)}`;
264+normalizeTargetPath(args.artifact_root);
459265const body = renderEvidenceComment({
460- artifactRoot,
461266artifactUrl: args.artifact_url,
462267 manifest,
463268marker: args.marker,
464- rawBase,
465269requestSource: args.request_source,
466270runUrl: args.run_url,
467- treeUrl,
468271});
469272upsertPrComment({
470273 body,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。