























@@ -5,19 +5,9 @@ on:
55types: [created]
66workflow_dispatch:
77inputs:
8-baseline_ref:
9-description: Ref, tag, or SHA to capture as the before GIF
10-required: true
11-default: main
12-type: string
13-candidate_ref:
14-description: Ref, tag, or SHA to capture as the after GIF
15-required: true
16-default: main
17-type: string
188pr_number:
19-description: Optional PR number to receive the QA evidence comment
20-required: false
9+description: PR number to capture
10+required: true
2111type: string
2212instructions:
2313description: Optional freeform proof instructions for the agent
@@ -35,19 +25,14 @@ on:
3525description: Optional existing Crabbox desktop lease id or slug to reuse
3626required: false
3727type: string
38-allow_fork_candidate:
39-description: Allow a fork PR head candidate when pr_number points at that PR
40-required: false
41-default: false
42-type: boolean
43284429permissions:
4530contents: write
4631issues: write
4732pull-requests: write
48334934concurrency:
50-group: mantis-telegram-desktop-proof-${{ github.event.issue.number || inputs.pr_number || inputs.candidate_ref || github.run_id }}-${{ github.run_attempt }}
35+group: mantis-telegram-desktop-proof-${{ github.event.issue.number || inputs.pr_number || github.run_id }}-${{ github.run_attempt }}
5136cancel-in-progress: false
52375338env:
@@ -68,6 +53,7 @@ jobs:
6853 (
6954 github.event_name == 'issue_comment' &&
7055 github.event.issue.pull_request &&
56+ contains(github.event.issue.labels.*.name, 'mantis: telegram-visible-proof') &&
7157 (
7258 contains(github.event.comment.body, '@openclaw-mantis') ||
7359 contains(github.event.comment.body, '/openclaw-mantis')
@@ -100,15 +86,13 @@ jobs:
10086needs: authorize_actor
10187runs-on: ubuntu-24.04
10288outputs:
103-allow_fork_candidate: ${{ steps.resolve.outputs.allow_fork_candidate }}
10489baseline_ref: ${{ steps.resolve.outputs.baseline_ref }}
10590candidate_ref: ${{ steps.resolve.outputs.candidate_ref }}
10691crabbox_provider: ${{ steps.resolve.outputs.crabbox_provider }}
10792instructions: ${{ steps.resolve.outputs.instructions }}
10893lease_id: ${{ steps.resolve.outputs.lease_id }}
10994pr_number: ${{ steps.resolve.outputs.pr_number }}
11095request_source: ${{ steps.resolve.outputs.request_source }}
111-should_run: ${{ steps.resolve.outputs.should_run }}
11296steps:
11397 - name: Resolve refs and target PR
11498id: resolve
@@ -122,110 +106,47 @@ jobs:
122106 core.info(`${name}=${value ?? ""}`);
123107 }
124108125- if (eventName === "workflow_dispatch") {
126- const inputs = context.payload.inputs ?? {};
127- setOutput("should_run", "true");
128- setOutput(
129- "allow_fork_candidate",
130- String(inputs.allow_fork_candidate) === "true" ? "true" : "false",
131- );
132- setOutput("baseline_ref", inputs.baseline_ref || "main");
133- setOutput("candidate_ref", inputs.candidate_ref || "main");
134- setOutput("pr_number", inputs.pr_number || "");
135- setOutput("instructions", inputs.instructions || "");
136- setOutput("crabbox_provider", inputs.crabbox_provider || "aws");
137- setOutput("lease_id", inputs.crabbox_lease_id || "");
138- setOutput("request_source", "workflow_dispatch");
139- return;
140- }
141-142- if (eventName !== "issue_comment") {
143- core.setFailed(`Unsupported event: ${eventName}`);
144- return;
145- }
146-147- const issue = context.payload.issue;
148- const body = context.payload.comment?.body ?? "";
149- if (!issue?.pull_request) {
150- core.setFailed("Mantis issue_comment trigger requires a pull request comment.");
151- return;
152- }
153-154- const normalized = body.toLowerCase();
155- const requested =
156- (normalized.includes("@openclaw-mantis") || normalized.includes("/openclaw-mantis")) &&
157- normalized.includes("telegram") &&
158- (normalized.includes("desktop") || normalized.includes("native")) &&
159- normalized.includes("proof");
160- if (!requested) {
161- core.notice("Comment mentioned Mantis but did not request Telegram desktop proof.");
162- setOutput("should_run", "false");
163- setOutput("allow_fork_candidate", "false");
164- setOutput("baseline_ref", "");
165- setOutput("candidate_ref", "");
166- setOutput("pr_number", "");
167- setOutput("instructions", "");
168- setOutput("crabbox_provider", "");
169- setOutput("lease_id", "");
170- setOutput("request_source", "unsupported_issue_comment");
109+ const inputs = context.payload.inputs ?? {};
110+ const prNumber =
111+ eventName === "workflow_dispatch" ? inputs.pr_number : String(context.payload.issue?.number ?? "");
112+ if (!prNumber) {
113+ core.setFailed("Mantis Telegram desktop proof requires a pull request.");
171114 return;
172115 }
173116174117 const { owner, repo } = context.repo;
175118 const { data: pr } = await github.rest.pulls.get({
176119 owner,
177120 repo,
178- pull_number: issue.number,
121+ pull_number: Number(prNumber),
179122 });
180- let mergedBaseline = "";
181- let mergedCandidate = "";
182- if (pr.merged) {
183- const { data: commits } = await github.rest.pulls.listCommits({
184- owner,
185- repo,
186- pull_number: issue.number,
187- per_page: 100,
188- });
189- mergedCandidate = pr.merge_commit_sha || commits.at(-1)?.sha || "";
190- mergedBaseline = mergedCandidate && commits.length > 0 ? `${mergedCandidate}~${commits.length}` : "";
191- }
192- const baselineMatch = body.match(/(?:baseline|base)[\s:=]+([^\s`]+)/i);
193- const candidateMatch = body.match(/(?:candidate|head)[\s:=]+([^\s`]+)/i);
194- const providerMatch = body.match(/(?:provider|crabbox_provider)[\s:=]+([^\s`]+)/i);
195- const leaseMatch = body.match(/(?:lease|lease_id|crabbox_lease_id)[\s:=]+([^\s`]+)/i);
196- const provider = providerMatch?.[1] || "aws";
123+ const body = eventName === "workflow_dispatch" ? inputs.instructions || "" : context.payload.comment?.body || "";
124+ const provider = inputs.crabbox_provider || "aws";
197125 if (!["aws", "hetzner"].includes(provider)) {
198126 core.setFailed(`Unsupported Crabbox provider for Mantis Telegram desktop proof: ${provider}`);
199127 return;
200128 }
201- const rawCandidate = candidateMatch?.[1];
202- const candidate =
203- rawCandidate && !["head", "pr", "pr-head"].includes(rawCandidate.toLowerCase())
204- ? rawCandidate
205- : mergedCandidate || pr.head.sha;
206- const allowForkCandidate = /\bfork[-_]ok\b/i.test(body);
207129208- setOutput("should_run", "true");
209- setOutput("allow_fork_candidate", allowForkCandidate ? "true" : "false");
210- setOutput("baseline_ref", baselineMatch?.[1] || mergedBaseline || "main");
211- setOutput("candidate_ref", candidate);
212- setOutput("pr_number", String(issue.number));
130+ setOutput("baseline_ref", pr.base.sha);
131+ setOutput("candidate_ref", pr.head.sha);
132+ setOutput("pr_number", String(pr.number));
213133 setOutput("instructions", body);
214134 setOutput("crabbox_provider", provider);
215- setOutput("lease_id", leaseMatch?.[1] || "");
216- setOutput("request_source", "issue_comment");
135+ setOutput("lease_id", inputs.crabbox_lease_id || "");
136+ setOutput("request_source", eventName);
217137218- await github.rest.reactions.createForIssueComment({
219- owner,
220- repo,
221- comment_id: context.payload.comment.id,
222- content: "eyes",
223- }).catch((error) => core.warning(`Could not add eyes reaction: ${error.message}`));
138+ if (eventName === "issue_comment") {
139+ await github.rest.reactions.createForIssueComment({
140+ owner,
141+ repo,
142+ comment_id: context.payload.comment.id,
143+ content: "eyes",
144+ }).catch((error) => core.warning(`Could not add eyes reaction: ${error.message}`));
145+ }
224146225147 validate_refs:
226148name: Validate selected refs
227149needs: resolve_request
228-if: ${{ needs.resolve_request.outputs.should_run == 'true' }}
229150runs-on: ubuntu-24.04
230151outputs:
231152baseline_revision: ${{ steps.validate.outputs.baseline_revision }}
@@ -241,7 +162,6 @@ jobs:
241162 - name: Validate refs are trusted
242163id: validate
243164env:
244-ALLOW_FORK_CANDIDATE: ${{ needs.resolve_request.outputs.allow_fork_candidate }}
245165BASELINE_REF: ${{ needs.resolve_request.outputs.baseline_ref }}
246166CANDIDATE_REF: ${{ needs.resolve_request.outputs.candidate_ref }}
247167GH_TOKEN: ${{ github.token }}
@@ -255,64 +175,48 @@ jobs:
255175 git fetch --no-tags origin "+refs/pull/${PR_NUMBER}/head:refs/remotes/origin/pr/${PR_NUMBER}" || true
256176 fi
257177258- validate_ref() {
259- local label="$1"
178+ resolve_commit() {
260179 local input_ref="$2"
261180 local revision=""
262- local reason=""
263181264182 if ! revision="$(git rev-parse --verify "${input_ref}^{commit}" 2>/dev/null)"; then
265- echo "${label} ref '${input_ref}' is not available in the workflow checkout." >&2
183+ echo "$1 ref '${input_ref}' is not available in the workflow checkout." >&2
266184 exit 1
267185 fi
268- if git merge-base --is-ancestor "$revision" refs/remotes/origin/main; then
269- reason="main-ancestor"
270- elif git tag --points-at "$revision" | grep -Eq '^v'; then
271- reason="release-tag"
272- else
273- local pr_head_count
274- pr_head_count="$(
275- gh api \
276- -H "Accept: application/vnd.github+json" \
277- "repos/${GITHUB_REPOSITORY}/commits/${revision}/pulls" \
278- --jq '[.[] | select(.state == "open" and .head.repo.full_name == "'"${GITHUB_REPOSITORY}"'" and .head.sha == "'"${revision}"'")] | length'
279- )"
280- if [[ "$pr_head_count" != "0" ]]; then
281- reason="open-pr-head"
282- elif [[ "$label" == "candidate" && "${ALLOW_FORK_CANDIDATE:-false}" == "true" && -n "${PR_NUMBER:-}" ]]; then
283- local fork_pr_head_count
284- fork_pr_head_count="$(
285- gh api \
286- -H "Accept: application/vnd.github+json" \
287- "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" \
288- --jq 'if .state == "open" and .head.repo.full_name != "'"${GITHUB_REPOSITORY}"'" and .head.sha == "'"${revision}"'" then 1 else 0 end'
289- )"
290- if [[ "$fork_pr_head_count" == "1" ]]; then
291- reason="maintainer-approved-fork-pr-head"
292- fi
293- fi
294- fi
295-296- if [[ -z "$reason" ]]; then
297- echo "${label} ref '${input_ref}' resolved to ${revision}, which is not trusted for this secret-bearing Mantis run. Add fork-ok only for a maintainer-approved fork PR head." >&2
298- exit 1
299- fi
300- printf '%s\t%s\n' "$revision" "$reason"
186+ printf '%s\n' "$revision"
301187 }
302188303- baseline_revision="$(validate_ref baseline "$BASELINE_REF")"
304- baseline_trust="${baseline_revision#*$'\t'}"
305- baseline_revision="${baseline_revision%%$'\t'*}"
306- candidate_revision="$(validate_ref candidate "$CANDIDATE_REF")"
307- candidate_trust="${candidate_revision#*$'\t'}"
308- candidate_revision="${candidate_revision%%$'\t'*}"
189+ baseline_revision="$(resolve_commit baseline "$BASELINE_REF")"
190+ candidate_revision="$(resolve_commit candidate "$CANDIDATE_REF")"
191+ if ! git merge-base --is-ancestor "$baseline_revision" refs/remotes/origin/main; then
192+ echo "baseline ref '${BASELINE_REF}' resolved to ${baseline_revision}, which is not on main." >&2
193+ exit 1
194+ fi
195+ pr_head="$(
196+ gh api \
197+ -H "Accept: application/vnd.github+json" \
198+ "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" \
199+ --jq '{state, head_sha: .head.sha, head_repo: .head.repo.full_name}'
200+ )"
201+ pr_state="$(jq -r '.state' <<<"$pr_head")"
202+ pr_head_sha="$(jq -r '.head_sha' <<<"$pr_head")"
203+ pr_head_repo="$(jq -r '.head_repo' <<<"$pr_head")"
204+ if [[ "$pr_state" != "open" || "$candidate_revision" != "$pr_head_sha" ]]; then
205+ echo "candidate ref '${CANDIDATE_REF}' resolved to ${candidate_revision}, which is not the open PR head." >&2
206+ exit 1
207+ fi
208+ candidate_trust="open-pr-head"
209+ if [[ "$pr_head_repo" != "$GITHUB_REPOSITORY" ]]; then
210+ candidate_trust="fork-pr-head"
211+ fi
212+309213 echo "baseline_revision=${baseline_revision}" >> "$GITHUB_OUTPUT"
310214 echo "candidate_revision=${candidate_revision}" >> "$GITHUB_OUTPUT"
311215 echo "candidate_trust=${candidate_trust}" >> "$GITHUB_OUTPUT"
312216 {
313217 echo "baseline: \`${BASELINE_REF}\`"
314218 echo "baseline SHA: \`${baseline_revision}\`"
315- echo "baseline trust: \`${baseline_trust}\`"
219+ echo "baseline trust: \`main-ancestor\`"
316220 echo "candidate: \`${CANDIDATE_REF}\`"
317221 echo "candidate SHA: \`${candidate_revision}\`"
318222 echo "candidate trust: \`${candidate_trust}\`"
@@ -321,7 +225,6 @@ jobs:
321225 run_telegram_desktop_proof:
322226name: Run agentic native Telegram proof
323227needs: [resolve_request, validate_refs]
324-if: ${{ needs.resolve_request.outputs.should_run == 'true' }}
325228runs-on: blacksmith-16vcpu-ubuntu-2404
326229timeout-minutes: 360
327230environment: qa-live-shared
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。