@@ -474,6 +474,40 @@ jobs:
474474 echo "- Candidate desktop video: \`candidate/discord-status-reactions-tool-only-desktop.mp4\`"
475475 } > "$root/mantis-report.md"
476476
477+ jq -n \
478+ --arg baseline_status "$baseline_status" \
479+ --arg candidate_status "$candidate_status" \
480+ --arg baseline_sha "${{ needs.validate_refs.outputs.baseline_revision }}" \
481+ --arg candidate_sha "${{ needs.validate_refs.outputs.candidate_revision }}" \
482+ '{
483+ schemaVersion: 1,
484+ id: "discord-status-reactions",
485+ title: "Mantis Discord Status Reactions QA",
486+ summary: "Mantis reran Discord status reactions against the known queued-only baseline and the candidate ref. The baseline reproduced the bug, while the candidate showed the expected queued -> thinking -> done reaction sequence.",
487+ scenario: "discord-status-reactions-tool-only",
488+ comparison: {
489+ baseline: { sha: $baseline_sha, expected: "queued-only", status: $baseline_status, reproduced: ($baseline_status == "fail") },
490+ candidate: { sha: $candidate_sha, expected: "queued -> thinking -> done", status: $candidate_status, fixed: ($candidate_status == "pass") },
491+ pass: (($baseline_status == "fail") and ($candidate_status == "pass"))
492+ },
493+ artifacts: [
494+ { kind: "timeline", lane: "baseline", label: "Baseline queued-only", path: "baseline/discord-status-reactions-tool-only-timeline.png", targetPath: "baseline.png", alt: "Baseline Discord status reaction timeline", width: 420 },
495+ { kind: "timeline", lane: "candidate", label: "Candidate queued -> thinking -> done", path: "candidate/discord-status-reactions-tool-only-timeline.png", targetPath: "candidate.png", alt: "Candidate Discord status reaction timeline", width: 420 },
496+ { kind: "desktopScreenshot", lane: "baseline", label: "Baseline desktop/VNC browser", path: "baseline/discord-status-reactions-tool-only-desktop.png", targetPath: "baseline-desktop.png", alt: "Baseline Mantis desktop browser screenshot", width: 420 },
497+ { kind: "desktopScreenshot", lane: "candidate", label: "Candidate desktop/VNC browser", path: "candidate/discord-status-reactions-tool-only-desktop.png", targetPath: "candidate-desktop.png", alt: "Candidate Mantis desktop browser screenshot", width: 420 },
498+ { kind: "motionPreview", lane: "baseline", label: "Baseline motion preview", path: "baseline/discord-status-reactions-tool-only-desktop-preview.gif", targetPath: "baseline-desktop-preview.gif", alt: "Animated baseline desktop preview", width: 420, required: false },
499+ { kind: "motionPreview", lane: "candidate", label: "Candidate motion preview", path: "candidate/discord-status-reactions-tool-only-desktop-preview.gif", targetPath: "candidate-desktop-preview.gif", alt: "Animated candidate desktop preview", width: 420, required: false },
500+ { kind: "motionClip", lane: "baseline", label: "Baseline change MP4", path: "baseline/discord-status-reactions-tool-only-desktop-change.mp4", targetPath: "baseline-desktop-change.mp4", required: false },
501+ { kind: "motionClip", lane: "candidate", label: "Candidate change MP4", path: "candidate/discord-status-reactions-tool-only-desktop-change.mp4", targetPath: "candidate-desktop-change.mp4", required: false },
502+ { kind: "fullVideo", lane: "baseline", label: "Baseline desktop MP4", path: "baseline/discord-status-reactions-tool-only-desktop.mp4", targetPath: "baseline-desktop.mp4" },
503+ { kind: "fullVideo", lane: "candidate", label: "Candidate desktop MP4", path: "candidate/discord-status-reactions-tool-only-desktop.mp4", targetPath: "candidate-desktop.mp4" },
504+ { kind: "metadata", lane: "baseline", label: "Baseline preview metadata", path: "baseline/discord-status-reactions-tool-only-desktop-preview.json", targetPath: "baseline-desktop-preview.json", required: false },
505+ { kind: "metadata", lane: "candidate", label: "Candidate preview metadata", path: "candidate/discord-status-reactions-tool-only-desktop-preview.json", targetPath: "candidate-desktop-preview.json", required: false },
506+ { kind: "metadata", lane: "run", label: "Comparison JSON", path: "comparison.json", targetPath: "comparison.json" },
507+ { kind: "report", lane: "run", label: "Mantis report", path: "mantis-report.md", targetPath: "mantis-report.md" }
508+ ]
509+ }' > "$root/mantis-evidence.json"
510+
477511 cat "$root/mantis-report.md" >> "$GITHUB_STEP_SUMMARY"
478512
479513 if [[ "$baseline_status" != "fail" ]]; then
@@ -514,155 +548,17 @@ jobs:
514548 GH_TOKEN : ${{ steps.mantis_app_token.outputs.token }}
515549 TARGET_PR : ${{ needs.resolve_request.outputs.pr_number }}
516550 ARTIFACT_URL : ${{ steps.upload_artifact.outputs.artifact-url }}
517- BASELINE_SHA : ${{ needs.validate_refs.outputs.baseline_revision }}
518- CANDIDATE_SHA : ${{ needs.validate_refs.outputs.candidate_revision }}
519551 REQUEST_SOURCE : ${{ needs.resolve_request.outputs.request_source }}
520552 shell : bash
521553 run : |
522554 set -euo pipefail
523555
524- if [[ ! "$TARGET_PR" =~ ^[0-9]+$ ]]; then
525- echo "pr_number must be numeric, got '${TARGET_PR}'." >&2
526- exit 1
527- fi
528-
529556 root=".artifacts/qa-e2e/mantis/discord-status-reactions"
530- for required in \
531- "$root/comparison.json" \
532- "$root/baseline/discord-status-reactions-tool-only-timeline.png" \
533- "$root/candidate/discord-status-reactions-tool-only-timeline.png" \
534- "$root/baseline/discord-status-reactions-tool-only-desktop.png" \
535- "$root/candidate/discord-status-reactions-tool-only-desktop.png" \
536- "$root/baseline/discord-status-reactions-tool-only-desktop.mp4" \
537- "$root/candidate/discord-status-reactions-tool-only-desktop.mp4"
538- do
539- if [[ ! -f "$required" ]]; then
540- echo "Missing required QA evidence file: $required" >&2
541- exit 1
542- fi
543- done
544-
545- gh api "repos/${GITHUB_REPOSITORY}/pulls/${TARGET_PR}" --jq '.number' >/dev/null
546-
547- artifact_root="mantis/discord-status-reactions/pr-${TARGET_PR}/run-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
548- artifacts_worktree="$(mktemp -d)"
549- git init --quiet "$artifacts_worktree"
550- git -C "$artifacts_worktree" config user.name "github-actions[bot]"
551- git -C "$artifacts_worktree" config user.email "41898282+github-actions[bot]@users.noreply.github.com"
552- git -C "$artifacts_worktree" remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
553-
554- if git -C "$artifacts_worktree" fetch --quiet origin qa-artifacts; then
555- git -C "$artifacts_worktree" checkout --quiet -B qa-artifacts FETCH_HEAD
556- else
557- git -C "$artifacts_worktree" checkout --quiet --orphan qa-artifacts
558- fi
559-
560- mkdir -p "$artifacts_worktree/$artifact_root"
561- cp "$root/baseline/discord-status-reactions-tool-only-timeline.png" "$artifacts_worktree/$artifact_root/baseline.png"
562- cp "$root/candidate/discord-status-reactions-tool-only-timeline.png" "$artifacts_worktree/$artifact_root/candidate.png"
563- cp "$root/baseline/discord-status-reactions-tool-only-desktop.png" "$artifacts_worktree/$artifact_root/baseline-desktop.png"
564- cp "$root/candidate/discord-status-reactions-tool-only-desktop.png" "$artifacts_worktree/$artifact_root/candidate-desktop.png"
565- has_desktop_previews="false"
566- if [[ -f "$root/baseline/discord-status-reactions-tool-only-desktop-preview.gif" && -f "$root/candidate/discord-status-reactions-tool-only-desktop-preview.gif" ]]; then
567- cp "$root/baseline/discord-status-reactions-tool-only-desktop-preview.gif" "$artifacts_worktree/$artifact_root/baseline-desktop-preview.gif"
568- cp "$root/candidate/discord-status-reactions-tool-only-desktop-preview.gif" "$artifacts_worktree/$artifact_root/candidate-desktop-preview.gif"
569- cp "$root/baseline/discord-status-reactions-tool-only-desktop-preview.json" "$artifacts_worktree/$artifact_root/baseline-desktop-preview.json"
570- cp "$root/candidate/discord-status-reactions-tool-only-desktop-preview.json" "$artifacts_worktree/$artifact_root/candidate-desktop-preview.json"
571- has_desktop_previews="true"
572- fi
573- has_change_clips="false"
574- if [[ -f "$root/baseline/discord-status-reactions-tool-only-desktop-change.mp4" && -f "$root/candidate/discord-status-reactions-tool-only-desktop-change.mp4" ]]; then
575- cp "$root/baseline/discord-status-reactions-tool-only-desktop-change.mp4" "$artifacts_worktree/$artifact_root/baseline-desktop-change.mp4"
576- cp "$root/candidate/discord-status-reactions-tool-only-desktop-change.mp4" "$artifacts_worktree/$artifact_root/candidate-desktop-change.mp4"
577- has_change_clips="true"
578- fi
579- cp "$root/baseline/discord-status-reactions-tool-only-desktop.mp4" "$artifacts_worktree/$artifact_root/baseline-desktop.mp4"
580- cp "$root/candidate/discord-status-reactions-tool-only-desktop.mp4" "$artifacts_worktree/$artifact_root/candidate-desktop.mp4"
581- cp "$root/comparison.json" "$artifacts_worktree/$artifact_root/comparison.json"
582- cp "$root/mantis-report.md" "$artifacts_worktree/$artifact_root/mantis-report.md"
583-
584- git -C "$artifacts_worktree" add "$artifact_root"
585- if git -C "$artifacts_worktree" diff --cached --quiet; then
586- echo "No QA screenshot/video artifact changes to publish."
587- else
588- git -C "$artifacts_worktree" commit --quiet -m "qa: publish Mantis Discord evidence for PR ${TARGET_PR}"
589- git -C "$artifacts_worktree" push --quiet origin HEAD:qa-artifacts
590- fi
591-
592- encoded_artifact_root="${artifact_root// /%20}"
593- raw_base="https://raw.githubusercontent.com/${GITHUB_REPOSITORY}/qa-artifacts/${encoded_artifact_root}"
594- baseline_status="$(jq -r '.baseline.status' "$root/comparison.json")"
595- candidate_status="$(jq -r '.candidate.status' "$root/comparison.json")"
596- pass="$(jq -r '.pass' "$root/comparison.json")"
597- preview_section=""
598- if [[ "$has_desktop_previews" == "true" ]]; then
599- preview_section="$(cat <<EOF
600-
601- | Baseline motion preview | Candidate motion preview |
602- | --- | --- |
603- | <img src="${raw_base}/baseline-desktop-preview.gif" width="420" alt="Animated baseline desktop preview"> | <img src="${raw_base}/candidate-desktop-preview.gif" width="420" alt="Animated candidate desktop preview"> |
604- EOF
605- )"
606- fi
607- change_clip_section=""
608- if [[ "$has_change_clips" == "true" ]]; then
609- change_clip_section="$(cat <<EOF
610-
611- Motion-trimmed clips:
612- - [Baseline change MP4](${raw_base}/baseline-desktop-change.mp4)
613- - [Candidate change MP4](${raw_base}/candidate-desktop-change.mp4)
614- EOF
615- )"
616- fi
617- comment_file="$(mktemp)"
618- cat > "$comment_file" <<EOF
619- <!-- mantis-discord-status-reactions -->
620- ## Mantis Discord Status Reactions QA
621-
622- Summary: Mantis reran Discord status reactions against the known queued-only baseline and the candidate ref. The baseline reproduced the bug, while the candidate showed the expected queued -> thinking -> done reaction sequence.
623-
624- - Scenario: \`discord-status-reactions-tool-only\`
625- - Trigger: \`${REQUEST_SOURCE}\`
626- - Run: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}
627- - Artifact: ${ARTIFACT_URL}
628- - Baseline: \`${baseline_status}\` at \`${BASELINE_SHA}\`
629- - Candidate: \`${candidate_status}\` at \`${CANDIDATE_SHA}\`
630- - Overall: \`${pass}\`
631-
632- | Baseline queued-only | Candidate queued -> thinking -> done |
633- | --- | --- |
634- | <img src="${raw_base}/baseline.png" width="420" alt="Baseline Discord status reaction timeline"> | <img src="${raw_base}/candidate.png" width="420" alt="Candidate Discord status reaction timeline"> |
635-
636- | Baseline desktop/VNC browser | Candidate desktop/VNC browser |
637- | --- | --- |
638- | <img src="${raw_base}/baseline-desktop.png" width="420" alt="Baseline Mantis desktop browser screenshot"> | <img src="${raw_base}/candidate-desktop.png" width="420" alt="Candidate Mantis desktop browser screenshot"> |
639- ${preview_section}
640- ${change_clip_section}
641-
642- Full videos:
643- - [Baseline desktop MP4](${raw_base}/baseline-desktop.mp4)
644- - [Candidate desktop MP4](${raw_base}/candidate-desktop.mp4)
645-
646- Raw QA files: https://github.com/${GITHUB_REPOSITORY}/tree/qa-artifacts/${artifact_root}
647- EOF
648-
649- comment_id="$(
650- gh api --paginate "repos/${GITHUB_REPOSITORY}/issues/${TARGET_PR}/comments" \
651- --jq '.[] | select(.body | contains("<!-- mantis-discord-status-reactions -->")) | .id' \
652- | tail -n 1
653- )"
654-
655- if [[ -n "$comment_id" ]]; then
656- comment_payload="$(mktemp)"
657- jq -n --rawfile body "$comment_file" '{ body: $body }' > "$comment_payload"
658- if gh api --method PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${comment_id}" --input "$comment_payload" >/dev/null; then
659- echo "Updated Mantis QA evidence comment on PR #${TARGET_PR}."
660- else
661- echo "::warning::Could not update existing Mantis QA evidence comment ${comment_id}; creating a new one."
662- gh pr comment "$TARGET_PR" --body-file "$comment_file"
663- echo "Created Mantis QA evidence comment on PR #${TARGET_PR}."
664- fi
665- else
666- gh pr comment "$TARGET_PR" --body-file "$comment_file"
667- echo "Created Mantis QA evidence comment on PR #${TARGET_PR}."
668- fi
557+ node scripts/mantis/publish-pr-evidence.mjs \
558+ --manifest "$root/mantis-evidence.json" \
559+ --target-pr "$TARGET_PR" \
560+ --artifact-root "mantis/discord-status-reactions/pr-${TARGET_PR}/run-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" \
561+ --marker "<!-- mantis-discord-status-reactions -->" \
562+ --artifact-url "$ARTIFACT_URL" \
563+ --run-url "https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
564+ --request-source "$REQUEST_SOURCE"
0 commit comments