From 83bcee517323687a86b888faea2f23463cf8649c Mon Sep 17 00:00:00 2001 From: AlgoFoe Date: Thu, 20 Aug 2026 18:25:40 +0530 Subject: [PATCH 1/2] add script and python helper for precommit settings --- bash/update_precommit_config.sh | 149 ++++++++++++++++++++++++++++++++ python/precommit_updater.py | 63 ++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 bash/update_precommit_config.sh create mode 100644 python/precommit_updater.py diff --git a/bash/update_precommit_config.sh b/bash/update_precommit_config.sh new file mode 100644 index 0000000..43fc053 --- /dev/null +++ b/bash/update_precommit_config.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ORG="brainglobe" +BRANCH_NAME="update/standardize-precommit" +PR_TITLE="ci: standardise pre-commit config" +PR_BODY="Drops black in favor of ruff-format and bumps pre-commit-hooks/ruff/mypy/check-manifest/codespell revs to match the rest of BrainGlobe. Repo-specific hooks are left untouched." +WORK_DIR="$(pwd)/.brainglobe_precommit_work" +DRY_RUN=false +ONLY_REPO="" + +REPOS=( + "brainglobe-atlasapi" + "brainglobe-ccf-translator" + "brainglobe-heatmap" + "brainrender-napari" + "brainrender" + "cellfinder" + "morphapi" + "brainglobe-registeration" +) + +# parsing args +while [[ $# -gt 0 ]]; do + case "$1" in + --dry-run) DRY_RUN=true ; shift ;; + --repo) ONLY_REPO="$2" ; shift 2 ;; + *) echo "Unknown argument: $1" ; exit 1 ;; + esac +done + +log() { echo "[INFO] $*" ; } +warn() { echo "[WARN] $*" >&2 ; } +die() { echo "[ERROR] $*" >&2 ; exit 1 ; } + +require_cmd() { + command -v "$1" &>/dev/null || die "'$1' is required but not found in PATH." +} + +require_cmd git +require_cmd gh +require_cmd python3 + +mkdir -p "$WORK_DIR" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PYTHON_HELPER="$SCRIPT_DIR/../python/precommit_updater.py" +[[ -f "$PYTHON_HELPER" ]] || die "Cannot find Python helper: $PYTHON_HELPER" + +process_repo() { + local repo="$1" + local repo_dir="$WORK_DIR/$repo" + + log "Processing: $ORG/$repo" + + # 1. Clone/update + if [[ -d "$repo_dir/.git" ]]; then + log "Repo exists locally, resetting to origin/main…" + git -C "$repo_dir" fetch origin --prune + git -C "$repo_dir" checkout main 2>/dev/null \ + || git -C "$repo_dir" checkout master 2>/dev/null \ + || { warn "Cannot find main/master for $repo - skipping."; return 0; } + git -C "$repo_dir" reset --hard origin/HEAD + else + log "Cloning $ORG/$repo…" + if ! gh repo clone "$ORG/$repo" "$repo_dir" -- --depth=1 2>&1; then + warn "Could not clone $ORG/$repo - skipping." + return 0 + fi + fi + + # 2. Find pre-commit config + local config="$repo_dir/.pre-commit-config.yaml" + if [[ ! -f "$config" ]]; then + warn "No .pre-commit-config.yaml found in $repo - skipping." + return 0 + fi + + log "Config: $config" + + # 3. Check if branch already exists remotely + if git -C "$repo_dir" ls-remote --exit-code --heads origin "$BRANCH_NAME" &>/dev/null; then + warn "Branch '$BRANCH_NAME' already exists on $repo - skipping." + return 0 + fi + + # 4. Create branch + git -C "$repo_dir" checkout -b "$BRANCH_NAME" + + # 5. Run standardize.py + log "Running pre-commit standardizer…" + if ! python3 "$PYTHON_HELPER" "$config"; then + warn "Standardizer failed for $repo - skipping." + git -C "$repo_dir" checkout -f main 2>/dev/null || true + git -C "$repo_dir" branch -D "$BRANCH_NAME" 2>/dev/null || true + return 0 + fi + + # 6. Check for actual diff + if git -C "$repo_dir" diff --quiet; then + log "No changes needed for $repo - skipping PR." + git -C "$repo_dir" checkout -f main 2>/dev/null || true + git -C "$repo_dir" branch -D "$BRANCH_NAME" 2>/dev/null || true + return 0 + fi + + log "Diff preview:" + git -C "$repo_dir" diff -- "$(basename "$config")" || true + + if [[ "$DRY_RUN" == true ]]; then + log "[DRY RUN] Would commit, push, and open PR for $repo." + return 0 + fi + + # 7. Commit + git -C "$repo_dir" add "$(basename "$config")" + git -C "$repo_dir" commit -m "$PR_TITLE" + + # 8. Push + log "Pushing branch $BRANCH_NAME to origin…" + git -C "$repo_dir" push origin "$BRANCH_NAME" + + # 9. Open PR + log "Opening PR…" + gh pr create \ + --repo "$ORG/$repo" \ + --head "$BRANCH_NAME" \ + --base main \ + --title "$PR_TITLE" \ + --body "$PR_BODY" \ + --label "maintenance" \ + && log "PR opened for $repo." \ + || warn "PR creation failed for $repo (maybe label doesn't exist - try without --label)." + + log "Done: $repo" +} + +# Main +if [[ -n "$ONLY_REPO" ]]; then + process_repo "$ONLY_REPO" +else + for repo in "${REPOS[@]}"; do + process_repo "$repo" || warn "Unhandled error for $repo - continuing." + done +fi + +log "All repos processed." +log "Work directory: $WORK_DIR" \ No newline at end of file diff --git a/python/precommit_updater.py b/python/precommit_updater.py new file mode 100644 index 0000000..5857e38 --- /dev/null +++ b/python/precommit_updater.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +""" +Standardize a single .pre-commit-config.yaml in place: + - bump pre-commit-hooks / ruff-pre-commit / mypy / check-manifest / codespell revs + - drop the black hook entirely + - make sure ruff-pre-commit has both `ruff` and `ruff-format`, each with + args: [--config=pyproject.toml] +Everything else (napari-plugin-checks, exclude:, ci:, extra mypy deps, etc.) +is left untouched. +""" +import sys +from ruamel.yaml import YAML + +yaml = YAML() +yaml.preserve_quotes = True +yaml.width = 4096 +yaml.indent(mapping=4, sequence=4, offset=2) + +REVS = { + "https://github.com/pre-commit/pre-commit-hooks": "v6.0.0", + "https://github.com/astral-sh/ruff-pre-commit": "v0.16.1", + "https://github.com/pre-commit/mirrors-mypy": "v1.13.0", + "https://github.com/mgedmin/check-manifest": "0.50", + "https://github.com/codespell-project/codespell": "v2.4.3", +} +BLACK_REPO = "https://github.com/psf/black-pre-commit-mirror" +RUFF_REPO = "https://github.com/astral-sh/ruff-pre-commit" +RUFF_ARGS = ["--config=pyproject.toml"] + + +def fix_ruff_hooks(entry): + ids = {h["id"] for h in entry["hooks"]} + for h in entry["hooks"]: + h["args"] = list(RUFF_ARGS) + if "ruff-format" not in ids: + fmt = type(entry["hooks"][0])() + fmt["id"] = "ruff-format" + fmt["args"] = list(RUFF_ARGS) + entry["hooks"].append(fmt) + + +def standardize(path): + with open(path) as f: + data = yaml.load(f) + + new_repos = [] + for entry in data["repos"]: + url = entry["repo"] + if url == BLACK_REPO: + continue # drop black entirely + if url in REVS: + entry["rev"] = REVS[url] + if url == RUFF_REPO: + fix_ruff_hooks(entry) + new_repos.append(entry) + data["repos"] = new_repos + + with open(path, "w") as f: + yaml.dump(data, f) + + +standardize(sys.argv[1] if len(sys.argv) > 1 else ".pre-commit-config.yaml") +print(f"standardized {sys.argv[1] if len(sys.argv) > 1 else '.pre-commit-config.yaml'}") \ No newline at end of file From 52525cee9eb2a5e872e90689f08192c731ee9d92 Mon Sep 17 00:00:00 2001 From: AlgoFoe Date: Thu, 20 Aug 2026 18:33:51 +0530 Subject: [PATCH 2/2] update pr body --- bash/update_precommit_config.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bash/update_precommit_config.sh b/bash/update_precommit_config.sh index 43fc053..35b2047 100644 --- a/bash/update_precommit_config.sh +++ b/bash/update_precommit_config.sh @@ -4,8 +4,8 @@ set -euo pipefail ORG="brainglobe" BRANCH_NAME="update/standardize-precommit" -PR_TITLE="ci: standardise pre-commit config" -PR_BODY="Drops black in favor of ruff-format and bumps pre-commit-hooks/ruff/mypy/check-manifest/codespell revs to match the rest of BrainGlobe. Repo-specific hooks are left untouched." +PR_TITLE="ci: standardize pre-commit config" +PR_BODY="Standardizes the pre-commit configuration across BrainGlobe repositories while preserving repo-specific hooks and settings." WORK_DIR="$(pwd)/.brainglobe_precommit_work" DRY_RUN=false ONLY_REPO="" @@ -88,7 +88,7 @@ process_repo() { # 4. Create branch git -C "$repo_dir" checkout -b "$BRANCH_NAME" - # 5. Run standardize.py + # 5. Run python helper log "Running pre-commit standardizer…" if ! python3 "$PYTHON_HELPER" "$config"; then warn "Standardizer failed for $repo - skipping."