























@@ -255,6 +255,24 @@ jobs:
255255 - name: Build Mantis harness
256256run: pnpm build
257257258+ - name: Install Crabbox CLI
259+env:
260+GH_TOKEN: ${{ github.token }}
261+shell: bash
262+run: |
263+ set -euo pipefail
264+ install_dir="${RUNNER_TEMP}/crabbox"
265+ mkdir -p "$install_dir" "$HOME/.local/bin"
266+ gh release download \
267+ --repo openclaw/crabbox \
268+ --pattern 'crabbox_*_linux_amd64.tar.gz' \
269+ --dir "$install_dir" \
270+ --clobber
271+ tar -xzf "$install_dir"/crabbox_*_linux_amd64.tar.gz -C "$install_dir"
272+ install -m 0755 "$install_dir/crabbox" "$HOME/.local/bin/crabbox"
273+ echo "$HOME/.local/bin" >> "$GITHUB_PATH"
274+ "$HOME/.local/bin/crabbox" --version
275+258276 - name: Prepare baseline and candidate worktrees
259277shell: bash
260278env:
@@ -285,6 +303,10 @@ jobs:
285303OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
286304OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
287305OPENCLAW_QA_DISCORD_CAPTURE_CONTENT: "1"
306+CRABBOX_COORDINATOR: ${{ secrets.CRABBOX_COORDINATOR }}
307+CRABBOX_COORDINATOR_TOKEN: ${{ secrets.CRABBOX_COORDINATOR_TOKEN }}
308+CRABBOX_ACCESS_CLIENT_ID: ${{ secrets.CRABBOX_ACCESS_CLIENT_ID }}
309+CRABBOX_ACCESS_CLIENT_SECRET: ${{ secrets.CRABBOX_ACCESS_CLIENT_SECRET }}
288310run: |
289311 set -euo pipefail
290312@@ -299,6 +321,7 @@ jobs:
299321 require_var OPENAI_API_KEY
300322 require_var OPENCLAW_QA_CONVEX_SITE_URL
301323 require_var OPENCLAW_QA_CONVEX_SECRET_CI
324+ require_var CRABBOX_COORDINATOR_TOKEN
302325303326 root=".artifacts/qa-e2e/mantis/discord-status-reactions"
304327 worktree_root=".artifacts/qa-e2e/mantis/discord-status-reactions-worktrees"
@@ -328,6 +351,55 @@ jobs:
328351 run_lane baseline
329352 run_lane candidate
330353354+ desktop_lease_id=""
355+ warmup_output="$(
356+ crabbox warmup \
357+ --provider hetzner \
358+ --desktop \
359+ --browser \
360+ --class standard \
361+ --idle-timeout 30m \
362+ --ttl 90m
363+ )"
364+ printf '%s\n' "$warmup_output" | tee "$root/crabbox-desktop-warmup.log"
365+ desktop_lease_id="$(printf '%s\n' "$warmup_output" | grep -Eo 'cbx_[a-f0-9]+' | head -n 1 || true)"
366+ if [[ ! "$desktop_lease_id" =~ ^cbx_[a-f0-9]+$ ]]; then
367+ echo "Crabbox desktop warmup did not return a lease id." >&2
368+ exit 1
369+ fi
370+371+ cleanup_desktop_lease() {
372+ if [[ -n "$desktop_lease_id" ]]; then
373+ crabbox stop --provider hetzner "$desktop_lease_id" || true
374+ fi
375+ }
376+ trap cleanup_desktop_lease EXIT
377+378+ capture_desktop_lane() {
379+ local lane="$1"
380+ local html_file="$root/$lane/discord-status-reactions-tool-only-timeline.html"
381+ local desktop_dir="$root/$lane/desktop-browser"
382+ if [[ ! -f "$html_file" ]]; then
383+ echo "Missing desktop source HTML for ${lane}: ${html_file}" >&2
384+ exit 1
385+ fi
386+ local args=(
387+ openclaw qa mantis desktop-browser-smoke
388+ --html-file "$html_file"
389+ --output-dir "$desktop_dir"
390+ --provider hetzner
391+ --class standard
392+ --idle-timeout 30m
393+ --ttl 90m
394+ --lease-id "$desktop_lease_id"
395+ )
396+ pnpm "${args[@]}"
397+ cp "$desktop_dir/desktop-browser-smoke.png" "$root/$lane/discord-status-reactions-tool-only-desktop.png"
398+ }
399+400+ capture_desktop_lane baseline
401+ capture_desktop_lane candidate
402+331403 baseline_status="$(jq -r '.scenarios[0].status' "$root/baseline/discord-qa-summary.json")"
332404 candidate_status="$(jq -r '.scenarios[0].status' "$root/candidate/discord-qa-summary.json")"
333405@@ -351,6 +423,8 @@ jobs:
351423 echo "- Candidate status: \`${candidate_status}\`"
352424 echo "- Baseline screenshot: \`baseline/discord-status-reactions-tool-only-timeline.png\`"
353425 echo "- Candidate screenshot: \`candidate/discord-status-reactions-tool-only-timeline.png\`"
426+ echo "- Baseline desktop screenshot: \`baseline/discord-status-reactions-tool-only-desktop.png\`"
427+ echo "- Candidate desktop screenshot: \`candidate/discord-status-reactions-tool-only-desktop.png\`"
354428 } > "$root/mantis-report.md"
355429356430 cat "$root/mantis-report.md" >> "$GITHUB_STEP_SUMMARY"
@@ -409,7 +483,9 @@ jobs:
409483 for required in \
410484 "$root/comparison.json" \
411485 "$root/baseline/discord-status-reactions-tool-only-timeline.png" \
412- "$root/candidate/discord-status-reactions-tool-only-timeline.png"
486+ "$root/candidate/discord-status-reactions-tool-only-timeline.png" \
487+ "$root/baseline/discord-status-reactions-tool-only-desktop.png" \
488+ "$root/candidate/discord-status-reactions-tool-only-desktop.png"
413489 do
414490 if [[ ! -f "$required" ]]; then
415491 echo "Missing required QA evidence file: $required" >&2
@@ -435,6 +511,8 @@ jobs:
435511 mkdir -p "$artifacts_worktree/$artifact_root"
436512 cp "$root/baseline/discord-status-reactions-tool-only-timeline.png" "$artifacts_worktree/$artifact_root/baseline.png"
437513 cp "$root/candidate/discord-status-reactions-tool-only-timeline.png" "$artifacts_worktree/$artifact_root/candidate.png"
514+ cp "$root/baseline/discord-status-reactions-tool-only-desktop.png" "$artifacts_worktree/$artifact_root/baseline-desktop.png"
515+ cp "$root/candidate/discord-status-reactions-tool-only-desktop.png" "$artifacts_worktree/$artifact_root/candidate-desktop.png"
438516 cp "$root/comparison.json" "$artifacts_worktree/$artifact_root/comparison.json"
439517 cp "$root/mantis-report.md" "$artifacts_worktree/$artifact_root/mantis-report.md"
440518@@ -470,6 +548,10 @@ jobs:
470548 | --- | --- |
471549 | <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"> |
472550551+ | Baseline desktop/VNC browser | Candidate desktop/VNC browser |
552+ | --- | --- |
553+ | <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"> |
554+473555 Raw QA files: https://github.com/${GITHUB_REPOSITORY}/tree/qa-artifacts/${artifact_root}
474556 EOF
475557此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。