Skip to content

Instantly share code, notes, and snippets.

@Borda
Last active May 12, 2026 14:01
Show Gist options
  • Select an option

  • Save Borda/59cc2c3c89f9e079eae9c67c9e053c7f to your computer and use it in GitHub Desktop.

Select an option

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…
#!/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