Skip to content

Instantly share code, notes, and snippets.

@unrevised6419
Last active March 5, 2026 15:42
Show Gist options
  • Select an option

  • Save unrevised6419/daebc2f085222142b5092d72c7a2685f to your computer and use it in GitHub Desktop.

Select an option

Save unrevised6419/daebc2f085222142b5092d72c7a2685f to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -euo pipefail
# List all starred archived GitHub repositories, then optionally unstar them.
echo "Fetching starred repositories..."
archived_repos=()
page=1
while true; do
results=$(gh api "/user/starred?per_page=100&page=${page}" --jq '.[] | select(.archived == true) | .full_name')
total=$(gh api "/user/starred?per_page=100&page=${page}" --jq 'length')
echo "Page ${page}: ${total} repo(s) found"
if [[ "$total" -eq 0 ]]; then
break
fi
while IFS= read -r repo; do
[[ -n "$repo" ]] && archived_repos+=("$repo")
done <<< "$results"
page=$((page + 1))
done
if [[ ${#archived_repos[@]} -eq 0 ]]; then
echo "No archived starred repos found."
exit 0
fi
echo ""
echo "Found ${#archived_repos[@]} archived starred repo(s):"
echo ""
for repo in "${archived_repos[@]}"; do
echo " - $repo"
done
echo ""
read -rp "Unstar all of them? [y/N] " confirm
if [[ "$confirm" =~ ^[Yy]$ ]]; then
for repo in "${archived_repos[@]}"; do
echo "Unstarring: $repo"
gh api -X DELETE "/user/starred/${repo}" --silent
done
echo "Done. Unstarred ${#archived_repos[@]} repo(s)."
else
echo "Aborted."
fi
#!/usr/bin/env bash
set -euo pipefail
CUTOFF_DATE="2025-01-01T00:00:00Z"
CUTOFF_EPOCH=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$CUTOFF_DATE" +%s 2>/dev/null || date -d "$CUTOFF_DATE" +%s)
INACTIVE_REPOS=()
PAGE=1
PER_PAGE=100
TOTAL_FETCHED=0
echo "Fetching starred repos..."
while true; do
RESPONSE=$(gh api "/user/starred?per_page=${PER_PAGE}&page=${PAGE}" \
--method GET \
-H "Accept: application/vnd.github+json" \
--jq '.[] | "\(.full_name)\t\(.pushed_at)"' 2>&1)
# Break if empty page (no more results)
[[ -z "$RESPONSE" ]] && break
PAGE_COUNT=0
while IFS=$'\t' read -r full_name pushed_at; do
[[ -z "$full_name" ]] && continue
PAGE_COUNT=$((PAGE_COUNT + 1))
repo_epoch=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$pushed_at" +%s 2>/dev/null || date -d "$pushed_at" +%s)
if [[ "$repo_epoch" -lt "$CUTOFF_EPOCH" ]]; then
INACTIVE_REPOS+=("$full_name")
echo " [INACTIVE] https://github.com/$full_name (last pushed: $pushed_at)"
fi
done <<< "$RESPONSE"
TOTAL_FETCHED=$((TOTAL_FETCHED + PAGE_COUNT))
echo " ... checked $TOTAL_FETCHED repos so far (page $PAGE)"
# If we got fewer than per_page, we've reached the last page
[[ "$PAGE_COUNT" -lt "$PER_PAGE" ]] && break
PAGE=$((PAGE + 1))
done
echo ""
echo "Found ${#INACTIVE_REPOS[@]} starred repos with no activity in 5+ years."
if [[ ${#INACTIVE_REPOS[@]} -eq 0 ]]; then
echo "Nothing to unstar."
exit 0
fi
echo ""
read -rp "Unstar all ${#INACTIVE_REPOS[@]} repos? (y/N): " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "Aborted."
exit 0
fi
echo ""
for repo in "${INACTIVE_REPOS[@]}"; do
echo "Unstarring https://github.com/$repo"
gh api --method DELETE "/user/starred/$repo" --silent 2>/dev/null && echo " ✓ $repo" || echo " ✗ Failed: $repo"
done
echo ""
echo "Done. Unstarred ${#INACTIVE_REPOS[@]} repos."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment