blumeops/mise-tasks/mirror-update-pats
Erich Blume f91b8d0d98 Fix mirror-update-pats corrupting all GitHub mirror URLs
The bash parameter expansion `${var/pat/rep}` treats `\/` in the
replacement as a literal backslash-slash, not an escaped delimiter.
This produced URLs like `https:\/\/eblume:...` instead of
`https://eblume:...`, breaking Forgejo's URL parser (500 on mirror
settings pages) and preventing mirror syncs.

Use prefix stripping (`${var#prefix}`) instead.

All 22 corrupted mirrors have been repaired on indri.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 11:46:41 -08:00

71 lines
2.2 KiB
Bash
Executable file

#!/usr/bin/env bash
#MISE description="Update GitHub PAT on all mirror repos on indri"
#USAGE flag "--dry-run" help="Show what would be done without changing anything"
set -euo pipefail
OP_GITHUB_PAT_REF="op://blumeops/w3663ffnvkewbftncqxtcpeavy/github-mirror-pat"
REPO_BASE="/opt/homebrew/var/forgejo/data/forgejo-repositories"
DB_PATH="/opt/homebrew/var/forgejo/data/forgejo.db"
DRY_RUN="${usage_dry_run:-false}"
echo "Reading GitHub PAT from 1Password..."
pat="$(op read "$OP_GITHUB_PAT_REF")"
if [[ -z "$pat" ]]; then
echo "Error: GitHub PAT is empty"
exit 1
fi
echo "Querying mirrors on indri..."
# Get all GitHub mirrors: org_name, repo_name, upstream_url
mirrors=$(ssh indri "sqlite3 '$DB_PATH' \"
SELECT u.name, r.name, m.remote_address
FROM mirror m
JOIN repository r ON m.repo_id = r.id
JOIN [user] u ON r.owner_id = u.id
WHERE m.remote_address LIKE '%github.com%'
\"" 2>/dev/null)
if [[ -z "$mirrors" ]]; then
echo "No GitHub mirrors found."
exit 0
fi
updated=0
skipped=0
while IFS='|' read -r org repo upstream_url; do
bare_repo="${REPO_BASE}/${org}/${repo}.git"
# Build authenticated URL: https://eblume:<pat>@github.com/...
# Note: \/ in the replacement part of ${var/pat/rep} is literal backslash-slash,
# not an escaped slash. Use prefix stripping instead.
auth_url="https://eblume:${pat}@github.com${upstream_url#https://github.com}"
if [[ "$DRY_RUN" == "true" ]]; then
echo "[dry-run] ${org}/${repo}: would set origin to authenticated URL"
((updated++))
continue
fi
# Check current remote URL (< /dev/null prevents ssh from consuming loop stdin)
current_url=$(ssh indri "git -C '${bare_repo}' config remote.origin.url" < /dev/null 2>/dev/null || echo "")
if [[ "$current_url" == "$auth_url" ]]; then
echo " ${org}/${repo}: already up to date"
((skipped++))
continue
fi
ssh indri "git -C '${bare_repo}' remote set-url origin '${auth_url}'" < /dev/null 2>/dev/null
echo " ${org}/${repo}: updated"
((updated++))
done <<< "$mirrors"
echo
echo "Done. Updated: ${updated}, Skipped: ${skipped}"
if [[ "$DRY_RUN" == "true" ]]; then
echo "(dry-run mode — no changes were made)"
fi