Last active
May 12, 2026 14:01
-
-
Save Borda/59cc2c3c89f9e079eae9c67c9e053c7f to your computer and use it in GitHub Desktop.
Scans an org or user account for repositories with no push activity since a configurable cutoff date (default: Jan 1 2025), producing a triage-ready CSV of archival candidates. It uses a single gh repo list call to fetch pushedAt for all non-archived repos, computes days since last push, and sorts the output from least stale to most stale so you…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # list-inactive.sh | |
| # | |
| # Find repos in an org/user account with no push activity since a cutoff date, | |
| # so they're candidates for archiving. | |
| # | |
| # Uses the pushedAt field from a single GitHub API call — fast (one request), | |
| # no per-repo lookup. Computes days since last push and sorts the output | |
| # from least stale (recent) to most stale (oldest). | |
| # | |
| # Usage: bash list-inactive.sh <org-or-user> | |
| # Defaults: ORG=Borda SINCE=2025-01-01T00:00:00Z LIMIT=1000 OUT=inactive-repos.csv | |
| # Outputs: inactive-repos.csv | |
| # repo, type, visibility, last_push, days_since, url | |
| # type = "fork" or "src" | |
| # visibility = "public" or "private" | |
| # sorted ascending by days_since | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| set -uo pipefail | |
| ORG="${1:-Borda}" | |
| SINCE="${SINCE:-2025-01-01T00:00:00Z}" | |
| LIMIT="${LIMIT:-1000}" | |
| OUT="${OUT:-inactive-repos.csv}" | |
| echo "→ Listing repos in org: $ORG (inactive since $SINCE)" >&2 | |
| repos_json=$(gh repo list "$ORG" \ | |
| --no-archived --limit "$LIMIT" \ | |
| --json nameWithOwner,pushedAt,isFork,isPrivate,url) | |
| total=$(echo "$repos_json" | jq 'length') | |
| echo "→ Scanned $total non-archived repo(s)" >&2 | |
| echo "$repos_json" \ | |
| | jq -r --arg since "$SINCE" ' | |
| [ .[] | |
| | select(.pushedAt < $since) | |
| | { | |
| repo: .nameWithOwner, | |
| type: (if .isFork then "fork" else "src" end), | |
| visibility: (if .isPrivate then "private" else "public" end), | |
| last_push: .pushedAt, | |
| days_since: ((now - (.pushedAt | fromdateiso8601)) / 86400 | floor), | |
| url: .url | |
| } | |
| ] | |
| | sort_by(.days_since) | |
| | (["repo","type","visibility","last_push","days_since","url"]), | |
| (.[] | [.repo, .type, .visibility, .last_push, .days_since, .url]) | |
| | @csv | |
| ' > "$OUT" | |
| rows=$(($(wc -l < "$OUT") - 1)) | |
| echo "→ Done. Found $rows inactive repo(s). Wrote $OUT" >&2 | |
| [[ "$rows" -gt 0 ]] && { | |
| echo "→ Preview (recent → stale):" >&2 | |
| column -s, -t "$OUT" | head -11 >&2 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment