mirror of
https://github.com/ArchiveBox/ArchiveBox.git
synced 2026-09-13 18:46:17 +05:00
338 lines
12 KiB
Bash
Executable File
338 lines
12 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 abxpkg abx-plugins abx-dl archivebox)
|
|
GIT_BINARY=""
|
|
|
|
locked_abxpkg_spec() {
|
|
UV_LOCK_PATH="$1" uv run --no-cache --no-project python -c 'import os, tomllib; package = next(item for item in tomllib.load(open(os.environ["UV_LOCK_PATH"], "rb"))["package"] if item["name"] == "abxpkg"); wheel = package["wheels"][0]; print("abxpkg @ {}#{}".format(wheel["url"], wheel["hash"].replace(":", "=")))'
|
|
}
|
|
|
|
resolve_git_binary() {
|
|
local lock_file="$SCRIPT_REPO_ROOT/uv.lock"
|
|
if [[ ! -f "$lock_file" && -f "$SCRIPT_REPO_ROOT/archivebox/uv.lock" ]]; then
|
|
lock_file="$SCRIPT_REPO_ROOT/archivebox/uv.lock"
|
|
fi
|
|
|
|
local abxpkg_requirement="abxpkg"
|
|
if [[ -f "$lock_file" ]]; then
|
|
abxpkg_requirement="$(locked_abxpkg_spec "$lock_file")"
|
|
[[ -n "$abxpkg_requirement" ]] || { printf 'Unable to find abxpkg in %s\n' "$lock_file" >&2; exit 1; }
|
|
fi
|
|
|
|
export ABXPKG_LIB_DIR="${ABXPKG_LIB_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/archivebox/setup-monorepo-abxpkg}"
|
|
mkdir -p "$ABXPKG_LIB_DIR/env/bin"
|
|
uv run --no-cache --no-project --with "$abxpkg_requirement" abxpkg env \
|
|
--install \
|
|
--lib="$ABXPKG_LIB_DIR" \
|
|
--binproviders=env,brew,apt \
|
|
git >/dev/null
|
|
GIT_BINARY="$ABXPKG_LIB_DIR/env/bin/git"
|
|
test -L "$GIT_BINARY"
|
|
test -x "$GIT_BINARY"
|
|
}
|
|
|
|
repo_target_branch() {
|
|
case "$1" in
|
|
archivebox)
|
|
printf 'dev\n'
|
|
;;
|
|
*)
|
|
printf 'main\n'
|
|
;;
|
|
esac
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
bootstrap_build_dependencies() {
|
|
export ABXPKG_LIB_DIR="$ROOT_DIR/.venv/abxpkg"
|
|
mkdir -p "$ABXPKG_LIB_DIR/env/bin"
|
|
export PATH="$ABXPKG_LIB_DIR/env/bin:$PATH"
|
|
|
|
case "$OSTYPE" in
|
|
linux*)
|
|
uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \
|
|
--install \
|
|
--no-cache \
|
|
--json \
|
|
--lib="$ABXPKG_LIB_DIR" \
|
|
--binproviders=env,apt \
|
|
--overrides='{"apt":{"install_args":["build-essential"]}}' \
|
|
cc >/dev/null
|
|
uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \
|
|
--install \
|
|
--no-cache \
|
|
--json \
|
|
--lib="$ABXPKG_LIB_DIR" \
|
|
--binproviders=env,apt \
|
|
--overrides='{"env":{"version":["ldapsearch","-VV"]},"apt":{"install_args":["ldap-utils","python3-dev","python3-setuptools","libssl-dev","libldap2-dev","libsasl2-dev","zlib1g-dev","libatomic1"],"version":["ldapsearch","-VV"]}}' \
|
|
ldapsearch >/dev/null
|
|
|
|
uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \
|
|
--install \
|
|
--no-cache \
|
|
--json \
|
|
--lib="$ABXPKG_LIB_DIR" \
|
|
--binproviders=env \
|
|
cc >/dev/null
|
|
uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \
|
|
--install \
|
|
--no-cache \
|
|
--json \
|
|
--lib="$ABXPKG_LIB_DIR" \
|
|
--binproviders=env \
|
|
--overrides='{"env":{"version":["ldapsearch","-VV"]}}' \
|
|
ldapsearch >/dev/null
|
|
|
|
test -L "$ABXPKG_LIB_DIR/env/bin/cc"
|
|
test -x "$ABXPKG_LIB_DIR/env/bin/cc"
|
|
test -L "$ABXPKG_LIB_DIR/env/bin/ldapsearch"
|
|
test -x "$ABXPKG_LIB_DIR/env/bin/ldapsearch"
|
|
;;
|
|
darwin*)
|
|
uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \
|
|
--install \
|
|
--json \
|
|
--lib="$ABXPKG_LIB_DIR" \
|
|
--binproviders=env \
|
|
brew >/dev/null
|
|
|
|
local brew_binary="$ABXPKG_LIB_DIR/env/bin/brew"
|
|
local brew_target
|
|
local brew_root
|
|
brew_target="$(readlink "$brew_binary")"
|
|
test -x "$brew_target"
|
|
brew_root="$(dirname "$(dirname "$brew_target")")"
|
|
export ABXPKG_BREW_ROOT="$brew_root"
|
|
|
|
uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \
|
|
--install \
|
|
--no-cache \
|
|
--json \
|
|
--lib="$ABXPKG_LIB_DIR" \
|
|
--binproviders=env,brew \
|
|
--overrides='{"brew":{"install_args":["llvm"]}}' \
|
|
clang >/dev/null
|
|
uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \
|
|
--install \
|
|
--no-cache \
|
|
--json \
|
|
--lib="$ABXPKG_LIB_DIR" \
|
|
--binproviders=env,brew \
|
|
--overrides='{"env":{"version":["ldapvc","-VV"]},"brew":{"install_args":["openldap"],"postinstall_scripts":true,"version":["ldapvc","-VV"]}}' \
|
|
ldapvc >/dev/null
|
|
|
|
PATH="$PATH:$brew_root/opt/llvm/bin" \
|
|
uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \
|
|
--install \
|
|
--no-cache \
|
|
--json \
|
|
--lib="$ABXPKG_LIB_DIR" \
|
|
--binproviders=env \
|
|
clang >/dev/null
|
|
PATH="$PATH:$brew_root/opt/openldap/bin" \
|
|
uv tool run --from "$ROOT_DIR/abxpkg" abxpkg env \
|
|
--install \
|
|
--no-cache \
|
|
--json \
|
|
--lib="$ABXPKG_LIB_DIR" \
|
|
--binproviders=env \
|
|
--overrides='{"env":{"version":["ldapvc","-VV"]}}' \
|
|
ldapvc >/dev/null
|
|
|
|
test -L "$ABXPKG_LIB_DIR/env/bin/clang"
|
|
test -x "$ABXPKG_LIB_DIR/env/bin/clang"
|
|
test -L "$ABXPKG_LIB_DIR/env/bin/ldapvc"
|
|
|
|
local ldapvc_target
|
|
local next_ldapvc_target
|
|
local openldap_prefix
|
|
ldapvc_target="$(readlink "$ABXPKG_LIB_DIR/env/bin/ldapvc")"
|
|
while [[ -L "$ldapvc_target" ]]; do
|
|
next_ldapvc_target="$(readlink "$ldapvc_target")"
|
|
if [[ "$next_ldapvc_target" == /* ]]; then
|
|
ldapvc_target="$next_ldapvc_target"
|
|
else
|
|
ldapvc_target="$(dirname "$ldapvc_target")/$next_ldapvc_target"
|
|
fi
|
|
done
|
|
test -x "$ldapvc_target"
|
|
openldap_prefix="$(dirname "$(dirname "$ldapvc_target")")"
|
|
test -f "$openldap_prefix/include/ldap.h"
|
|
test -f "$openldap_prefix/lib/libldap.dylib"
|
|
export CPPFLAGS="-I$openldap_prefix/include${CPPFLAGS:+ $CPPFLAGS}"
|
|
export LDFLAGS="-L$openldap_prefix/lib${LDFLAGS:+ $LDFLAGS}"
|
|
export PKG_CONFIG_PATH="$openldap_prefix/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
|
|
;;
|
|
*)
|
|
printf 'Unsupported monorepo setup platform: %s\n' "$OSTYPE" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
sync_workspace() {
|
|
uv sync --all-packages --all-extras --all-groups --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_BINARY" -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_BINARY" -C "$monorepo_root" remote add origin "$MONOREPO_REMOTE"
|
|
fi
|
|
|
|
printf 'Updating monorepo root: %s\n' "$monorepo_root"
|
|
if "$GIT_BINARY" -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_BINARY" -C "$monorepo_root" init -b main >/dev/null
|
|
"$GIT_BINARY" -C "$monorepo_root" remote add origin "$MONOREPO_REMOTE"
|
|
"$GIT_BINARY" -C "$monorepo_root" fetch --depth=1 origin main --quiet
|
|
|
|
if "$GIT_BINARY" -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
|
|
}
|
|
|
|
resolve_git_binary
|
|
|
|
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"
|
|
local target_branch
|
|
local current_branch
|
|
target_branch="$(repo_target_branch "$repo_name")"
|
|
|
|
if [[ -d "$repo_dir/.git" ]]; then
|
|
printf 'Updating existing checkout: %s\n' "$repo_name"
|
|
current_branch="$("$GIT_BINARY" -C "$repo_dir" branch --show-current)"
|
|
|
|
if [[ "$current_branch" != "$target_branch" ]]; then
|
|
"$GIT_BINARY" -C "$repo_dir" fetch --depth=1 origin "$target_branch" --quiet
|
|
"$GIT_BINARY" -C "$repo_dir" checkout -B "$target_branch" --track "origin/$target_branch" >/dev/null
|
|
fi
|
|
|
|
if "$GIT_BINARY" -C "$repo_dir" -c pull.rebase=false pull --ff-only --quiet origin "$target_branch" >/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_BINARY" clone --branch "$target_branch" "$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"
|
|
bootstrap_build_dependencies
|
|
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
|
|
echo "TIPS:"
|
|
echo " - Use 'uv run ...' inside member repos for package work; the root uv.lock owns this workspace"
|
|
echo " - Always read $ROOT_DIR/README.md into context before starting any work"
|