130 lines
3.8 KiB
Bash
Executable File
130 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# clone_all.sh — clone (or update) every project in this workspace.
|
|
#
|
|
# Idempotent, safe to re-run:
|
|
# - directory missing / not a git repo -> git clone --branch <branch>
|
|
# - directory already a git repo -> fetch + fast-forward to <branch>
|
|
#
|
|
# Usage:
|
|
# ./clone_all.sh # clone or update all projects
|
|
# CLONE_PROTO=https ./clone_all.sh # rewrite URLs to https (no SSH key needed)
|
|
|
|
set -uo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
cd "$ROOT_DIR" || exit 1
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Project list: "<dir-name>|<git-url>|<branch>"
|
|
# ---------------------------------------------------------------------------
|
|
REPOS=(
|
|
"crearte|git@github.com:XingfenD/crearte.git|feat/submission-preview"
|
|
"crearte-server|git@github.com:XingfenD/crearte-server.git|feat/submission-preview"
|
|
"crearte-deploy|git@github.com:XingfenD/crearte-deploy.git|master"
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
if [ -t 1 ]; then
|
|
C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_ERR=$'\033[31m'; C_OFF=$'\033[0m'
|
|
else
|
|
C_OK=''; C_WARN=''; C_ERR=''; C_OFF=''
|
|
fi
|
|
info() { printf '==> %s\n' "$*"; }
|
|
ok() { printf "${C_OK} ok${C_OFF} %s\n" "$*"; }
|
|
warn() { printf "${C_WARN} warn${C_OFF} %s\n" "$*"; }
|
|
err() { printf "${C_ERR} error${C_OFF} %s\n" "$*" >&2; }
|
|
|
|
# ssh url -> https url when the caller asked for it
|
|
resolve_url() {
|
|
local url="$1"
|
|
if [ "${CLONE_PROTO:-ssh}" = "https" ]; then
|
|
url="${url#git@}"
|
|
url="${url/://}"
|
|
url="https://$url"
|
|
fi
|
|
printf '%s' "$url"
|
|
}
|
|
|
|
has_local_changes() {
|
|
local dir="$1"
|
|
[ -n "$(git -C "$dir" status --porcelain 2>/dev/null)" ]
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Actions
|
|
# ---------------------------------------------------------------------------
|
|
clone_repo() {
|
|
local dir="$1" url="$2" branch="$3"
|
|
info "$dir: cloning $branch from $url"
|
|
if git clone --branch "$branch" "$url" "$dir"; then
|
|
ok "$dir cloned"
|
|
return 0
|
|
fi
|
|
err "$dir clone failed"
|
|
return 1
|
|
}
|
|
|
|
update_repo() {
|
|
local dir="$1" branch="$2"
|
|
info "$dir: updating $branch"
|
|
|
|
if ! git -C "$dir" fetch --prune origin; then
|
|
err "$dir fetch failed (network or credentials?)"
|
|
return 1
|
|
fi
|
|
|
|
local current
|
|
current="$(git -C "$dir" rev-parse --abbrev-ref HEAD)"
|
|
if [ "$current" != "$branch" ]; then
|
|
if has_local_changes "$dir"; then
|
|
warn "$dir has uncommitted changes on '$current' — skipping branch switch to '$branch'"
|
|
return 0
|
|
fi
|
|
if ! git -C "$dir" checkout "$branch"; then
|
|
err "$dir could not switch to '$branch'"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
if ! git -C "$dir" pull --ff-only origin "$branch"; then
|
|
err "$dir not fast-forwardable — resolve manually (git -C $dir status)"
|
|
return 1
|
|
fi
|
|
ok "$dir up to date"
|
|
return 0
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Main
|
|
# ---------------------------------------------------------------------------
|
|
cloned=0 updated=0 skipped=0 failed=0
|
|
|
|
for entry in "${REPOS[@]}"; do
|
|
IFS='|' read -r dir url branch <<< "$entry"
|
|
|
|
if [ -d "$dir/.git" ] || git -C "$dir" rev-parse --git-dir >/dev/null 2>&1; then
|
|
if update_repo "$dir" "$branch"; then
|
|
updated=$((updated + 1))
|
|
else
|
|
failed=$((failed + 1))
|
|
fi
|
|
elif [ -e "$dir" ]; then
|
|
warn "$dir exists but is not a git repo — skipping (move it away or delete it)"
|
|
skipped=$((skipped + 1))
|
|
else
|
|
if clone_repo "$dir" "$(resolve_url "$url")" "$branch"; then
|
|
cloned=$((cloned + 1))
|
|
else
|
|
failed=$((failed + 1))
|
|
fi
|
|
fi
|
|
done
|
|
|
|
printf '\nSummary: %d cloned, %d updated, %d skipped, %d failed\n' \
|
|
"$cloned" "$updated" "$skipped" "$failed"
|
|
|
|
[ "$failed" -eq 0 ]
|