From 10e0677c634df7d53f6b5038302ec6482f836495 Mon Sep 17 00:00:00 2001 From: swapnil <78632212+swapnilpaliwal-sd@users.noreply.github.com> Date: Thu, 15 Oct 2026 11:54:03 -0700 Subject: [PATCH] ci: close the issues a pull request names once it reaches dev, whatever branch it merged into GitHub closes issues from "Closes #N" only when the merge lands on the default branch. Pull requests into integration or release branches (0.2.1, apps/integration-*) leave their issues open after the work reaches dev. On each push to dev this closes them, with a comment naming the pull request. "Part of #N" and "Refs #N" are not closing words, so umbrella issues stay open. Manual runs are dry runs by default. --- .github/scripts/close-issues-on-dev.sh | 43 ++++++++++++++++++++++ .github/workflows/close-issues-on-dev.yml | 44 +++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100755 .github/scripts/close-issues-on-dev.sh create mode 100644 .github/workflows/close-issues-on-dev.yml diff --git a/.github/scripts/close-issues-on-dev.sh b/.github/scripts/close-issues-on-dev.sh new file mode 100755 index 00000000..cfd511a1 --- /dev/null +++ b/.github/scripts/close-issues-on-dev.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# close-issues-on-dev.sh — close the issues named by pull requests that merged into a branch other than +# dev, once their merge commit is part of dev (see .github/workflows/close-issues-on-dev.yml). +# +# DRY_RUN=1 .github/scripts/close-issues-on-dev.sh # list, close nothing +# +# Needs: gh (authenticated; GH_TOKEN in CI), a checkout with full history, and origin/dev or HEAD on dev. +# LOOKBACK: how many recently merged pull requests to consider (default 300). +set -euo pipefail + +DRY_RUN="${DRY_RUN:-0}" +LOOKBACK="${LOOKBACK:-300}" +REPO="${GITHUB_REPOSITORY:-$(gh repo view --json nameWithOwner -q .nameWithOwner)}" +DEV_REF="${DEV_REF:-HEAD}" +[ "$DEV_REF" = HEAD ] && [ "$(git rev-parse --abbrev-ref HEAD)" != dev ] && DEV_REF=origin/dev + +closed=0; pending=0 +while IFS=$'\t' read -r pr base merge body64; do + [ -n "$merge" ] || continue + [ "$base" = dev ] && continue # GitHub closes these itself + if ! git merge-base --is-ancestor "$merge" "$DEV_REF" 2>/dev/null; then + pending=$((pending + 1)); continue # merged, but not on dev yet + fi + body=$(printf '%s' "$body64" | base64 --decode) + for n in $(printf '%s\n' "$body" | grep -oiE '\b(close[sd]?|fix(e[sd])?|resolve[sd]?)[[:space:]]*:?[[:space:]]+#[0-9]+' \ + | grep -oE '[0-9]+$' | sort -un); do + state=$(gh issue view "$n" --repo "$REPO" --json state -q .state 2>/dev/null || echo MISSING) + [ "$state" = OPEN ] || continue + if [ "$DRY_RUN" = 1 ]; then + echo "would close #$n (pull request #$pr into $base, ${merge:0:9} is on dev)" + else + gh issue close "$n" --repo "$REPO" \ + --comment "Closed by #$pr, which merged into \`$base\` and reached \`dev\` in ${merge:0:9}." + echo "closed #$n (pull request #$pr into $base)" + fi + closed=$((closed + 1)) + done +done < <(gh pr list --repo "$REPO" --state merged --limit "$LOOKBACK" \ + --json number,baseRefName,mergeCommit,body \ + -q '.[] | [.number, .baseRefName, (.mergeCommit.oid // ""), (.body // "" | @base64)] | @tsv') + +verb=closed; [ "$DRY_RUN" = 1 ] && verb="to close" +echo "issues $verb: $closed; merged pull requests not on dev yet: $pending" diff --git a/.github/workflows/close-issues-on-dev.yml b/.github/workflows/close-issues-on-dev.yml new file mode 100644 index 00000000..e491bed8 --- /dev/null +++ b/.github/workflows/close-issues-on-dev.yml @@ -0,0 +1,44 @@ +# ───────────────────────────────────────────────────────────────────────────── +# A pull request that merges into a release or integration branch (0.2.1, a +# perf branch) never closes the issues it names: GitHub acts on "Closes #N" only +# when the merge lands on the default branch. This closes them when that work +# reaches `dev`. +# +# On every push to dev it looks at pull requests merged into any other branch, +# keeps those whose merge commit is now part of dev, and closes each open issue +# their description closes ("Closes", "Fixes", "Resolves" #N), with a comment +# naming the pull request. "Part of #N" and "Refs #N" are not closing words, so +# umbrella issues stay open. +# ───────────────────────────────────────────────────────────────────────────── +name: close-issues-on-dev + +on: + push: + branches: [dev] + workflow_dispatch: + inputs: + dry_run: + description: list what would be closed, close nothing + type: boolean + default: true + +permissions: + contents: read + issues: write + pull-requests: read + +jobs: + close: + name: close issues whose pull request reached dev + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: close the issues + env: + GH_TOKEN: ${{ github.token }} + DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run && '1' || '0' }} + run: .github/scripts/close-issues-on-dev.sh