#!/usr/bin/env bash
set -euo pipefail
PIP_MIRROR="${PIP_MIRROR:-https://mirrors.aliyun.com/pypi/simple/}"
PIP_HOST="$(printf '%s' "$PIP_MIRROR" | sed -E 's#^https?://([^/]+)/.*#\1#')"
VENV_DIR=".pre-commit-venv"
log() { printf '\033[1;32m[setup]\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m[warn ]\033[0m %s\n' "$*"; }
die() { printf '\033[1;31m[error]\033[0m %s\n' "$*" >&2; exit 1; }
command -v git >/dev/null 2>&1 || die "未找到 git"
git rev-parse --show-toplevel >/dev/null 2>&1 || die "当前不在 git 仓库内,请在仓库根目录运行"
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT"
[ -f .pre-commit-config.yaml ] || die "当前仓库没有 .pre-commit-config.yaml,无法安装"
PY=""
for c in python3.13 python3.12 python3.11 python3.10 python3; do
if command -v "$c" >/dev/null 2>&1; then
ver="$("$c" -c 'import sys;print("%d.%d"%sys.version_info[:2])' 2>/dev/null || echo 0.0)"
major="${ver%%.*}"; minor="${ver##*.}"
if [ "${major:-0}" -eq 3 ] && [ "${minor:-0}" -ge 10 ]; then PY="$c"; break; fi
fi
done
[ -n "$PY" ] || die "需要 python>=3.10(pre-commit 要求)。请先安装。"
log "使用 python: $PY ($("$PY" --version 2>&1))"
log "使用 pip 镜像(临时): $PIP_MIRROR"
if [ ! -d "$VENV_DIR" ]; then
log "创建隔离环境: $VENV_DIR/"
"$PY" -m venv "$VENV_DIR" || die "创建 venv 失败(可能缺 python venv 模块)"
else
log "复用已存在的隔离环境: $VENV_DIR/"
fi
VENV_PY="$VENV_DIR/bin/python"
PKGS=(pre-commit mypy)
if grep -q "id: flake8" .pre-commit-config.yaml 2>/dev/null; then
PKGS+=(flake8)
fi
log "安装工具到隔离环境: ${PKGS[*]}"
"$VENV_PY" -m pip install --disable-pip-version-check -q \
-i "$PIP_MIRROR" --trusted-host "$PIP_HOST" --upgrade pip
"$VENV_PY" -m pip install --disable-pip-version-check \
-i "$PIP_MIRROR" --trusted-host "$PIP_HOST" \
"${PKGS[@]}"
PRE_COMMIT="$VENV_DIR/bin/pre-commit"
log "安装 git 钩子"
"$PRE_COMMIT" install
log "预热钩子环境,首次会联网从 gitcode 拉取,请稍候…"
PIP_INDEX_URL="$PIP_MIRROR" PIP_TRUSTED_HOST="$PIP_HOST" \
"$PRE_COMMIT" install-hooks
if ! grep -qxF "$VENV_DIR/" .gitignore 2>/dev/null && ! grep -qxF "$VENV_DIR" .gitignore 2>/dev/null; then
warn "建议将 $VENV_DIR/ 加入 .gitignore(避免误提交隔离环境)"
fi
log "完成!之后每次 git commit 会自动检查本次改动(无需手动激活 venv)。"
log "手动试跑: $PRE_COMMIT run --files <某个文件>"
log "卸载: $PRE_COMMIT uninstall 并删除 $VENV_DIR/"