Skip to content

Commit c68c119

Browse files
authored
feat(ci): daily merge-train/spartan stale-PR notifier (#23189)
When the open merge-train/spartan PR has been open >24h, post a one-line alert to #team-alpha. The cron fires once per day, so the channel sees at most one notification per stuck day. Silent on healthy days. ## Files - `ci3/merge_train_stale_check` — bash script: queries the open PR for a merge-train branch, computes age from `created_at`, and posts a `:warning:` Slack message via `ci3/slack_notify` if age >= `$STALE_HOURS` (default 24). - `.github-new/workflows/merge-train-stale-check.yml` — daily schedule (`7 9 * * *`, 09:07 UTC) + `workflow_dispatch`. Calls the script for `merge-train/spartan` → `#team-alpha`. ## ⚠ Move workflow into `.github/workflows/` before merging The workflow is under `.github-new/` because this session was not started with the `ci-allow` prefix (the prefix needs to be the first token of the prompt; mid-message `ci-allow` was not picked up by the session parser, so `.github/` was still blocked). Before merging, move the file: git mv .github-new/workflows/merge-train-stale-check.yml .github/workflows/merge-train-stale-check.yml Scheduled workflows only execute from the default branch, so the notifier only starts firing once it has landed on `next`. ## Behaviour | State of the open `merge-train/spartan` PR | Action | |---|---| | No open PR (just merged, awaiting auto-recreate) | Silent — no Slack post. | | Open < 24 h (`STALE_HOURS`) | Silent — within expected merge window. | | Open ≥ 24 h | One `:warning:` line to `#team-alpha` with PR link + `mergeable_state`. | ## Reuse Other teams can wire in their own merge-train by adding a job that calls `./ci3/merge_train_stale_check <branch> <channel>`. Threshold and base branch are overridable via `STALE_HOURS` / `BASE_BRANCH` env vars. ## Motivation Driven by a Slack request: merge-train/spartan PR #22980 has been stuck on conflicts for ~6 days with no automated notification. ClaudeBox log: https://claudebox.work/s/e4b1d8ae8d5c867b?run=2 --------- Co-authored-by: Santiago Palladino <santiago@aztec-labs.com>
2 parents 15dfe0d + d34256d commit c68c119

2 files changed

Lines changed: 79 additions & 0 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: Merge-Train Stale Check
2+
3+
on:
4+
schedule:
5+
# Daily at 09:15 UTC — once per day, off the round-minute mark.
6+
- cron: "15 9 * * *"
7+
workflow_dispatch:
8+
9+
jobs:
10+
spartan:
11+
name: Check merge-train/spartan
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: read
15+
pull-requests: read
16+
steps:
17+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
18+
- name: Run stale check
19+
env:
20+
GH_TOKEN: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }}
21+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
22+
run: ./ci3/merge_train_stale_check merge-train/spartan '#team-alpha'

ci3/merge_train_stale_check

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env bash
2+
# Check whether the open PR for a merge-train branch has been open longer
3+
# than a threshold (default 24h). If so, post a one-line alert to a Slack
4+
# channel. Intended to be called from a daily scheduled GitHub Actions job.
5+
#
6+
# Usage: merge_train_stale_check <merge-train-branch> <slack-channel>
7+
#
8+
# Example:
9+
# merge_train_stale_check merge-train/spartan '#team-alpha'
10+
#
11+
# Required env vars:
12+
# GH_TOKEN — GitHub API token (used by `gh api`)
13+
# SLACK_BOT_TOKEN — Slack bot token (consumed by ci3/slack_notify)
14+
#
15+
# Optional env vars:
16+
# STALE_HOURS — alert threshold in hours (default 24)
17+
# BASE_BRANCH — PR base branch to filter on (default "next")
18+
19+
set -euo pipefail
20+
21+
REF_NAME="${1:?Usage: $0 <merge-train-branch> <slack-channel>}"
22+
CHANNEL="${2:?Usage: $0 <merge-train-branch> <slack-channel>}"
23+
STALE_HOURS="${STALE_HOURS:-24}"
24+
BASE_BRANCH="${BASE_BRANCH:-next}"
25+
26+
pr_json=$(gh api "repos/AztecProtocol/aztec-packages/pulls?head=AztecProtocol:${REF_NAME}&state=open&base=${BASE_BRANCH}" --jq '.[0] // empty')
27+
28+
if [[ -z "$pr_json" ]]; then
29+
echo "$REF_NAME: no open PR targeting $BASE_BRANCH — nothing to alert."
30+
exit 0
31+
fi
32+
33+
pr_number=$(jq -r '.number' <<<"$pr_json")
34+
pr_url=$(jq -r '.html_url' <<<"$pr_json")
35+
created_at=$(jq -r '.created_at' <<<"$pr_json")
36+
37+
now_s=$(date -u +%s)
38+
created_s=$(date -u -d "$created_at" +%s)
39+
age_hours=$(( (now_s - created_s) / 3600 ))
40+
41+
echo "$REF_NAME PR #$pr_number opened $age_hours hour(s) ago (created $created_at)"
42+
43+
if (( age_hours < STALE_HOURS )); then
44+
echo "Within ${STALE_HOURS}h window — no alert."
45+
exit 0
46+
fi
47+
48+
mergeable_state=$(gh api "repos/AztecProtocol/aztec-packages/pulls/$pr_number" --jq '.mergeable_state // "unknown"')
49+
days=$(( age_hours / 24 ))
50+
51+
message=$(printf ':warning: `%s` has not merged into `%s` in %d day(s). <%s|PR #%d> is in state `%s`.' \
52+
"$REF_NAME" "$BASE_BRANCH" "$days" "$pr_url" "$pr_number" "$mergeable_state")
53+
54+
TOPDIR=$(git rev-parse --show-toplevel)
55+
"$TOPDIR/ci3/slack_notify" "$message" "$CHANNEL"
56+
57+
echo "Alert sent to $CHANNEL: $message"

0 commit comments

Comments
 (0)