Created
May 12, 2026 09:04
-
-
Save Borda/63a217e7a143a044259595d49e044b04 to your computer and use it in GitHub Desktop.
Takes one or more CSV files as positional arguments, extracts repo slugs from the first column, deduplicates across all inputs, and archives each via gh repo archive --yes. Runs in dry-run mode by default — set APPLY=1 to actually archive, since GitHub's API has no bulk un-archive operation. Each archive attempt is logged to archive-log.csv with…
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 | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # archive-repos.sh | |
| # | |
| # Archive GitHub repos listed in one or more CSV files. Reads the first | |
| # column (repo slug) of each CSV, deduplicates across files, and calls | |
| # `gh repo archive --yes` on each. | |
| # | |
| # Defaults to DRY RUN — prints what would happen but doesn't touch anything. | |
| # Set APPLY=1 to actually archive. Note: GitHub's API does NOT support | |
| # un-archiving in bulk, so review the dry-run output carefully first. | |
| # | |
| # Usage: bash archive-repos.sh <csv> [<csv> ...] # dry run | |
| # APPLY=1 bash archive-repos.sh <csv> [<csv> ...] # real archive | |
| # Defaults: LOG=archive-log.csv APPLY=0 | |
| # Outputs: archive-log.csv (repo, status, detail) — only when APPLY=1 | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| set -uo pipefail | |
| APPLY="${APPLY:-0}" | |
| LOG="${LOG:-archive-log.csv}" | |
| if [[ $# -eq 0 ]]; then | |
| echo "Usage: $0 <csv> [<csv> ...]" >&2 | |
| echo " APPLY=1 $0 <csv> [<csv> ...]" >&2 | |
| exit 1 | |
| fi | |
| # Extract repo slug from first column, skipping CSV header | |
| extract_slugs() { | |
| local f="$1" | |
| if [[ ! -f "$f" ]]; then | |
| echo " (skip: $f not found)" >&2 | |
| return 0 | |
| fi | |
| echo " + $f" >&2 | |
| tail -n +2 "$f" | awk -F'","' 'NF { gsub(/"/, "", $1); print $1 }' | |
| } | |
| echo "→ Reading $# input file(s):" >&2 | |
| mapfile -t repos < <( | |
| for f in "$@"; do extract_slugs "$f"; done | sort -u | grep -v '^$' | |
| ) | |
| total=${#repos[@]} | |
| if [[ $total -eq 0 ]]; then | |
| echo "→ Nothing to archive — input files empty or missing." >&2 | |
| exit 0 | |
| fi | |
| echo "→ $total unique repo(s) selected" >&2 | |
| # Dry run | |
| if [[ "$APPLY" != "1" ]]; then | |
| echo "→ DRY RUN (set APPLY=1 to actually archive):" >&2 | |
| printf ' %s\n' "${repos[@]}" >&2 | |
| exit 0 | |
| fi | |
| # Apply | |
| echo 'repo,status,detail' > "$LOG" | |
| ok=0; fail=0 | |
| for repo in "${repos[@]}"; do | |
| out=$(gh repo archive "$repo" --yes 2>&1) | |
| if [[ $? -eq 0 ]]; then | |
| printf '"%s","ok",""\n' "$repo" >> "$LOG" | |
| printf " %-50s ARCHIVED\n" "$repo" >&2 | |
| ok=$((ok + 1)) | |
| else | |
| detail=$(echo "$out" | tr '\n' ' ' | tr '"' "'" | cut -c1-120) | |
| printf '"%s","fail","%s"\n' "$repo" "$detail" >> "$LOG" | |
| printf " %-50s FAIL %s\n" "$repo" "$detail" >&2 | |
| fail=$((fail + 1)) | |
| fi | |
| done | |
| echo "→ Done. $ok archived, $fail failed. Log → $LOG" >&2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment