Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
348 lines
16 KiB
YAML
348 lines
16 KiB
YAML
name: ClawSweeper Dispatch
|
|
|
|
on:
|
|
issues:
|
|
types: [opened, reopened, edited, labeled, unlabeled]
|
|
issue_comment:
|
|
types: [created, edited]
|
|
push:
|
|
branches: [main]
|
|
pull_request_target: # zizmor: ignore[dangerous-triggers] maintainer-owned external dispatch; no checkout or untrusted PR code execution
|
|
types: [opened, reopened, synchronize, ready_for_review, edited, labeled, unlabeled]
|
|
pull_request_review:
|
|
types: [submitted, edited, dismissed]
|
|
pull_request_review_comment:
|
|
types: [created, edited]
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
concurrency:
|
|
group: ${{ github.event_name == 'push' && format('clawsweeper-dispatch-{0}-{1}', github.repository, github.ref) || format('clawsweeper-dispatch-{0}-{1}', github.repository, github.event.issue.number || github.event.pull_request.number || github.run_id) }}
|
|
cancel-in-progress: ${{ github.event_name == 'push' || github.event.action == 'edited' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review' }}
|
|
|
|
jobs:
|
|
dispatch:
|
|
runs-on: ubuntu-latest
|
|
if: >-
|
|
${{
|
|
(github.event_name != 'issue_comment' ||
|
|
(github.actor != 'clawsweeper[bot]' && github.actor != 'openclaw-clawsweeper[bot]')) &&
|
|
!(
|
|
endsWith(github.actor, '[bot]') &&
|
|
(github.event.action == 'labeled' || github.event.action == 'unlabeled')
|
|
)
|
|
}}
|
|
env:
|
|
HAS_CLAWSWEEPER_APP_PRIVATE_KEY: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY != '' }}
|
|
CLAWSWEEPER_APP_CLIENT_ID: Iv23liOECG0slfuhz093
|
|
SUPERSEDES_IN_PROGRESS: ${{ (github.event.action == 'edited' || github.event.action == 'synchronize' || github.event.action == 'ready_for_review') && 'true' || 'false' }}
|
|
steps:
|
|
- name: Debounce bursty metadata events
|
|
if: ${{ github.event.action == 'labeled' || github.event.action == 'unlabeled' }}
|
|
run: sleep 20
|
|
|
|
- name: Debounce main push dispatch
|
|
if: ${{ github.event_name == 'push' }}
|
|
run: sleep 45
|
|
|
|
- name: Install GitHub API backoff helper
|
|
run: |
|
|
cat > "$RUNNER_TEMP/github-api-backoff.sh" <<'BASH'
|
|
gh_api_with_retry() {
|
|
local attempt output status lower_output
|
|
for attempt in 1 2 3 4 5; do
|
|
if output="$(gh api "$@" 2>&1)"; then
|
|
printf '%s\n' "$output"
|
|
return 0
|
|
fi
|
|
status=$?
|
|
lower_output="${output,,}"
|
|
if [[ "$lower_output" != *"rate limit"* && "$output" != *"HTTP 429"* ]]; then
|
|
printf '%s\n' "$output" >&2
|
|
return "$status"
|
|
fi
|
|
echo "::warning::GitHub API throttled ClawSweeper dispatch on attempt ${attempt}; retrying after backoff." >&2
|
|
sleep $((attempt * attempt * 5))
|
|
done
|
|
printf '%s\n' "$output" >&2
|
|
return "$status"
|
|
}
|
|
BASH
|
|
|
|
- name: Create ClawSweeper dispatch token
|
|
id: token
|
|
if: ${{ env.HAS_CLAWSWEEPER_APP_PRIVATE_KEY == 'true' }}
|
|
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
|
with:
|
|
client-id: ${{ env.CLAWSWEEPER_APP_CLIENT_ID }}
|
|
private-key: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY }}
|
|
owner: openclaw
|
|
repositories: clawsweeper
|
|
permission-contents: write
|
|
|
|
- name: Pre-filter ClawSweeper comment
|
|
id: comment_filter
|
|
if: ${{ github.event_name == 'issue_comment' }}
|
|
env:
|
|
COMMENT_BODY: ${{ github.event.comment.body }}
|
|
run: |
|
|
set -euo pipefail
|
|
if grep -Eiq '(^|[[:space:]])@(clawsweeper|openclaw-clawsweeper)\b(\[bot\])?|(^|[[:space:]])/(clawsweeper|review|autoclose|auto([[:space:]]+|-)?merge)\b' <<< "$COMMENT_BODY"; then
|
|
echo "is_command=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "is_command=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
- name: Create target comment token
|
|
id: target_token
|
|
if: >-
|
|
${{
|
|
github.event_name == 'issue_comment' &&
|
|
steps.comment_filter.outputs.is_command == 'true' &&
|
|
env.HAS_CLAWSWEEPER_APP_PRIVATE_KEY == 'true'
|
|
}}
|
|
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
|
with:
|
|
client-id: ${{ env.CLAWSWEEPER_APP_CLIENT_ID }}
|
|
private-key: ${{ secrets.CLAWSWEEPER_APP_PRIVATE_KEY }}
|
|
owner: ${{ github.repository_owner }}
|
|
repositories: ${{ github.event.repository.name }}
|
|
permission-issues: write
|
|
permission-pull-requests: read
|
|
|
|
- name: Dispatch GitHub activity to ClawSweeper
|
|
env:
|
|
GH_TOKEN: ${{ steps.token.outputs.token }}
|
|
TARGET_REPO: ${{ github.repository }}
|
|
SOURCE_EVENT: ${{ github.event_name }}
|
|
SOURCE_ACTION: ${{ github.event.action }}
|
|
ACTOR: ${{ github.actor }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -z "$GH_TOKEN" ]; then
|
|
echo "::notice::Skipping GitHub activity dispatch because no ClawSweeper app token is configured."
|
|
exit 0
|
|
fi
|
|
. "$RUNNER_TEMP/github-api-backoff.sh"
|
|
activity="$(jq -c \
|
|
--arg target_repo "$TARGET_REPO" \
|
|
--arg event_name "$SOURCE_EVENT" \
|
|
--arg source_action "$SOURCE_ACTION" \
|
|
--arg actor "$ACTOR" \
|
|
'
|
|
def body_excerpt(value):
|
|
if (value // "" | type) == "string" then
|
|
((value // "") | gsub("\\s+"; " ") | .[0:1200])
|
|
else null end;
|
|
{
|
|
type: $event_name,
|
|
repo: $target_repo,
|
|
action: $source_action,
|
|
actor: $actor,
|
|
subject: (
|
|
if .pull_request then {
|
|
kind: "pull_request",
|
|
number: .pull_request.number,
|
|
title: .pull_request.title,
|
|
url: .pull_request.html_url,
|
|
state: (if .pull_request.merged == true then "merged" else .pull_request.state end)
|
|
} elif .issue then {
|
|
kind: (if .issue.pull_request then "pull_request" else "issue" end),
|
|
number: .issue.number,
|
|
title: .issue.title,
|
|
url: .issue.html_url,
|
|
state: .issue.state
|
|
} elif $event_name == "push" then {
|
|
kind: "push",
|
|
title: (.head_commit.message // .after // "push"),
|
|
url: (.head_commit.url // .compare),
|
|
state: .ref
|
|
} else {
|
|
kind: $event_name
|
|
} end),
|
|
comment: (if .comment then {
|
|
id: .comment.id,
|
|
url: .comment.html_url,
|
|
body_excerpt: body_excerpt(.comment.body)
|
|
} else null end),
|
|
review: (if .review then {
|
|
id: .review.id,
|
|
state: .review.state,
|
|
url: .review.html_url,
|
|
body_excerpt: body_excerpt(.review.body)
|
|
} else null end),
|
|
review_comment: (if .comment and $event_name == "pull_request_review_comment" then {
|
|
id: .comment.id,
|
|
path: .comment.path,
|
|
line: (.comment.line // .comment.original_line),
|
|
url: .comment.html_url,
|
|
body_excerpt: body_excerpt(.comment.body)
|
|
} else null end),
|
|
push: (if $event_name == "push" then {
|
|
before: .before,
|
|
after: .after,
|
|
ref: .ref,
|
|
compare: .compare,
|
|
head_commit: .head_commit.id
|
|
} else null end),
|
|
delivery_id: (.comment.id // .review.id // .pull_request.head.sha // .issue.updated_at // .after // env.GITHUB_RUN_ID)
|
|
} | del(.. | nulls)
|
|
' "$GITHUB_EVENT_PATH")"
|
|
payload="$(jq -nc --argjson activity "$activity" \
|
|
'{event_type:"github_activity",client_payload:{activity:$activity}}')"
|
|
if gh_api_with_retry repos/openclaw/clawsweeper/dispatches \
|
|
--method POST \
|
|
--input - <<< "$payload"; then
|
|
echo "Dispatched GitHub activity to ClawSweeper."
|
|
else
|
|
echo "::warning::Skipping GitHub activity dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
|
|
fi
|
|
|
|
- name: Dispatch exact ClawSweeper review
|
|
if: ${{ github.event_name == 'issues' || github.event_name == 'pull_request_target' }}
|
|
env:
|
|
GH_TOKEN: ${{ steps.token.outputs.token }}
|
|
TARGET_REPO: ${{ github.repository }}
|
|
ITEM_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
|
|
ITEM_KIND: ${{ github.event_name == 'pull_request_target' && 'pull_request' || 'issue' }}
|
|
SOURCE_EVENT: ${{ github.event_name }}
|
|
SOURCE_ACTION: ${{ github.event.action }}
|
|
run: |
|
|
if [ -z "$GH_TOKEN" ]; then
|
|
echo "::notice::Skipping ClawSweeper dispatch because no ClawSweeper app token is configured. Not falling back to a maintainer token."
|
|
exit 0
|
|
fi
|
|
. "$RUNNER_TEMP/github-api-backoff.sh"
|
|
payload="$(jq -nc \
|
|
--arg target_repo "$TARGET_REPO" \
|
|
--argjson item_number "$ITEM_NUMBER" \
|
|
--arg item_kind "$ITEM_KIND" \
|
|
--arg source_event "$SOURCE_EVENT" \
|
|
--arg source_action "$SOURCE_ACTION" \
|
|
--argjson supersedes_in_progress "$SUPERSEDES_IN_PROGRESS" \
|
|
'{event_type:"clawsweeper_item",client_payload:{target_repo:$target_repo,item_number:$item_number,item_kind:$item_kind,source_event:$source_event,source_action:$source_action,supersedes_in_progress:$supersedes_in_progress}}')"
|
|
if gh_api_with_retry repos/openclaw/clawsweeper/dispatches \
|
|
--method POST \
|
|
--input - <<< "$payload"; then
|
|
echo "Dispatched ClawSweeper review."
|
|
else
|
|
echo "::warning::Skipping ClawSweeper dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
|
|
fi
|
|
|
|
- name: Acknowledge and dispatch ClawSweeper comment
|
|
if: >-
|
|
${{
|
|
github.event_name == 'issue_comment' &&
|
|
steps.comment_filter.outputs.is_command == 'true'
|
|
}}
|
|
env:
|
|
DISPATCH_TOKEN: ${{ steps.token.outputs.token }}
|
|
TARGET_TOKEN: ${{ steps.target_token.outputs.token }}
|
|
TARGET_REPO: ${{ github.repository }}
|
|
ITEM_NUMBER: ${{ github.event.issue.number }}
|
|
COMMENT_ID: ${{ github.event.comment.id }}
|
|
COMMENT_BODY: ${{ github.event.comment.body }}
|
|
AUTHOR_ASSOCIATION: ${{ github.event.comment.author_association }}
|
|
SOURCE_ACTION: ${{ github.event.action }}
|
|
run: |
|
|
set -euo pipefail
|
|
if [ -z "$DISPATCH_TOKEN" ]; then
|
|
echo "::notice::Skipping ClawSweeper comment dispatch because no ClawSweeper app token is configured."
|
|
exit 0
|
|
fi
|
|
. "$RUNNER_TEMP/github-api-backoff.sh"
|
|
body_file="$RUNNER_TEMP/clawsweeper-comment-body.txt"
|
|
printf '%s\n' "$COMMENT_BODY" > "$body_file"
|
|
if [ -n "$TARGET_TOKEN" ]; then
|
|
err="$(mktemp)"
|
|
if GH_TOKEN="$TARGET_TOKEN" gh_api_with_retry -X POST \
|
|
-H "Accept: application/vnd.github+json" \
|
|
"repos/$TARGET_REPO/issues/comments/$COMMENT_ID/reactions" \
|
|
-f content="eyes" 2>"$err" >/dev/null; then
|
|
echo "Acknowledged ClawSweeper command comment."
|
|
elif grep -qi "HTTP 422\\|already exists" "$err"; then
|
|
echo "ClawSweeper command comment already acknowledged."
|
|
else
|
|
cat "$err" >&2
|
|
echo "::warning::Could not acknowledge ClawSweeper command comment."
|
|
fi
|
|
rm -f "$err"
|
|
else
|
|
echo "::notice::Skipping ClawSweeper comment acknowledgement because no target token is configured."
|
|
fi
|
|
status_comment_id=""
|
|
if [ -n "$TARGET_TOKEN" ]; then
|
|
case "$AUTHOR_ASSOCIATION" in
|
|
OWNER|MEMBER|COLLABORATOR)
|
|
status_body="$(printf '%s\n' \
|
|
"<!-- clawsweeper-command-ack:$COMMENT_ID -->" \
|
|
"🦞👀" \
|
|
"ClawSweeper picked this up." \
|
|
"" \
|
|
"Command router queued. I will update this comment with the next step.")"
|
|
status_payload="$(jq -nc --arg body "$status_body" '{body:$body}')"
|
|
status_err="$(mktemp)"
|
|
if status_response="$(GH_TOKEN="$TARGET_TOKEN" gh_api_with_retry \
|
|
"repos/$TARGET_REPO/issues/$ITEM_NUMBER/comments" \
|
|
--method POST \
|
|
--input - <<< "$status_payload" 2>"$status_err")"; then
|
|
status_comment_id="$(jq -r '.id // empty' <<< "$status_response")"
|
|
else
|
|
cat "$status_err" >&2
|
|
echo "::warning::Could not create ClawSweeper queued status comment; dispatching command router without one."
|
|
fi
|
|
rm -f "$status_err"
|
|
;;
|
|
esac
|
|
fi
|
|
payload="$(jq -nc \
|
|
--arg target_repo "$TARGET_REPO" \
|
|
--argjson item_number "$ITEM_NUMBER" \
|
|
--argjson comment_id "$COMMENT_ID" \
|
|
--arg status_comment_id "$status_comment_id" \
|
|
--arg source_event "issue_comment" \
|
|
--arg source_action "$SOURCE_ACTION" \
|
|
'{event_type:"clawsweeper_comment",client_payload:({target_repo:$target_repo,item_number:$item_number,comment_id:$comment_id,source_event:$source_event,source_action:$source_action,max_comments:"1"} + (if $status_comment_id != "" then {status_comment_id:($status_comment_id|tonumber)} else {} end))}')"
|
|
if GH_TOKEN="$DISPATCH_TOKEN" gh_api_with_retry repos/openclaw/clawsweeper/dispatches \
|
|
--method POST \
|
|
--input - <<< "$payload"; then
|
|
echo "Dispatched ClawSweeper comment router."
|
|
else
|
|
echo "::warning::Skipping ClawSweeper comment dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
|
|
fi
|
|
|
|
- name: Dispatch ClawSweeper commit review
|
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.event.deleted != true }}
|
|
env:
|
|
GH_TOKEN: ${{ steps.token.outputs.token }}
|
|
TARGET_REPO: ${{ github.repository }}
|
|
BEFORE_SHA: ${{ github.event.before }}
|
|
AFTER_SHA: ${{ github.sha }}
|
|
SOURCE_REF: ${{ github.ref }}
|
|
CREATE_CHECKS: ${{ vars.CLAWSWEEPER_COMMIT_REVIEW_CREATE_CHECKS || 'false' }}
|
|
run: |
|
|
if [ -z "$GH_TOKEN" ]; then
|
|
echo "::notice::Skipping ClawSweeper commit dispatch because no ClawSweeper app token is configured. Not falling back to a maintainer token."
|
|
exit 0
|
|
fi
|
|
. "$RUNNER_TEMP/github-api-backoff.sh"
|
|
case "$CREATE_CHECKS" in
|
|
true|TRUE|1|yes|YES|on|ON) create_checks=true ;;
|
|
*) create_checks=false ;;
|
|
esac
|
|
payload="$(jq -nc \
|
|
--arg target_repo "$TARGET_REPO" \
|
|
--arg before_sha "$BEFORE_SHA" \
|
|
--arg after_sha "$AFTER_SHA" \
|
|
--arg ref "$SOURCE_REF" \
|
|
--argjson create_checks "$create_checks" \
|
|
'{event_type:"clawsweeper_commit_review",client_payload:{target_repo:$target_repo,before_sha:$before_sha,after_sha:$after_sha,ref:$ref,enabled:true,create_checks:$create_checks}}')"
|
|
if gh_api_with_retry repos/openclaw/clawsweeper/dispatches \
|
|
--method POST \
|
|
--input - <<< "$payload"; then
|
|
echo "Dispatched ClawSweeper commit review."
|
|
else
|
|
echo "::warning::Skipping ClawSweeper commit dispatch because the configured credential could not dispatch to openclaw/clawsweeper."
|
|
fi
|