-
Notifications
You must be signed in to change notification settings - Fork 109
feat: add issue reminder bot #1027
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
aaf5874
a21570e
6e2add8
b89bcdd
a3b537e
34ba63c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
|
|
||
| # Env: | ||
| # GH_TOKEN - provided by GitHub Actions | ||
| # REPO - owner/repo (fallback to GITHUB_REPOSITORY) | ||
| # DAYS - reminder threshold in days (default 7) | ||
| # DRY_RUN - if "true", only log actions without posting comments | ||
|
|
||
| REPO="${REPO:-${GITHUB_REPOSITORY:-}}" | ||
| DAYS="${DAYS:-7}" | ||
| DRY_RUN="${DRY_RUN:-false}" | ||
|
|
||
| if [ -z "$REPO" ]; then | ||
| echo "ERROR: REPO environment variable not set." | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "------------------------------------------------------------" | ||
| echo " Issue Reminder Bot (No PR)" | ||
| echo " Repo: $REPO" | ||
| echo " Threshold: $DAYS days" | ||
| echo " Dry Run: $DRY_RUN" | ||
| echo "------------------------------------------------------------" | ||
| echo | ||
|
|
||
| NOW_TS=$(date +%s) | ||
|
|
||
| # Cross-platform timestamp parsing (Linux + macOS/BSD) | ||
| parse_ts() { | ||
| local ts="$1" | ||
| if date --version >/dev/null 2>&1; then | ||
| date -d "$ts" +%s # GNU date (Linux) | ||
| else | ||
| date -j -f "%Y-%m-%dT%H:%M:%SZ" "$ts" +"%s" # macOS/BSD | ||
| fi | ||
| } | ||
|
|
||
| # Fetch open ISSUES (not PRs) that have assignees | ||
| ISSUES=$(gh api "repos/$REPO/issues" \ | ||
| --paginate \ | ||
| --jq '.[] | select(.state=="open" and (.assignees | length > 0) and (.pull_request | not)) | .number') | ||
|
|
||
| if [ -z "$ISSUES" ]; then | ||
| echo "No open issues with assignees found." | ||
| exit 0 | ||
| fi | ||
|
|
||
| for ISSUE in $ISSUES; do | ||
| echo "============================================================" | ||
| echo " ISSUE #$ISSUE" | ||
| echo "============================================================" | ||
|
|
||
| ISSUE_JSON=$(gh api "repos/$REPO/issues/$ISSUE") | ||
| ASSIGNEES=$(echo "$ISSUE_JSON" | jq -r '.assignees[].login') | ||
|
|
||
| if [ -z "$ASSIGNEES" ]; then | ||
| echo "[INFO] No assignees? Skipping." | ||
| echo | ||
| continue | ||
| fi | ||
|
|
||
| echo "[INFO] Assignees: $ASSIGNEES" | ||
| echo | ||
|
|
||
| # Check if this issue already has a reminder comment from ReminderBot | ||
| EXISTING_COMMENT=$(gh api "repos/$REPO/issues/$ISSUE/comments" \ | ||
| --jq ".[] | select(.user.login == \"github-actions[bot]\") | select(.body | contains(\"ReminderBot\")) | .id" \ | ||
| | head -n1) | ||
|
|
||
| if [ -n "$EXISTING_COMMENT" ]; then | ||
| echo "[INFO] Reminder comment already posted on this issue." | ||
| echo | ||
| continue | ||
| fi | ||
|
|
||
| # Get assignment time (use the last assigned event) | ||
| ASSIGN_TS=$(gh api "repos/$REPO/issues/$ISSUE/events" \ | ||
| --jq ".[] | select(.event==\"assigned\") | .created_at" \ | ||
| | tail -n1) | ||
|
|
||
| if [ -z "$ASSIGN_TS" ]; then | ||
| echo "[WARN] No assignment event found. Skipping." | ||
| continue | ||
| fi | ||
|
|
||
| ASSIGN_TS_SEC=$(parse_ts "$ASSIGN_TS") | ||
| DIFF_DAYS=$(( (NOW_TS - ASSIGN_TS_SEC) / 86400 )) | ||
|
|
||
| echo "[INFO] Assigned at: $ASSIGN_TS" | ||
| echo "[INFO] Days since assignment: $DIFF_DAYS" | ||
|
|
||
| # Check if any open PRs are linked to this issue | ||
| PR_NUMBERS=$(gh api \ | ||
| -H "Accept: application/vnd.github.mockingbird-preview+json" \ | ||
| "repos/$REPO/issues/$ISSUE/timeline" \ | ||
| --jq ".[] | ||
| | select(.event == \"cross-referenced\") | ||
| | select(.source.issue.pull_request != null) | ||
| | .source.issue.number" 2>/dev/null || true) | ||
|
|
||
| OPEN_PR_FOUND="" | ||
| if [ -n "$PR_NUMBERS" ]; then | ||
| for PR_NUM in $PR_NUMBERS; do | ||
| PR_STATE=$(gh pr view "$PR_NUM" --repo "$REPO" --json state --jq '.state' 2>/dev/null || true) | ||
| if [ "$PR_STATE" = "OPEN" ]; then | ||
| OPEN_PR_FOUND="$PR_NUM" | ||
| break | ||
| fi | ||
| done | ||
| fi | ||
|
|
||
| if [ -n "$OPEN_PR_FOUND" ]; then | ||
| echo "[KEEP] An OPEN PR #$OPEN_PR_FOUND is linked to this issue → skip reminder." | ||
| echo | ||
| continue | ||
| fi | ||
|
|
||
| echo "[RESULT] No OPEN PRs linked to this issue." | ||
|
|
||
| # Check if threshold has been reached | ||
| if [ "$DIFF_DAYS" -lt "$DAYS" ]; then | ||
| echo "[WAIT] Only $DIFF_DAYS days (< $DAYS) → not yet time for reminder." | ||
| echo | ||
| continue | ||
| fi | ||
|
|
||
| echo "[REMIND] Issue #$ISSUE assigned for $DIFF_DAYS days, posting reminder." | ||
|
|
||
| # Post reminder comment | ||
| MESSAGE="Hi, this is ReminderBot. This issue has been assigned but has had no pull request created. Are you still planning on working on the issue? | ||
|
|
||
| From the Python SDK Team" | ||
|
|
||
| if [ "$DRY_RUN" = "true" ]; then | ||
| echo "[DRY RUN] Would post comment on issue #$ISSUE:" | ||
| echo "$MESSAGE" | ||
| else | ||
| gh issue comment "$ISSUE" --repo "$REPO" --body "$MESSAGE" | ||
| echo "[DONE] Posted reminder comment on issue #$ISSUE." | ||
| fi | ||
| echo | ||
| done | ||
|
|
||
| echo "------------------------------------------------------------" | ||
| echo " Issue Reminder Bot (No PR) complete." | ||
| echo "------------------------------------------------------------" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| name: bot-issue-reminder-no-pr | ||
|
|
||
| on: | ||
| schedule: | ||
| - cron: "0 10 * * *" #runs daily at 10:00 UTC | ||
| workflow_dispatch: | ||
| inputs: | ||
| dry_run: | ||
| description: "Dry run (log only, do not post comments)" | ||
| required: false | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. either make it really clear DRY run can only be true or false, or, allow yes/1 too this is important because some of our dry runs use 1, but if a user types those in now, it will not pick them up and assign false |
||
| default: true #safe default for manual testing | ||
| type: boolean | ||
|
|
||
| permissions: | ||
| contents: read | ||
| issues: write #needed to comment on issues | ||
| pull-requests: read #needed to check PR state | ||
|
|
||
| jobs: | ||
| reminder: | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Harden the runner | ||
| uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 | ||
prajeeta15 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| with: | ||
| egress-policy: audit | ||
|
|
||
| - name: Checkout repository | ||
| uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 #6.0.1 | ||
|
|
||
| - name: Post reminder on assigned issues with no PRs | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| REPO: ${{ github.repository }} | ||
| DAYS: 7 | ||
exploreriii marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| DRY_RUN: ${{ inputs.dry_run }} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are passing i think it will be clearer if we pass false here on workflow runs so the user knows what the workflow will do |
||
| run: bash .github/scripts/issue_reminder_no_pr.sh | ||
Uh oh!
There was an error while loading. Please reload this page.