mirror of
https://github.com/max-sixty/worktrunk.git
synced 2026-09-14 20:00:38 +08:00
1e2e05cfb4
Automated nightly regeneration of tend's workflow files, picking up tend 0.1.18. **tend version:** 0.1.17 → 0.1.18 Notable changes: - `tend-mention`: both halves of the 👀 reaction now live in the `handle` job. Previously `verify` added the eyes and `handle` removed them, so a burst of mentions on one thread could cancel a queued `handle` — which allocates no runner and runs no steps, `always()` included — leaving the reaction stranded (max-sixty/tend#990). - `tend-review` / `tend-triage`: the eyes-removal lookup now paginates (`--paginate`, `per_page=100`). A thread with more than 30 reactions could push the bot's own eyes off the first page, so the removal silently found nothing (max-sixty/tend#990). - `tend check`: the secret audit now reports only org secrets this repo can actually read, instead of every org secret (max-sixty/tend#994). - `running-in-ci` skill: notes that fork PR reviews reach no successor session, and scopes PR-description claims to the merge base (max-sixty/tend#985, max-sixty/tend#992). Full comparison: https://github.com/max-sixty/tend/compare/0.1.17...0.1.18 Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
161 lines
7.9 KiB
YAML
161 lines
7.9 KiB
YAML
# Generated by tend 0.1.18. Regenerate with: uvx tend@latest init
|
|
#
|
|
# Do not edit this file directly — it will be overwritten on regeneration.
|
|
# To customize behavior, edit the relevant skill (for example,
|
|
# `running-tend`) in this repo's .claude/skills/ directory, or open an issue at
|
|
# https://github.com/max-sixty/tend/issues for changes that need to
|
|
# happen upstream in the tend-ci-runner plugin.
|
|
|
|
name: tend-notifications
|
|
on:
|
|
schedule:
|
|
- cron: "*/15 * * * *"
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
notifications:
|
|
if: github.repository_owner == 'max-sixty'
|
|
runs-on: ubuntu-24.04
|
|
environment:
|
|
name: tend
|
|
deployment: false
|
|
permissions:
|
|
contents: write
|
|
pull-requests: write
|
|
actions: read
|
|
issues: write
|
|
steps:
|
|
- name: Check for unread notifications
|
|
id: check
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }}
|
|
BOT_NAME: worktrunk-bot
|
|
run: |
|
|
# shellcheck shell=bash
|
|
# Pre-check for tend-notifications: decide whether the agent needs to boot at
|
|
# all, and clear inbox noise no agent run is needed for.
|
|
#
|
|
# Inlined into the generated workflow (adopter repos have no copy of this
|
|
# file), so it stays self-contained: env in, GITHUB_OUTPUT out.
|
|
#
|
|
# env: BOT_NAME, GITHUB_REPOSITORY, GITHUB_OUTPUT, GITHUB_TOKEN
|
|
|
|
# Fetch notifications once, tolerating transient non-JSON responses.
|
|
# GitHub occasionally returns an HTML error page (even with a 200)
|
|
# during a brief API blip; under `bash -e` an untolerated `gh api`
|
|
# failure would abort the whole step red. A failed fetch just means
|
|
# this cycle can't enumerate — the next scheduled cycle picks up
|
|
# anything missed — so retry briefly, then skip cleanly.
|
|
#
|
|
# On every failed attempt, log what actually came back: the plain
|
|
# fetch hides the body behind `2>/dev/null`, and a transient blip
|
|
# that recovers on retry would otherwise leave no trace of the bad
|
|
# response. `gh api -i` re-fetches with the HTTP status line +
|
|
# headers ahead of the body, so a recurring failure shows the real
|
|
# status code and body — not just jq's parse complaint. It exits
|
|
# non-zero on an error status, hence `|| true`. The extra call runs
|
|
# only on the (rare) failing attempt; the happy path is one fetch.
|
|
NOTIFS=""
|
|
for attempt in 1 2 3; do
|
|
if NOTIFS=$(gh api notifications 2>/dev/null) && echo "$NOTIFS" | jq -e . >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
NOTIFS=""
|
|
echo "--- notifications fetch attempt $attempt failed; actual response (status + body head) ---"
|
|
gh api notifications -i 2>&1 | head -c 1000 || true
|
|
echo
|
|
[ "$attempt" -lt 3 ] && sleep "$attempt"
|
|
done
|
|
if [ -z "$NOTIFS" ]; then
|
|
echo "count=0" >> "$GITHUB_OUTPUT"
|
|
echo "notifications fetch failed after retries — skipping this cycle"
|
|
exit 0
|
|
fi
|
|
|
|
COUNT=$(echo "$NOTIFS" | jq 'length')
|
|
if [ "$COUNT" = "0" ]; then
|
|
echo "count=0" >> "$GITHUB_OUTPUT"
|
|
echo "No unread notifications — skipping"
|
|
exit 0
|
|
fi
|
|
|
|
# --- Layer B: drop notifications shadowed by recent dedicated runs ---
|
|
# Event workflows mark their own notifications read via the harness
|
|
# action's post-step on success; this sweeps the case where Claude failed
|
|
# (post-step is gated by `if: success()`) so the notification still
|
|
# gets cleared without burning Claude turns to rediscover it.
|
|
SINCE=$(date -u -d '30 minutes ago' +%Y-%m-%dT%H:%M:%SZ)
|
|
RECENT_PRS=$(gh api "repos/$GITHUB_REPOSITORY/actions/runs?created=>=$SINCE&per_page=50" --jq '[.workflow_runs[] | select(.name | test("^(tend-review|tend-mention|tend-triage|tend-ci-fix)$")) | .pull_requests[]?.number] | unique | .[]' || true)
|
|
|
|
if [ -n "$RECENT_PRS" ]; then
|
|
for pr in $RECENT_PRS; do
|
|
echo "$NOTIFS" | jq -r --arg repo "$GITHUB_REPOSITORY" --arg pr "$pr" '.[] | select(.subject.url == "https://api.github.com/repos/" + $repo + "/pulls/" + $pr or .subject.url == "https://api.github.com/repos/" + $repo + "/issues/" + $pr) | .id' | while read -r tid; do
|
|
[ -n "$tid" ] || continue
|
|
gh api "notifications/threads/$tid" -X PATCH || true
|
|
done
|
|
done
|
|
fi
|
|
|
|
# --- Layer C: drop notifications on bot-authored closed PRs ---
|
|
# The bot auto-subscribes to its own PRs. After merge/close, leftover
|
|
# subscription notifications are pure noise — no action needed.
|
|
# Reuses the snapshot fetched above; marking a thread read is
|
|
# idempotent, so a stale snapshot at worst re-PATCHes a read thread.
|
|
echo "$NOTIFS" | jq -r --arg repo "$GITHUB_REPOSITORY" '.[] | select(.repository.full_name == $repo and .subject.type == "PullRequest") | .id' | while read -r tid; do
|
|
[ -n "$tid" ] || continue
|
|
PR_NUM=$(echo "$NOTIFS" | jq -r --arg tid "$tid" '.[] | select(.id == $tid) | .subject.url | split("/") | last')
|
|
PR_INFO=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUM" --jq '"\(.user.login) \(.state)"' 2>/dev/null) || continue
|
|
PR_AUTHOR=${PR_INFO%% *}
|
|
PR_STATE=${PR_INFO##* }
|
|
if [ "$PR_AUTHOR" = "$BOT_NAME" ] && [ "$PR_STATE" = "closed" ]; then
|
|
gh api "notifications/threads/$tid" -X PATCH || true
|
|
fi
|
|
done
|
|
|
|
# --- Layer D: count processable notifications ---
|
|
# Same-repo notifications younger than 10 minutes are deferred: a
|
|
# dedicated workflow (tend-review/mention/triage/ci-fix) is likely
|
|
# still starting up or mid-flight and hasn't posted its response yet.
|
|
# Processing them now risks duplicating work. Cross-repo notifications
|
|
# are exempt — no dedicated workflow handles them.
|
|
CUTOFF=$(date -u -d '10 minutes ago' +%Y-%m-%dT%H:%M:%SZ)
|
|
# Re-fetch so the count reflects threads marked read in Layers B/C.
|
|
# Tolerate a transient failure by falling back to the pre-mutation
|
|
# snapshot — an over-count at worst spends one agent run, never fails.
|
|
if ! REMAINING=$(gh api notifications 2>/dev/null) || ! echo "$REMAINING" | jq -e . >/dev/null 2>&1; then
|
|
REMAINING="$NOTIFS"
|
|
fi
|
|
COUNT=$(echo "$REMAINING" | jq --arg repo "$GITHUB_REPOSITORY" --arg cutoff "$CUTOFF" '[.[] | select(.repository.full_name != $repo or .updated_at <= $cutoff)] | length')
|
|
echo "count=$COUNT" >> "$GITHUB_OUTPUT"
|
|
if [ "$COUNT" = "0" ]; then
|
|
TOTAL=$(echo "$REMAINING" | jq 'length')
|
|
if [ "$TOTAL" = "0" ]; then
|
|
echo "All notifications handled by pre-checks — skipping"
|
|
else
|
|
echo "$TOTAL notification(s) remain but all are fresh same-repo (deferred) — skipping"
|
|
fi
|
|
else
|
|
echo "$COUNT processable notification(s) — proceeding"
|
|
fi
|
|
|
|
- uses: actions/checkout@v7
|
|
if: steps.check.outputs.count != '0' || github.event_name == 'workflow_dispatch'
|
|
with:
|
|
ref: main
|
|
fetch-depth: 0
|
|
fetch-tags: true
|
|
token: ${{ secrets.TEND_BOT_TOKEN }}
|
|
|
|
- uses: ./.github/actions/claude-setup
|
|
if: steps.check.outputs.count != '0' || github.event_name == 'workflow_dispatch'
|
|
- uses: max-sixty/tend/claude@0.1.18
|
|
if: steps.check.outputs.count != '0' || github.event_name == 'workflow_dispatch'
|
|
with:
|
|
github_token: ${{ secrets.TEND_BOT_TOKEN }}
|
|
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
bot_name: worktrunk-bot
|
|
model: opus
|
|
prompt: |
|
|
/tend-ci-runner:notifications
|