Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/qwen-triage-finalize.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ jobs:
CI_BEGIN="<!-- qwen-triage-ci sha=${HEAD_SHA} -->"
CI_END='<!-- /qwen-triage-ci -->'
APPROVE_MARKER="<!-- qwen-triage approve-on-green sha=${HEAD_SHA} -->"
STATUS_MARKER='<!-- qwen-triage stage=status -->'
STATUS_MARKER='<!-- qwen-triage lifecycle -->'
LEGACY_STATUS_MARKER='<!-- qwen-triage stage=status -->'

if ! BOT_LOGIN="$(gh api user --jq '.login')" || [ -z "$BOT_LOGIN" ]; then
echo "::warning::Cannot resolve the bot identity; skipping (markers cannot be author-verified)."
Expand Down Expand Up @@ -320,8 +321,8 @@ jobs:
;;
*) return 0 ;;
esac
sid="$(jq -r --arg bot "$BOT_LOGIN" --arg m "$STATUS_MARKER" \
'[.[] | select(.user.login == $bot) | select(.body | contains($m))] | last | .id // empty' \
sid="$(jq -r --arg bot "$BOT_LOGIN" --arg m "$STATUS_MARKER" --arg legacy "$LEGACY_STATUS_MARKER" \
'[.[] | select(.user.login == $bot) | select((.body | startswith($m)) or (.body | startswith($legacy)))] | last | .id // empty' \
"/tmp/comments-$pr.json")"
[ -n "$sid" ] || return 0
printf '%s\n\n%s\n\n%s' "$STATUS_MARKER" "$en" "$zh" > /tmp/status-body.md
Expand Down
16 changes: 10 additions & 6 deletions .github/workflows/qwen-triage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -311,16 +311,18 @@ jobs:
RUN_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
run: |-
set -uo pipefail
MARKER='<!-- qwen-triage stage=status -->'
MARKER='<!-- qwen-triage lifecycle -->'
LEGACY_MARKER='<!-- qwen-triage stage=status -->'
BOT_LOGIN="$(gh api user --jq '.login' 2>/dev/null || true)"
printf -v BODY '%s\n\n%s\n\n%s' \
Comment thread
yiliang114 marked this conversation as resolved.
Outdated
"$MARKER" \
"🔄 **Qwen Triage is running** — [watch live progress]($RUN_URL). Stage results will post in this thread as they complete." \
"🔄 **Qwen Triage 正在运行** —— [查看实时进度]($RUN_URL)。各阶段结果完成后会更新在本线程。"
EXISTING_ID="$(
gh api "repos/$GITHUB_REPOSITORY/issues/$NUMBER/comments" \
--method GET --paginate -F per_page=100 |
jq -rs --arg m "$MARKER" \
'[.[][] | select(.body | contains($m))] | last | .id // empty'
jq -rs --arg m "$MARKER" --arg legacy "$LEGACY_MARKER" --arg bot "$BOT_LOGIN" \
'[.[][] | select(.user.login == $bot) | select((.body | startswith($m)) or (.body | startswith($legacy)))] | last | .id // empty'
)" || EXISTING_ID=''
if [ -n "$EXISTING_ID" ]; then
gh api --method PATCH \
Expand Down Expand Up @@ -673,7 +675,9 @@ jobs:
TRIAGE_OUTCOME: '${{ steps.triage.outcome }}'
run: |-
set -uo pipefail
MARKER='<!-- qwen-triage stage=status -->'
MARKER='<!-- qwen-triage lifecycle -->'
LEGACY_MARKER='<!-- qwen-triage stage=status -->'
BOT_LOGIN="$(gh api user --jq '.login' 2>/dev/null || true)"
if [ "${TRIAGE_OUTCOME:-}" = 'success' ]; then
Comment thread
yiliang114 marked this conversation as resolved.
Outdated
EN="✅ **Qwen Triage finished** — [view run]($RUN_URL). See the stage comments in this thread for the result."
ZH="✅ **Qwen Triage 已完成** —— [查看运行]($RUN_URL)。结果见本线程中的各阶段评论。"
Expand All @@ -685,8 +689,8 @@ jobs:
EXISTING_ID="$(
gh api "repos/$GITHUB_REPOSITORY/issues/$NUMBER/comments" \
--method GET --paginate -F per_page=100 |
jq -rs --arg m "$MARKER" \
'[.[][] | select(.body | contains($m))] | last | .id // empty'
jq -rs --arg m "$MARKER" --arg legacy "$LEGACY_MARKER" --arg bot "$BOT_LOGIN" \
'[.[][] | select(.user.login == $bot) | select((.body | startswith($m)) or (.body | startswith($legacy)))] | last | .id // empty'
)" || EXISTING_ID=''
if [ -n "$EXISTING_ID" ]; then
gh api --method PATCH \
Expand Down
13 changes: 12 additions & 1 deletion scripts/tests/qwen-triage-finalize-workflow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,17 @@ describe('qwen-triage-finalize workflow', () => {
// Marker text is public: anyone can paste it into a PR comment. Every
// lookup that acts on a marker must filter on the bot identity first.
expect(script).toContain("gh api user --jq '.login'");
// Status-comment lookup: body starts with the marker (a comment that
// merely quotes it must not be PATCHed), so startswith is the strict match.
expect(script).toContain(
'select(.user.login == $bot) | select((.body | startswith($m)) or (.body | startswith($legacy)))',
);
// CI-region, approve-marker, and new-head-marker lookups embed the marker
// inside the body (not at the start), so contains is the correct match —
// still author-filtered.
const authorFiltered = script.match(
/select\(\.user\.login == \$bot\) \| select\(\.body \| contains\(\$m\)\)/g,
);
// CI-region lookup, approve-marker lookup, and status-comment lookup.
expect(authorFiltered?.length).toBeGreaterThanOrEqual(3);
});

