diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..9a67237 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,21 @@ +name: pre-commit + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Install pre-commit + run: pip install pre-commit + - name: Run pre-commit + run: pre-commit run --all-files \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..33a032f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,12 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-added-large-files + - repo: https://github.com/koalaman/shellcheck-precommit + rev: v0.9.0 + hooks: + - id: shellcheck + files: \.(sh)$ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..d3c5837 --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +## Pre-commit Hooks + +This project uses [pre-commit](https://pre-commit.com/) to ensure code quality and consistent formatting for all contributors. + +### First-time setup + +1. Run the setup script (recommended): + ```bash + ./setup.sh + ``` + This will install pre-commit (if not already installed) and set up the git hooks for you. + +2. Or, manually install pre-commit and the hooks: + ```bash + pip install pre-commit + pre-commit install + ``` + +### Running hooks manually + +To check all files with pre-commit hooks: +```bash +pre-commit run --all-files +``` + +### CI Enforcement + +All commits and pull requests are checked with pre-commit hooks in CI. You must pass these checks to merge code. \ No newline at end of file diff --git a/git_repos_rebase_commit_push.sh b/git_repos_rebase_commit_push.sh new file mode 100755 index 0000000..5e4aa85 --- /dev/null +++ b/git_repos_rebase_commit_push.sh @@ -0,0 +1,113 @@ +#!/bin/bash + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +failed_repos=() +failed_msgs=() +success_repos=() + +gitdirs=$(find . -type d -name ".git") + +for gitdir in $gitdirs; do + repo=$(dirname "$gitdir") + cd "$repo" || continue + echo -e "${CYAN}📁 Processing: $repo${NC}" + repo_failed=0 + msg="" + + # Stash uncommitted changes if any + uncommitted=$(git status --porcelain | wc -l | tr -d ' ') + stashed=0 + if [ "$uncommitted" -gt 0 ]; then + echo -e " ${YELLOW}Stashing $uncommitted uncommitted change(s)...${NC}" + if git stash push -u -m "Auto-stash before rebase and push by script"; then + stashed=1 + else + echo -e " ${RED}Failed to stash changes! Skipping repo.${NC}" + repo_failed=1 + msg="Failed to stash changes." + fi + fi + + if [ "$repo_failed" -eq 0 ]; then + # Fetch and rebase + git remote update > /dev/null 2>&1 + if git rev-parse --abbrev-ref --symbolic-full-name @{u} > /dev/null 2>&1; then + echo -e " ${YELLOW}Rebasing onto upstream...${NC}" + if ! git rebase @{u}; then + echo -e " ${RED}Rebase failed! Attempting to abort and restore stash.${NC}" + git rebase --abort + repo_failed=1 + msg="Rebase failed." + fi + else + echo -e " ${RED}No upstream branch set. Skipping rebase and push.${NC}" + repo_failed=1 + msg="No upstream branch set." + fi + fi + + if [ "$repo_failed" -eq 0 ]; then + # Push local-only commits if any + ahead=$(git rev-list --count @{u}..HEAD 2>/dev/null) + if [ "$ahead" -gt 0 ]; then + echo -e " ${YELLOW}Pushing $ahead local commit(s) to remote...${NC}" + if ! git push; then + echo -e " ${RED}Push failed!${NC}" + repo_failed=1 + msg="Push failed." + fi + else + echo -e " ${GREEN}No local-only commits to push.${NC}" + fi + fi + + # Restore stashed changes if any + if [ "$stashed" -eq 1 ]; then + echo -e " ${YELLOW}Restoring stashed changes...${NC}" + if ! git stash pop; then + echo -e " ${RED}Stash pop failed! You may need to resolve conflicts manually.${NC}" + repo_failed=1 + msg="Stash pop failed." + fi + fi + + # Final check + ahead_final=0 + if git rev-parse --abbrev-ref --symbolic-full-name @{u} > /dev/null 2>&1; then + ahead_final=$(git rev-list --count @{u}..HEAD 2>/dev/null) + fi + if [ "$repo_failed" -eq 0 ] && [ "$ahead_final" -eq 0 ]; then + echo -e " ${GREEN}✅ Repo is up to date with remote. No local-only commits.${NC}" + success_repos+=("$repo") + else + if [ "$repo_failed" -eq 0 ]; then + msg="Still $ahead_final local-only commit(s) after push!" + fi + echo -e " ${RED}❌ $msg${NC}" + failed_repos+=("$repo") + failed_msgs+=("$msg") + fi + + echo -e "${CYAN}──────────────────────────────────────────────${NC}" + cd - > /dev/null || exit +done + +# Summary +printf "\n${CYAN}===== SUMMARY =====${NC}\n" +if [ ${#success_repos[@]} -gt 0 ]; then + echo -e "${GREEN}Repos successfully rebased and pushed:${NC}" + for repo in "${success_repos[@]}"; do + echo -e " $repo" + done +fi +if [ ${#failed_repos[@]} -gt 0 ]; then + echo -e "\n${RED}Repos with issues:${NC}" + for i in "${!failed_repos[@]}"; do + echo -e " ${failed_repos[$i]}: ${failed_msgs[$i]}" + done +fi \ No newline at end of file diff --git a/setup.sh b/setup.sh new file mode 100644 index 0000000..fa9dd82 --- /dev/null +++ b/setup.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Bootstrap script to install pre-commit and set up git hooks +set -e + +if ! command -v pre-commit >/dev/null 2>&1; then + echo "pre-commit not found, installing via pip..." + pip3 install pre-commit +else + echo "pre-commit already installed." +fi + +pre-commit install + +echo "pre-commit hooks installed!" \ No newline at end of file