Skip to content

Instantly share code, notes, and snippets.

@eliashaeussler
Last active January 1, 2026 19:04
Show Gist options
  • Select an option

  • Save eliashaeussler/6014fb3910201ffcef428fbf97dc387c to your computer and use it in GitHub Desktop.

Select an option

Save eliashaeussler/6014fb3910201ffcef428fbf97dc387c to your computer and use it in GitHub Desktop.
Bulk update of all GitHub repos owned by a given user

Bulk repo update

Shell script to update all public and private GitHub repositories of a given user with dedicated, auto-generated changes.

About this script

  • Runs a predefined Composer script to generate changes, e.g. composer fix
  • Supports public and private repos, no forks and archives repos
  • Requires the gh binary to be installed
  • Handles authentication via gh auth

Usage

./bulk_repo_update.sh <username>

Example:

./bulk_repo_update.sh eliashaeussler
#!/usr/bin/env bash
# shellcheck disable=SC2155
set -uo pipefail
readonly username="$1"
readonly commitMessage="[TASK] Run PHP-CS-Fixer"
readonly composerScript="fix:php"
readonly workdir="$(mktemp -d)"
failedRepos=()
# Validate username
if [ -z "$username" ]; then
echo "❌ No username given"
exit 1
fi
# Perform authentication, if needed
if [ "$(gh auth status --json hosts --jq ".hosts | .\"github.com\" | map(select(.login == \"$username\")) | length")" -eq 0 ]; then
echo "πŸ” Not authenticated for GitHub user '$username'. Starting interactive login..."
gh auth login || { echo "❌ Authentication failed"; exit 1; }
else
echo "βœ… Authenticated as '$username'"
fi
echo "Working directory: $workdir"
echo
cd "$workdir" || exit 1
# Fetch suitable repos
readonly repos=$(gh repo list "$username" \
--limit 1000 \
--json name,defaultBranchRef,isArchived,isFork \
--jq '.[] | select(.isFork == false and .isArchived == false) | "\(.name)|\(.defaultBranchRef.name)"')
# Process each repo
for entry in $repos; do
repoName="${entry%%|*}"
defaultBranch="${entry##*|}"
echo
echo "────────────────────────────────────────────"
echo "Processing repo: $repoName (branch: $defaultBranch)"
# Clone repo
if ! gh repo clone "$username/$repoName" -- --depth=1; then
failedRepos+=("$repoName (clone)")
continue
fi
# Switch to repo dir
cd "$repoName" || {
failedRepos+=("$repoName (cd)")
continue
}
# Skip if no composer.json exists
if [[ ! -f composer.json ]]; then
echo "ℹ️ No composer.json – skipping"
cd ..
rm -rf "$repoName"
continue
fi
lastCommitMessage=$(git log -1 --pretty=%s)
# Skip if repo was already processed
if [[ "$lastCommitMessage" == "$commitMessage" ]]; then
echo "ℹ️ Last commit already matches target message – skipping"
cd ..
rm -rf "$repoName"
continue
fi
# Detect suitable composer script
if composer help "$composerScript" >/dev/null 2>&1; then
composerCommand="$composerScript"
elif composer help cgl >/dev/null 2>&1; then
composerCommand="cgl $composerScript"
else
echo "⚠️ No suitable composer fix command found – skipping"
cd ..
rm -rf "$repoName"
continue
fi
# Run composer install
if ! composer install --no-interaction --no-progress; then
failedRepos+=("$repoName (composer install)")
cd ..
continue
fi
# Run composer fix
if ! PHP_CS_FIXER_IGNORE_ENV=1 composer $composerCommand; then
failedRepos+=("$repoName (composer $composerCommand)")
cd ..
continue
fi
# Skip if no files were changed
if git diff --quiet; then
echo "ℹ️ No changes – nothing to commit"
cd ..
rm -rf "$repoName"
continue
fi
# Commit changes
if ! git commit -A -m "$commitMessage"; then
failedRepos+=("$repoName (commit)")
cd ..
continue
fi
# Display changes
echo
echo "πŸ”Ž Review changes if needed:"
echo " cd \"$workdir/$repoName\""
echo
git show
echo
# Push changes back to GitHub
read -rp "πŸš€ Push changes to '$defaultBranch'? [y/N] " CONFIRM
if [[ "$CONFIRM" =~ ^[Yy]$ ]]; then
if ! git push origin "$defaultBranch"; then
failedRepos+=("$repoName (push)")
else
echo "βœ… Pushed successfully"
fi
else
echo "⏸️ Push skipped by user"
fi
# Remove repo
cd ..
rm -rf "$repoName"
done
echo
echo "════════════════════════════════════════════"
echo "Finished."
# Display failed repos
if (( ${#failedRepos[@]} > 0 )); then
echo "❌ Failed repositories:"
for repo in "${failedRepos[@]}"; do
echo " - $repo"
done
else
echo "βœ… All repositories processed successfully"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment