Files
ArchiveBox/bin/setup_monorepo.sh
Nick Sweeting 3e7b83ac91 bump versions
2026-04-04 23:10:12 -07:00

199 lines
5.9 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_REPO_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)"
GITHUB_BASE="${GITHUB_BASE:-https://github.com/ArchiveBox}"
MONOREPO_REMOTE="${MONOREPO_REMOTE:-$GITHUB_BASE/monorepo.git}"
REPO_NAMES=(abxbus abx-pkg abx-plugins abx-dl archivebox)
is_member_repo() {
local repo_root="$1"
local repo_name
for repo_name in "${REPO_NAMES[@]}"; do
if [[ "$(basename "$repo_root")" == "$repo_name" ]]; then
return 0
fi
done
return 1
}
monorepo_remote_matches() {
case "$1" in
git@github.com:ArchiveBox/monorepo.git | \
git+ssh://git@github.com/ArchiveBox/monorepo.git | \
https://github.com/ArchiveBox/monorepo.git)
return 0
;;
*)
return 1
;;
esac
}
warn() {
printf 'Warning: %s\n' "$1" >&2
}
have_ldap_build_deps() {
if command -v dpkg-query >/dev/null 2>&1; then
dpkg-query -W -f='${Status}' libldap2-dev 2>/dev/null | grep -q 'install ok installed' && return 0
fi
if command -v brew >/dev/null 2>&1; then
brew --prefix openldap >/dev/null 2>&1 && return 0
fi
return 1
}
ensure_ldap_build_deps() {
if have_ldap_build_deps; then
return
fi
printf 'Ensuring LDAP build dependencies (best effort)\n'
if command -v apt >/dev/null 2>&1 && sudo -n apt install -y libldap2-dev >/dev/null 2>&1; then
return
fi
if command -v brew >/dev/null 2>&1 && brew install openldap >/dev/null 2>&1; then
return
fi
warn "Could not auto-install LDAP build dependencies; continuing. If you need archivebox[ldap], run: sudo apt install libldap2-dev || brew install openldap"
}
sync_workspace() {
if uv sync --all-packages --all-extras --no-cache --active; then
return
fi
warn "'uv sync --all-packages --all-extras --no-cache --active' failed; retrying without --all-extras"
uv sync --all-packages --no-cache --active
}
ensure_setup_link() {
local repo_name="$1"
local repo_dir="$ROOT_DIR/$repo_name"
local link_path="$repo_dir/bin/setup_monorepo.sh"
local source_path="$ROOT_DIR/bin/setup.sh"
mkdir -p "$repo_dir/bin"
if [[ -e "$link_path" ]] && [[ "$source_path" -ef "$link_path" ]]; then
return
fi
if [[ -d "$link_path" && ! -L "$link_path" ]]; then
printf 'Refusing to replace directory: %s\n' "$link_path" >&2
exit 1
fi
rm -f "$link_path"
ln "$source_path" "$link_path"
}
bootstrap_monorepo_root() {
local monorepo_root="$1"
local origin_url=""
if [[ -d "$monorepo_root/.git" ]]; then
origin_url="$(git -C "$monorepo_root" remote get-url origin 2>/dev/null || true)"
if [[ -n "$origin_url" ]] && ! monorepo_remote_matches "$origin_url"; then
printf 'Refusing to reuse existing git repo at %s (origin: %s)\n' "$monorepo_root" "$origin_url" >&2
exit 1
fi
if [[ -z "$origin_url" ]]; then
git -C "$monorepo_root" remote add origin "$MONOREPO_REMOTE"
fi
printf 'Updating monorepo root: %s\n' "$monorepo_root"
if git -C "$monorepo_root" -c pull.rebase=false pull --ff-only --quiet >/dev/null 2>&1; then
printf 'Updated monorepo root\n'
else
printf 'Skipping monorepo pull (local changes, divergent branch, detached HEAD, or no upstream)\n' >&2
fi
return
fi
printf 'Bootstrapping monorepo root in %s\n' "$monorepo_root"
git -C "$monorepo_root" init -b main >/dev/null
git -C "$monorepo_root" remote add origin "$MONOREPO_REMOTE"
git -C "$monorepo_root" fetch --depth=1 origin main --quiet
if git -C "$monorepo_root" checkout -B main --track origin/main >/dev/null 2>&1; then
printf 'Initialized monorepo root\n'
else
printf 'Failed to materialize monorepo root in %s; existing files likely conflict with tracked monorepo files\n' "$monorepo_root" >&2
exit 1
fi
}
if is_member_repo "$SCRIPT_REPO_ROOT"; then
ROOT_DIR="$(cd -- "$SCRIPT_REPO_ROOT/.." && pwd)"
bootstrap_monorepo_root "$ROOT_DIR"
elif [[ -f "$SCRIPT_REPO_ROOT/pyproject.toml" ]]; then
ROOT_DIR="$SCRIPT_REPO_ROOT"
else
printf 'Unable to infer monorepo root from script location: %s\n' "$SCRIPT_DIR" >&2
exit 1
fi
ensure_member_repo() {
local repo_name="$1"
local repo_dir="$ROOT_DIR/$repo_name"
if [[ -d "$repo_dir/.git" ]]; then
printf 'Updating existing checkout: %s\n' "$repo_name"
if git -C "$repo_dir" -c pull.rebase=false pull --ff-only --quiet >/dev/null 2>&1; then
printf 'Updated: %s\n' "$repo_name"
else
printf 'Skipping pull for %s (local changes, divergent branch, detached HEAD, or no upstream)\n' "$repo_name" >&2
fi
return
fi
if [[ -e "$repo_dir" ]]; then
printf 'Refusing to overwrite existing path: %s\n' "$repo_dir" >&2
exit 1
fi
printf 'Cloning %s/%s.git -> %s\n' "$GITHUB_BASE" "$repo_name" "$repo_name"
git clone "$GITHUB_BASE/$repo_name.git" "$repo_dir"
}
for repo_name in "${REPO_NAMES[@]}"; do
ensure_member_repo "$repo_name"
done
for repo_name in "${REPO_NAMES[@]}"; do
ensure_setup_link "$repo_name"
done
cd "$ROOT_DIR"
deactivate || true
rm -Rf ./*/.venv # delete all sub-repo venvs, the monorepo venv needs to take precedence
uv venv --allow-existing "$ROOT_DIR/.venv"
# shellcheck disable=SC1091
source "$ROOT_DIR/.venv/bin/activate"
ensure_ldap_build_deps
sync_workspace
echo
echo
echo "[√] Monorepo setup complete, cloned and pulled: ${REPO_NAMES[*]}"
echo " MONOREPO_ROOT=$ROOT_DIR"
echo " VIRTUAL_ENV=$VIRTUAL_ENV"
echo " PYTHON_BIN=$VIRTUAL_ENV/bin/python"
echo " NODE_BIN=$(which node)"
echo
echo "TIPS:"
echo " - Always use 'uv run ...' within each subrepo, never in the root & never run 'python ...' directly"
echo " - Always read $ROOT_DIR/README.md into context before starting any work"