Expand Down Expand Up @@ -139,6 +146,10 @@ describe('qwen-triage-finalize workflow', () => {
expect(workflowText).toContain(
"HEAD_SHA: '${{ github.event.workflow_run.head_sha }}'",
);
expect(script).toContain("STATUS_MARKER='<!-- qwen-triage lifecycle -->'");
expect(script).toContain(
"LEGACY_STATUS_MARKER='<!-- qwen-triage stage=status -->'",
);
});

it('sanitizes attacker-influenced check names before the table', () => {
Expand Down
22 changes: 19 additions & 3 deletions scripts/tests/qwen-triage-workflow.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,21 +218,37 @@ describe('qwen-triage tmux workflow', () => {
it('posts an early live-progress status comment and finalizes the same one', () => {
const statusStep = step('Post triage status comment');
// Announced up front (before the long agent step) with the live run link.
expect(statusStep).toContain('<!-- qwen-triage stage=status -->');
expect(statusStep).toContain("MARKER='<!-- qwen-triage lifecycle -->'");
expect(statusStep).toContain(
"LEGACY_MARKER='<!-- qwen-triage stage=status -->'",
);
expect(statusStep).toContain('actions/runs/${{ github.run_id }}');
expect(statusStep).toContain('watch live progress');
// Upsert by marker so a re-run reuses the one comment instead of stacking.
expect(statusStep).toContain('contains($m)');
// startswith (not contains) prevents matching a comment that merely quotes
// the marker; the bot-author filter prevents matching a human's comment.
expect(statusStep).toContain("gh api user --jq '.login'");
expect(statusStep).toContain('select(.user.login == $bot)');
expect(statusStep).toContain('startswith($m)');
expect(statusStep).not.toContain('contains($m)');
expect(statusStep).toContain('--method PATCH');
// Best-effort: a failed status post warns and continues, never fails triage.
expect(statusStep).toContain('set -uo pipefail');
expect(statusStep).toContain('continuing.');

const finalizeStep = step('Finalize triage status comment');
// Runs on both outcomes and edits the SAME marker comment (no second post).
expect(finalizeStep).toContain('<!-- qwen-triage stage=status -->');
expect(finalizeStep).toContain(
"MARKER='<!-- qwen-triage lifecycle -->'",
);
expect(finalizeStep).toContain(
"LEGACY_MARKER='<!-- qwen-triage stage=status -->'",
);
expect(finalizeStep).toContain('success() || failure()');
expect(finalizeStep).toContain('steps.triage.outcome');
expect(finalizeStep).toContain('select(.user.login == $bot)');
expect(finalizeStep).toContain('startswith($m)');
expect(finalizeStep).not.toContain('contains($m)');
expect(finalizeStep).toContain('--method PATCH');
expect(finalizeStep).toContain('Qwen Triage finished');
expect(finalizeStep).toContain('ended early');
Expand Down
Loading