mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 12:56:01 -06:00
381cec0051
Require live Mantis and Telegram proof artifact uploads to fail when evidence is missing and guard the workflow invariant.
606 lines
26 KiB
YAML
606 lines
26 KiB
YAML
name: Mantis Telegram Live
|
|
|
|
on:
|
|
issue_comment:
|
|
types: [created]
|
|
workflow_dispatch:
|
|
inputs:
|
|
candidate_ref:
|
|
description: Ref, tag, or SHA to verify with Telegram live QA
|
|
required: true
|
|
default: main
|
|
type: string
|
|
pr_number:
|
|
description: Optional PR number to receive the QA evidence comment
|
|
required: false
|
|
type: string
|
|
scenario:
|
|
description: Optional comma-separated Telegram scenario ids
|
|
required: false
|
|
default: telegram-status-command
|
|
type: string
|
|
crabbox_provider:
|
|
description: Crabbox provider for the desktop transcript capture
|
|
required: false
|
|
default: aws
|
|
type: choice
|
|
options:
|
|
- aws
|
|
- hetzner
|
|
crabbox_lease_id:
|
|
description: Optional existing Crabbox desktop/browser lease id or slug to reuse
|
|
required: false
|
|
type: string
|
|
|
|
permissions:
|
|
actions: read
|
|
contents: read
|
|
issues: write
|
|
pull-requests: write
|
|
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
|
|
NODE_VERSION: "24.x"
|
|
OPENCLAW_BUILD_PRIVATE_QA: "1"
|
|
OPENCLAW_ENABLE_PRIVATE_QA_CLI: "1"
|
|
CRABBOX_REF: main
|
|
CRABBOX_AWS_REGION: us-east-1
|
|
CRABBOX_CAPACITY_REGIONS: us-east-1
|
|
|
|
jobs:
|
|
authorize_actor:
|
|
name: Authorize workflow actor
|
|
if: >-
|
|
${{
|
|
github.event_name == 'workflow_dispatch' ||
|
|
(
|
|
github.event_name == 'issue_comment' &&
|
|
github.event.issue.pull_request &&
|
|
(
|
|
contains(github.event.comment.body, '@openclaw-mantis') ||
|
|
contains(github.event.comment.body, '/openclaw-mantis')
|
|
)
|
|
)
|
|
}}
|
|
runs-on: ubuntu-24.04
|
|
outputs:
|
|
authorized: ${{ steps.permission.outputs.authorized }}
|
|
steps:
|
|
- name: Require maintainer-level repository access
|
|
id: permission
|
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
|
with:
|
|
script: |
|
|
const allowed = new Set(["admin", "maintain", "write"]);
|
|
const { owner, repo } = context.repo;
|
|
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
|
|
owner,
|
|
repo,
|
|
username: context.actor,
|
|
});
|
|
const permission = data.permission;
|
|
core.info(`Actor ${context.actor} permission: ${permission}`);
|
|
if (!allowed.has(permission)) {
|
|
core.notice(
|
|
`Workflow requires write/maintain/admin access. Actor "${context.actor}" has "${permission}".`,
|
|
);
|
|
core.setOutput("authorized", "false");
|
|
return;
|
|
}
|
|
core.setOutput("authorized", "true");
|
|
|
|
resolve_request:
|
|
name: Resolve Mantis request
|
|
needs: authorize_actor
|
|
if: needs.authorize_actor.outputs.authorized == 'true'
|
|
runs-on: ubuntu-24.04
|
|
outputs:
|
|
candidate_ref: ${{ steps.resolve.outputs.candidate_ref }}
|
|
crabbox_provider: ${{ steps.resolve.outputs.crabbox_provider }}
|
|
lease_id: ${{ steps.resolve.outputs.lease_id }}
|
|
pr_number: ${{ steps.resolve.outputs.pr_number }}
|
|
request_source: ${{ steps.resolve.outputs.request_source }}
|
|
scenario: ${{ steps.resolve.outputs.scenario }}
|
|
should_run: ${{ steps.resolve.outputs.should_run }}
|
|
steps:
|
|
- name: Resolve refs and target PR
|
|
id: resolve
|
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
|
with:
|
|
script: |
|
|
const eventName = context.eventName;
|
|
|
|
function setOutput(name, value) {
|
|
core.setOutput(name, value ?? "");
|
|
core.info(`${name}=${value ?? ""}`);
|
|
}
|
|
|
|
if (eventName === "workflow_dispatch") {
|
|
const inputs = context.payload.inputs ?? {};
|
|
setOutput("should_run", "true");
|
|
setOutput("candidate_ref", inputs.candidate_ref || "main");
|
|
setOutput("pr_number", inputs.pr_number || "");
|
|
setOutput("scenario", inputs.scenario || "telegram-status-command");
|
|
setOutput("crabbox_provider", inputs.crabbox_provider || "aws");
|
|
setOutput("lease_id", inputs.crabbox_lease_id || "");
|
|
setOutput("request_source", "workflow_dispatch");
|
|
return;
|
|
}
|
|
|
|
if (eventName !== "issue_comment") {
|
|
core.setFailed(`Unsupported event: ${eventName}`);
|
|
return;
|
|
}
|
|
|
|
const issue = context.payload.issue;
|
|
const body = context.payload.comment?.body ?? "";
|
|
if (!issue?.pull_request) {
|
|
core.setFailed("Mantis issue_comment trigger requires a pull request comment.");
|
|
return;
|
|
}
|
|
|
|
const normalized = body.toLowerCase();
|
|
const requestedDesktopProof =
|
|
normalized.includes("desktop proof") ||
|
|
normalized.includes("desktop-proof") ||
|
|
normalized.includes("telegram desktop") ||
|
|
normalized.includes("native telegram") ||
|
|
normalized.includes("visible proof") ||
|
|
normalized.includes("visible-proof") ||
|
|
normalized.includes("telegram-visible-proof");
|
|
const requested =
|
|
(normalized.includes("@openclaw-mantis") || normalized.includes("/openclaw-mantis")) &&
|
|
normalized.includes("telegram") &&
|
|
!requestedDesktopProof;
|
|
if (!requested) {
|
|
core.notice("Comment mentioned Mantis but did not request Telegram live QA.");
|
|
setOutput("should_run", "false");
|
|
setOutput("candidate_ref", "");
|
|
setOutput("pr_number", "");
|
|
setOutput("scenario", "");
|
|
setOutput("crabbox_provider", "");
|
|
setOutput("lease_id", "");
|
|
setOutput("request_source", "unsupported_issue_comment");
|
|
return;
|
|
}
|
|
|
|
const { owner, repo } = context.repo;
|
|
const { data: pr } = await github.rest.pulls.get({
|
|
owner,
|
|
repo,
|
|
pull_number: issue.number,
|
|
});
|
|
const candidateMatch = body.match(/(?:candidate|head)[\s:=]+([^\s`]+)/i);
|
|
const scenarioMatch = body.match(/(?:scenario|scenarios)[\s:=]+([^\s`]+)/i);
|
|
const providerMatch = body.match(/(?:provider|crabbox_provider)[\s:=]+([^\s`]+)/i);
|
|
const leaseMatch = body.match(/(?:lease|lease_id|crabbox_lease_id)[\s:=]+([^\s`]+)/i);
|
|
const rawCandidate = candidateMatch?.[1];
|
|
const candidate =
|
|
rawCandidate && !["head", "pr", "pr-head"].includes(rawCandidate.toLowerCase())
|
|
? rawCandidate
|
|
: pr.head.sha;
|
|
const provider = providerMatch?.[1] || "aws";
|
|
if (!["aws", "hetzner"].includes(provider)) {
|
|
core.setFailed(`Unsupported Crabbox provider for Mantis Telegram: ${provider}`);
|
|
return;
|
|
}
|
|
|
|
setOutput("should_run", "true");
|
|
setOutput("candidate_ref", candidate);
|
|
setOutput("pr_number", String(issue.number));
|
|
setOutput("scenario", scenarioMatch?.[1] || "telegram-status-command");
|
|
setOutput("crabbox_provider", provider);
|
|
setOutput("lease_id", leaseMatch?.[1] || "");
|
|
setOutput("request_source", "issue_comment");
|
|
|
|
await github.rest.reactions.createForIssueComment({
|
|
owner,
|
|
repo,
|
|
comment_id: context.payload.comment.id,
|
|
content: "eyes",
|
|
}).catch((error) => core.warning(`Could not add eyes reaction: ${error.message}`));
|
|
|
|
validate_ref:
|
|
name: Validate candidate ref
|
|
needs: resolve_request
|
|
if: ${{ needs.resolve_request.outputs.should_run == 'true' }}
|
|
runs-on: ubuntu-24.04
|
|
outputs:
|
|
candidate_revision: ${{ steps.validate.outputs.candidate_revision }}
|
|
steps:
|
|
- name: Checkout harness ref
|
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
with:
|
|
persist-credentials: false
|
|
fetch-depth: 0
|
|
|
|
- name: Validate ref is trusted
|
|
id: validate
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
CANDIDATE_REF: ${{ needs.resolve_request.outputs.candidate_ref }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
|
|
|
|
revision="$(git rev-parse "${CANDIDATE_REF}^{commit}")"
|
|
reason=""
|
|
if git merge-base --is-ancestor "$revision" refs/remotes/origin/main; then
|
|
reason="main-ancestor"
|
|
elif git tag --points-at "$revision" | grep -Eq '^v'; then
|
|
reason="release-tag"
|
|
else
|
|
pr_head_count="$(
|
|
gh api \
|
|
-H "Accept: application/vnd.github+json" \
|
|
"repos/${GITHUB_REPOSITORY}/commits/${revision}/pulls" \
|
|
--jq '[.[] | select(.state == "open" and .head.repo.full_name == "'"${GITHUB_REPOSITORY}"'" and .head.sha == "'"${revision}"'")] | length'
|
|
)"
|
|
if [[ "$pr_head_count" != "0" ]]; then
|
|
reason="open-pr-head"
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$reason" ]]; then
|
|
echo "Candidate ref '${CANDIDATE_REF}' resolved to ${revision}, which is not trusted for this secret-bearing Mantis run." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "candidate_revision=${revision}" >> "$GITHUB_OUTPUT"
|
|
{
|
|
echo "candidate: \`${CANDIDATE_REF}\`"
|
|
echo "candidate SHA: \`${revision}\`"
|
|
echo "candidate trust reason: \`${reason}\`"
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
run_telegram_live:
|
|
name: Run Telegram live QA with Crabbox evidence
|
|
needs: [resolve_request, validate_ref]
|
|
if: ${{ needs.resolve_request.outputs.should_run == 'true' }}
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 180
|
|
environment: qa-live-shared
|
|
outputs:
|
|
comparison_status: ${{ steps.run_mantis.outputs.comparison_status }}
|
|
output_dir: ${{ steps.run_mantis.outputs.output_dir }}
|
|
steps:
|
|
- name: Wait for older Mantis Telegram account run
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
current_created="$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" --jq .created_at)"
|
|
stale_before="$(date -u -d '8 hours ago' +%Y-%m-%dT%H:%M:%SZ)"
|
|
run_has_active_jobs() {
|
|
local run_id="$1"
|
|
local run_state="$2"
|
|
if [[ "$run_state" != "in_progress" ]]; then
|
|
return 0
|
|
fi
|
|
local active_jobs
|
|
active_jobs="$(gh run view "$run_id" --repo "$GITHUB_REPOSITORY" --json jobs --jq '[.jobs[] | select(.status == "queued" or .status == "in_progress" or .status == "waiting" or .status == "pending" or .status == "requested")] | length')"
|
|
[[ "$active_jobs" != "0" ]]
|
|
}
|
|
while true; do
|
|
candidates="$(
|
|
for workflow in mantis-telegram-desktop-proof.yml mantis-telegram-live.yml; do
|
|
for status in queued in_progress waiting pending requested; do
|
|
gh run list --repo "$GITHUB_REPOSITORY" --workflow "$workflow" --status "$status" --limit 100 --json databaseId,status,createdAt,url \
|
|
| jq -r \
|
|
--argjson current_id "$GITHUB_RUN_ID" \
|
|
--arg current_created "$current_created" \
|
|
--arg stale_before "$stale_before" \
|
|
'.[] | select(.databaseId != $current_id) | select(.createdAt >= $stale_before) | select(.createdAt < $current_created or (.createdAt == $current_created and .databaseId < $current_id)) | "\(.createdAt)\t#\(.databaseId)\t\(.status)\t\(.url)"'
|
|
done
|
|
done | sort -u
|
|
)"
|
|
blockers=""
|
|
while IFS=$'\t' read -r created run_id run_state url; do
|
|
if [[ -n "$run_id" ]] && run_has_active_jobs "${run_id#\#}" "$run_state"; then
|
|
blockers+="${created}"$'\t'"${run_id}"$'\t'"${run_state}"$'\t'"${url}"$'\n'
|
|
fi
|
|
done <<<"$candidates"
|
|
if [[ -z "$blockers" ]]; then
|
|
break
|
|
fi
|
|
echo "Waiting for older Mantis Telegram account run:"
|
|
printf '%s\n' "$blockers" | head -n 10
|
|
sleep 60
|
|
done
|
|
|
|
- name: Checkout harness ref
|
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
with:
|
|
persist-credentials: false
|
|
fetch-depth: 0
|
|
|
|
- name: Setup Node environment
|
|
uses: ./.github/actions/setup-node-env
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
install-bun: "true"
|
|
|
|
- name: Build Mantis harness
|
|
run: pnpm build
|
|
|
|
- name: Cache Mantis candidate pnpm store
|
|
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
|
|
with:
|
|
path: |
|
|
~/.local/share/pnpm/store
|
|
~/.cache/pnpm
|
|
key: mantis-telegram-pnpm-${{ runner.os }}-${{ env.NODE_VERSION }}-${{ hashFiles('pnpm-lock.yaml') }}
|
|
restore-keys: |
|
|
mantis-telegram-pnpm-${{ runner.os }}-${{ env.NODE_VERSION }}-
|
|
|
|
- name: Setup Go for Crabbox CLI
|
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6
|
|
with:
|
|
go-version: "1.26.x"
|
|
cache: false
|
|
|
|
- name: Install Crabbox CLI
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
install_dir="${RUNNER_TEMP}/crabbox"
|
|
mkdir -p "$install_dir/src" "$HOME/.local/bin"
|
|
git init "$install_dir/src"
|
|
git -C "$install_dir/src" remote add origin https://github.com/openclaw/crabbox.git
|
|
git -C "$install_dir/src" fetch --depth 1 origin "$CRABBOX_REF"
|
|
git -C "$install_dir/src" checkout --detach FETCH_HEAD
|
|
go build -C "$install_dir/src" -o "$HOME/.local/bin/crabbox" ./cmd/crabbox
|
|
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
"$HOME/.local/bin/crabbox" --version
|
|
"$HOME/.local/bin/crabbox" warmup --help > "$install_dir/warmup-help.txt" 2>&1
|
|
grep -q -- "-desktop" "$install_dir/warmup-help.txt"
|
|
"$HOME/.local/bin/crabbox" media preview --help >/dev/null
|
|
|
|
- name: Prepare candidate worktree
|
|
env:
|
|
CANDIDATE_SHA: ${{ needs.validate_ref.outputs.candidate_revision }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
worktree_root=".artifacts/qa-e2e/mantis/telegram-live-worktrees"
|
|
mkdir -p "$worktree_root"
|
|
git worktree add --detach "$worktree_root/candidate" "$CANDIDATE_SHA"
|
|
pnpm --dir "$worktree_root/candidate" install --frozen-lockfile --prefer-offline
|
|
pnpm --dir "$worktree_root/candidate" build
|
|
|
|
- name: Run Telegram live scenario and capture desktop evidence
|
|
id: run_mantis
|
|
env:
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
OPENCLAW_QA_CONVEX_SITE_URL: ${{ secrets.OPENCLAW_QA_CONVEX_SITE_URL }}
|
|
OPENCLAW_QA_CONVEX_SECRET_CI: ${{ secrets.OPENCLAW_QA_CONVEX_SECRET_CI }}
|
|
OPENCLAW_QA_CREDENTIAL_ACQUIRE_TIMEOUT_MS: "1800000"
|
|
OPENCLAW_QA_REDACT_PUBLIC_METADATA: "1"
|
|
CRABBOX_COORDINATOR: ${{ secrets.CRABBOX_COORDINATOR }}
|
|
CRABBOX_COORDINATOR_TOKEN: ${{ secrets.CRABBOX_COORDINATOR_TOKEN }}
|
|
OPENCLAW_QA_MANTIS_CRABBOX_COORDINATOR: ${{ secrets.OPENCLAW_QA_MANTIS_CRABBOX_COORDINATOR }}
|
|
OPENCLAW_QA_MANTIS_CRABBOX_COORDINATOR_TOKEN: ${{ secrets.OPENCLAW_QA_MANTIS_CRABBOX_COORDINATOR_TOKEN }}
|
|
CRABBOX_ACCESS_CLIENT_ID: ${{ secrets.CRABBOX_ACCESS_CLIENT_ID }}
|
|
CRABBOX_ACCESS_CLIENT_SECRET: ${{ secrets.CRABBOX_ACCESS_CLIENT_SECRET }}
|
|
CRABBOX_AWS_REGION: ${{ env.CRABBOX_AWS_REGION }}
|
|
CRABBOX_CAPACITY_REGIONS: ${{ env.CRABBOX_CAPACITY_REGIONS }}
|
|
CRABBOX_LEASE_ID: ${{ needs.resolve_request.outputs.lease_id }}
|
|
CRABBOX_PROVIDER: ${{ needs.resolve_request.outputs.crabbox_provider }}
|
|
SCENARIO_INPUT: ${{ needs.resolve_request.outputs.scenario }}
|
|
CANDIDATE_SHA: ${{ needs.validate_ref.outputs.candidate_revision }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
require_var() {
|
|
local key="$1"
|
|
if [[ -z "${!key:-}" ]]; then
|
|
echo "Missing required ${key}." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
CRABBOX_COORDINATOR="${CRABBOX_COORDINATOR:-${OPENCLAW_QA_MANTIS_CRABBOX_COORDINATOR:-}}"
|
|
CRABBOX_COORDINATOR_TOKEN="${CRABBOX_COORDINATOR_TOKEN:-${OPENCLAW_QA_MANTIS_CRABBOX_COORDINATOR_TOKEN:-}}"
|
|
export CRABBOX_COORDINATOR CRABBOX_COORDINATOR_TOKEN
|
|
|
|
require_var OPENAI_API_KEY
|
|
require_var OPENCLAW_QA_CONVEX_SITE_URL
|
|
require_var OPENCLAW_QA_CONVEX_SECRET_CI
|
|
require_var CRABBOX_COORDINATOR_TOKEN
|
|
|
|
candidate_repo="$(pwd)/.artifacts/qa-e2e/mantis/telegram-live-worktrees/candidate"
|
|
output_rel=".artifacts/qa-e2e/mantis/telegram-live"
|
|
root="$candidate_repo/$output_rel"
|
|
echo "output_dir=${root}" >> "$GITHUB_OUTPUT"
|
|
model="${OPENCLAW_CI_OPENAI_MODEL:-openai/gpt-5.5}"
|
|
|
|
scenario_args=()
|
|
if [[ -n "${SCENARIO_INPUT// }" ]]; then
|
|
IFS=',' read -r -a raw_scenarios <<<"${SCENARIO_INPUT}"
|
|
for raw in "${raw_scenarios[@]}"; do
|
|
scenario="$(printf '%s' "${raw}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
|
if [[ -n "${scenario}" ]]; then
|
|
scenario_args+=(--scenario "${scenario}")
|
|
fi
|
|
done
|
|
fi
|
|
|
|
set +e
|
|
pnpm --dir "$candidate_repo" openclaw qa telegram \
|
|
--repo-root "$candidate_repo" \
|
|
--output-dir "$output_rel" \
|
|
--provider-mode live-frontier \
|
|
--model "$model" \
|
|
--alt-model "$model" \
|
|
--fast \
|
|
--credential-source convex \
|
|
--credential-role ci \
|
|
--allow-failures \
|
|
"${scenario_args[@]}"
|
|
telegram_exit=$?
|
|
set -e
|
|
|
|
if [[ ! -f "$root/qa-evidence.json" && ! -f "$root/telegram-qa-summary.json" ]]; then
|
|
echo "Telegram live QA did not produce an evidence summary." >&2
|
|
exit "$telegram_exit"
|
|
fi
|
|
echo "telegram_exit=${telegram_exit}" >> "$GITHUB_OUTPUT"
|
|
|
|
node "${GITHUB_WORKSPACE}/scripts/mantis/build-telegram-evidence.mjs" \
|
|
--output-dir "$root" \
|
|
--candidate-ref "$CANDIDATE_SHA" \
|
|
--candidate-sha "$CANDIDATE_SHA" \
|
|
--scenario-label "${SCENARIO_INPUT:-telegram-live}"
|
|
|
|
comparison_status="$(jq -r 'if .comparison.pass then "pass" else "fail" end' "$root/mantis-evidence.json")"
|
|
echo "comparison_status=${comparison_status}" >> "$GITHUB_OUTPUT"
|
|
|
|
desktop_args=()
|
|
if [[ -n "${CRABBOX_LEASE_ID:-}" ]]; then
|
|
desktop_args+=(--lease-id "$CRABBOX_LEASE_ID")
|
|
fi
|
|
pnpm --dir "$candidate_repo" openclaw qa mantis desktop-browser-smoke \
|
|
--repo-root "$candidate_repo" \
|
|
--html-file "$output_rel/telegram-live-transcript.html" \
|
|
--output-dir "$output_rel/desktop-browser" \
|
|
--provider "$CRABBOX_PROVIDER" \
|
|
--class standard \
|
|
--idle-timeout 45m \
|
|
--ttl 120m \
|
|
--video-duration 18 \
|
|
"${desktop_args[@]}"
|
|
|
|
cp "$root/desktop-browser/desktop-browser-smoke.png" "$root/telegram-live-desktop.png"
|
|
if [[ -f "$root/desktop-browser/desktop-browser-smoke.mp4" ]]; then
|
|
cp "$root/desktop-browser/desktop-browser-smoke.mp4" "$root/telegram-live.mp4"
|
|
fi
|
|
|
|
if [[ -f "$root/telegram-live.mp4" ]]; then
|
|
if ! command -v ffmpeg >/dev/null 2>&1 || ! command -v ffprobe >/dev/null 2>&1; then
|
|
sudo apt-get update -y >/tmp/mantis-telegram-ffmpeg-apt.log 2>&1 || true
|
|
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y ffmpeg >>/tmp/mantis-telegram-ffmpeg-apt.log 2>&1 || true
|
|
fi
|
|
if ! crabbox media preview \
|
|
--input "$root/telegram-live.mp4" \
|
|
--output "$root/telegram-live-preview.gif" \
|
|
--trimmed-video-output "$root/telegram-live-change.mp4" \
|
|
--json > "$root/telegram-live-preview.json"; then
|
|
rm -f "$root/telegram-live-preview.gif"
|
|
rm -f "$root/telegram-live-change.mp4"
|
|
rm -f "$root/telegram-live-preview.json"
|
|
echo "::warning::Could not generate Telegram motion-trimmed desktop preview."
|
|
fi
|
|
fi
|
|
|
|
cat "$root/telegram-qa-report.md" >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
- name: Upload Mantis Telegram artifacts
|
|
id: upload_artifact
|
|
if: ${{ always() && steps.run_mantis.outputs.output_dir != '' }}
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: mantis-telegram-live-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: ${{ steps.run_mantis.outputs.output_dir }}
|
|
retention-days: 14
|
|
if-no-files-found: error
|
|
|
|
- name: Create Mantis GitHub App token
|
|
id: mantis_app_token
|
|
if: ${{ always() && needs.resolve_request.outputs.pr_number != '' }}
|
|
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3
|
|
with:
|
|
app-id: ${{ secrets.MANTIS_GITHUB_APP_ID }}
|
|
private-key: ${{ secrets.MANTIS_GITHUB_APP_PRIVATE_KEY }}
|
|
owner: ${{ github.repository_owner }}
|
|
repositories: ${{ github.event.repository.name }}
|
|
permission-issues: write
|
|
permission-pull-requests: write
|
|
|
|
- name: Comment PR with inline QA evidence
|
|
if: ${{ always() && needs.resolve_request.outputs.pr_number != '' && steps.run_mantis.outputs.output_dir != '' }}
|
|
env:
|
|
GH_TOKEN: ${{ steps.mantis_app_token.outputs.token }}
|
|
ARTIFACT_URL: ${{ steps.upload_artifact.outputs.artifact-url }}
|
|
MANTIS_ARTIFACT_R2_ACCESS_KEY_ID: ${{ secrets.MANTIS_ARTIFACT_R2_ACCESS_KEY_ID }}
|
|
MANTIS_ARTIFACT_R2_BUCKET: openclaw-crabbox-artifacts
|
|
MANTIS_ARTIFACT_R2_ENDPOINT: ${{ vars.MANTIS_ARTIFACT_R2_ENDPOINT }}
|
|
MANTIS_ARTIFACT_R2_PUBLIC_BASE_URL: https://artifacts.openclaw.ai
|
|
MANTIS_ARTIFACT_R2_REGION: auto
|
|
MANTIS_ARTIFACT_R2_SECRET_ACCESS_KEY: ${{ secrets.MANTIS_ARTIFACT_R2_SECRET_ACCESS_KEY }}
|
|
REQUEST_SOURCE: ${{ needs.resolve_request.outputs.request_source }}
|
|
TARGET_PR: ${{ needs.resolve_request.outputs.pr_number }}
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
root="${{ steps.run_mantis.outputs.output_dir }}"
|
|
if [[ ! -f "$root/mantis-evidence.json" ]]; then
|
|
echo "No Mantis evidence manifest found; skipping PR evidence comment."
|
|
exit 0
|
|
fi
|
|
artifact_url_args=()
|
|
if [[ -n "${ARTIFACT_URL:-}" ]]; then
|
|
artifact_url_args=(--artifact-url "$ARTIFACT_URL")
|
|
fi
|
|
node scripts/mantis/publish-pr-evidence.mjs \
|
|
--manifest "$root/mantis-evidence.json" \
|
|
--target-pr "$TARGET_PR" \
|
|
--artifact-root "mantis/telegram-live/pr-${TARGET_PR}/run-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" \
|
|
--marker "<!-- mantis-telegram-live -->" \
|
|
"${artifact_url_args[@]}" \
|
|
--run-url "https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
|
|
--request-source "$REQUEST_SOURCE"
|
|
|
|
- name: Fail when Mantis Telegram failed
|
|
if: ${{ always() && steps.run_mantis.outputs.output_dir != '' && (steps.run_mantis.outputs.comparison_status != 'pass' || steps.run_mantis.outputs.telegram_exit != '0') }}
|
|
env:
|
|
COMPARISON_STATUS: ${{ steps.run_mantis.outputs.comparison_status }}
|
|
TELEGRAM_EXIT: ${{ steps.run_mantis.outputs.telegram_exit }}
|
|
run: |
|
|
echo "Mantis Telegram live failed: comparison=${COMPARISON_STATUS:-unset} telegram_exit=${TELEGRAM_EXIT:-unset}." >&2
|
|
exit 1
|
|
|
|
clear_issue_comment_reaction:
|
|
name: Clear Mantis command reaction
|
|
needs: [resolve_request, validate_ref, run_telegram_live]
|
|
if: ${{ always() && github.event_name == 'issue_comment' && needs.resolve_request.outputs.request_source == 'issue_comment' }}
|
|
runs-on: ubuntu-24.04
|
|
permissions:
|
|
issues: write
|
|
steps:
|
|
- name: Remove workflow eyes reaction
|
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
|
|
with:
|
|
script: |
|
|
const { owner, repo } = context.repo;
|
|
const commentId = context.payload.comment?.id;
|
|
if (!commentId) {
|
|
core.info("No issue comment id found; skipping reaction cleanup.");
|
|
return;
|
|
}
|
|
|
|
const reactions = await github.paginate(github.rest.reactions.listForIssueComment, {
|
|
owner,
|
|
repo,
|
|
comment_id: commentId,
|
|
per_page: 100,
|
|
});
|
|
const eyes = reactions.filter(
|
|
(reaction) => reaction.content === "eyes" && reaction.user?.login === "github-actions[bot]",
|
|
);
|
|
for (const reaction of eyes) {
|
|
await github.rest.reactions.deleteForIssueComment({
|
|
owner,
|
|
repo,
|
|
comment_id: commentId,
|
|
reaction_id: reaction.id,
|
|
});
|
|
core.info(`Removed eyes reaction ${reaction.id} from comment ${commentId}.`);
|
|
}
|
|
if (eyes.length === 0) {
|
|
core.info(`No workflow eyes reaction found on comment ${commentId}.`);
|
|
}
|