From 52a0ec3b8fec0bc5d4a32625446c977b13e57ad5 Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov <1517853+kpavlov@users.noreply.github.com> Date: Fri, 13 Feb 2026 15:21:29 +0200 Subject: [PATCH] ci: enhance stale issue/PR workflow (#522) # ci: enhance stale issue/PR workflow - Split stale workflow into marking and closing jobs - Add dry-run mode input and environment variables - Improve configuration for timing, messages, and operations per run ## Motivation and Context To mark and close stale issues/PRs automatically ## How it works The automations.yml workflow automates the lifecycle of inactive issues and pull requests using a two-stage process. It is scheduled to run daily at 2:00 AM UTC and supports manual execution with a dry-run option. ### 1. Marking Phase (mark-stale job) This job identifies inactive items and labels them as stale without closing them. - Criteria: Any issue or PR with no activity for 30 days. - Action: Adds the stale label and posts a notification comment. - Exemptions: - Draft PRs are ignored. - Issues labeled with awaiting-approval or work-in-progress are ignored. - Recovery: If a contributor updates the item, the stale label is automatically removed. - Safety: This job is explicitly configured never to close items (days-before-*-close: -1). ### 2. Closing Phase (close-stale job) This job handles the final closure of items that have remained inactive after being marked. - Criteria: Only targets items that already have the stale label and have seen no further activity for 15 days. - Action: Closes the issue or PR and posts a final closure message. - Safety: This job is explicitly configured never to mark new items (days-before-*-stale: -1). ### Global Configuration - Dry Run: Controlled by the DRY_RUN variable. When true, the bot logs planned actions in the workflow output but does not actually apply labels, post comments, or close items. - Throttling: Processes up to 100 items per run to avoid hitting GitHub API rate limits. - Permissions: Requires write access to issues and pull requests to perform its actions. ## How Has This Been Tested? [Manually tested on CI](https://github.com/modelcontextprotocol/kotlin-sdk/actions/runs/21984701285) with dry run ## Breaking Changes No ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [x] Workflow update ## Checklist - [ ] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [ ] My code follows the repository's style guidelines - [ ] New and existing tests pass locally - [ ] I have added appropriate error handling - [ ] I have added or updated documentation as needed ## Additional context --- .github/workflows/automations.yml | 106 ++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 36 deletions(-) diff --git a/.github/workflows/automations.yml b/.github/workflows/automations.yml index fbccaea..4f88d37 100644 --- a/.github/workflows/automations.yml +++ b/.github/workflows/automations.yml @@ -5,6 +5,11 @@ on: # Runs every day at 2:00 AM UTC - cron: '0 2 * * *' workflow_dispatch: + inputs: + DRY_RUN: + type: boolean + description: 'Run in dry run mode' + default: true permissions: actions: write @@ -12,58 +17,87 @@ permissions: issues: write pull-requests: write +env: + DRY_RUN: true + MOVE_TO_STALE_DAYS: 30 + CLOSE_AFTER_STALE_DAYS: 15 + jobs: - close-question-prs: - name: Mark/Close stale PRs and issues + mark-stale: + name: Mark/unmark stale PRs and issues runs-on: ubuntu-latest steps: - uses: actions/stale@v10 - ## https://github.com/marketplace/actions/close-stale-issues with: - debug-only: true # 'true' for dry run - exempt-draft-pr: true # pull requests currently in draft will not be marked as stale + debug-only: ${{ env.DRY_RUN }} + exempt-draft-pr: true exempt-issue-labels: 'awaiting-approval,work-in-progress' + stale-issue-label: 'stale' stale-pr-label: 'stale' - only-labels: 'awaiting-feedback,awaiting-answers' - - # --- Scope: only PRs, not issues --- - stale-issue-message: > - 👋 **This issue has been marked as Stale.** - - This issue was awaiting feedback and has had no activity for **30 days**. - - — 🤖 Stale PR Bot - days-before-issue-stale: 30 - days-before-issue-close: -1 # --- Timing --- - # Since we target PRs already labeled "question", - # days-before-stale counts from last activity. - days-before-pr-stale: 30 - days-before-pr-close: 15 + # Job 1 marks/unmarks, but never closes. + days-before-issue-stale: ${{ env.MOVE_TO_STALE_DAYS }} + days-before-issue-close: -1 + days-before-pr-stale: ${{ env.MOVE_TO_STALE_DAYS }} + days-before-pr-close: -1 - # --- Labels --- remove-stale-when-updated: true remove-pr-stale-when-updated: true # --- Messages --- + stale-issue-message: > + 👋 **This issue has been marked as Stale.** + + This issue has had no activity for **${{ env.MOVE_TO_STALE_DAYS }} days**. + + — 🤖 Stale Bot stale-pr-message: > 👋 **This PR has been marked as Stale.** - - This PR was awaiting feedback and has had no activity for **30 days**. - - — 🤖 Stale PR Bot - close-pr-message: > - 👋 **This PR has been automatically closed.** - - This PR was labeled with `stale` and has had no activity - for **15 days** since that. - - If this is still relevant, please feel free to reopen it - or create a new PR. - - — 🤖 Stale PR Bot - # --- Operations --- + This PR has had no activity for **${{ env.MOVE_TO_STALE_DAYS }} days**. + + — 🤖 Stale Bot + + operations-per-run: 100 + ascending: true + + close-stale: + name: Close stale PRs and issues + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v10 + with: + debug-only: ${{ env.DRY_RUN }} + only-labels: 'stale' + stale-issue-label: 'stale' + stale-pr-label: 'stale' + + # --- Timing --- + # Job 2 only closes already marked items. + days-before-issue-stale: -1 + days-before-pr-stale: -1 + days-before-issue-close: ${{ env.CLOSE_AFTER_STALE_DAYS }} + days-before-pr-close: ${{ env.CLOSE_AFTER_STALE_DAYS }} + + # --- Messages --- + close-issue-message: > + 👋 **This issue has been automatically closed.** + + This issue was labeled with `stale` and has had no activity + for **${{ env.CLOSE_AFTER_STALE_DAYS }} days** since that. + + — 🤖 Stale Bot + close-pr-message: > + 👋 **This PR has been automatically closed.** + + This PR was labeled with `stale` and has had no activity + for **${{ env.CLOSE_AFTER_STALE_DAYS }} days** since that. + + If this is still relevant, please feel free to reopen it + or create a new PR. + + — 🤖 Stale Bot + operations-per-run: 100 ascending: true