From b1755989ce1b0db92fb56428a040f08b98b65e77 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 28 Feb 2026 05:41:18 +0000 Subject: [PATCH] feat: add branch protection rules, PR check workflow, and CODEOWNERS - Add pr-check.yml workflow: lint, test, frontend build checks on PRs to main/master - Add setup-branch-protection.sh script to configure GitHub branch rules via gh CLI - Add CODEOWNERS file assigning @CodeWithJuber as default reviewer - Add `make protect` target to Makefile Branch protection rules (applied via script): - Require PR with 1 approval before merging - Dismiss stale reviews on new pushes - Require status checks: Lint, Test, Frontend Build - Require branch up to date before merge - Require conversation resolution - Block force pushes and branch deletion - Enforce for admins https://claude.ai/code/session_01JGamoWGB9PN39TCpi3F6J1 --- .github/CODEOWNERS | 15 ++++ .github/workflows/pr-check.yml | 42 +++++++++++ Makefile | 3 + scripts/setup-branch-protection.sh | 112 +++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/pr-check.yml create mode 100755 scripts/setup-branch-protection.sh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..abbbdb3 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,15 @@ +# MIZAN Code Owners +# These users will be automatically requested for review on PRs. + +# Default owner for everything +* @CodeWithJuber + +# Backend core +/backend/core/ @CodeWithJuber +/backend/security/ @CodeWithJuber +/backend/api/main.py @CodeWithJuber + +# Infrastructure +/docker-compose.yml @CodeWithJuber +/.github/workflows/ @CodeWithJuber +/scripts/ @CodeWithJuber diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml new file mode 100644 index 0000000..b35260d --- /dev/null +++ b/.github/workflows/pr-check.yml @@ -0,0 +1,42 @@ +name: Branch Protection Check + +# Enforces quality gates on all PRs to main/master. +# Complement this with GitHub branch protection rules (see scripts/setup-branch-protection.sh). + +on: + pull_request: + branches: [main, master] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: pip install ruff + - run: ruff check backend/ tests/ + - run: ruff format --check backend/ tests/ + + test: + name: Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: pip install -e ".[dev]" + - run: pytest tests/ -v --tb=short + + frontend-build: + name: Frontend Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "20" + - run: cd frontend && npm ci && npx tsc --noEmit && npx vite build diff --git a/Makefile b/Makefile index 3835c5d..3c62ee8 100644 --- a/Makefile +++ b/Makefile @@ -108,3 +108,6 @@ release-major: ## Full release: bump major, changelog, tag, push release-dry: ## Dry run of a patch release (no changes) ./scripts/release.sh patch --dry-run + +protect: ## Setup GitHub branch protection rules for main + ./scripts/setup-branch-protection.sh diff --git a/scripts/setup-branch-protection.sh b/scripts/setup-branch-protection.sh new file mode 100755 index 0000000..f968602 --- /dev/null +++ b/scripts/setup-branch-protection.sh @@ -0,0 +1,112 @@ +#!/usr/bin/env bash +# ═══════════════════════════════════════════════════════════════ +# MIZAN Branch Protection Setup +# +# Sets up GitHub branch protection rules for main branch. +# Requires: gh CLI authenticated with repo admin access. +# +# Usage: +# ./scripts/setup-branch-protection.sh +# ./scripts/setup-branch-protection.sh --branch master +# ═══════════════════════════════════════════════════════════════ + +set -euo pipefail + +GOLD='\033[0;33m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +RED='\033[0;31m' +BOLD='\033[1m' +NC='\033[0m' + +info() { echo -e " ${BLUE}➜${NC} $1"; } +success() { echo -e " ${GREEN}✓${NC} $1"; } +error() { echo -e " ${RED}✗${NC} $1"; exit 1; } +step() { echo -e "\n ${GOLD}━━━${NC} ${BOLD}$1${NC}"; } + +BRANCH="${1:---branch}" +if [ "$BRANCH" = "--branch" ]; then + BRANCH="${2:-main}" +fi + +# Detect repo from git remote +REPO=$(gh repo view --json nameWithOwner -q '.nameWithOwner' 2>/dev/null) || true +if [ -z "$REPO" ]; then + REPO=$(git remote get-url origin | sed -E 's|.*github\.com[:/](.+)(\.git)?$|\1|' | sed 's/\.git$//') +fi + +if [ -z "$REPO" ]; then + error "Could not detect repository. Run from within a git repo with a GitHub remote." +fi + +echo "" +echo -e " ${GOLD}═══════════════════════════════════════════════════${NC}" +echo -e " ${BOLD} MIZAN Branch Protection Setup${NC}" +echo -e " ${GOLD}═══════════════════════════════════════════════════${NC}" +echo "" +info "Repository: $REPO" +info "Branch: $BRANCH" + +# ───── Check prerequisites ───── + +step "Checking prerequisites" + +if ! command -v gh &>/dev/null; then + error "GitHub CLI (gh) not installed. Install: https://cli.github.com" +fi +success "gh CLI found" + +if ! gh auth status &>/dev/null; then + error "Not authenticated. Run: gh auth login" +fi +success "Authenticated with GitHub" + +# ───── Apply branch protection rules ───── + +step "Applying branch protection rules" + +gh api \ + --method PUT \ + -H "Accept: application/vnd.github+json" \ + "/repos/$REPO/branches/$BRANCH/protection" \ + -f "required_status_checks[strict]=true" \ + -f "required_status_checks[contexts][]=Lint" \ + -f "required_status_checks[contexts][]=Test" \ + -f "required_status_checks[contexts][]=Frontend Build" \ + -f "enforce_admins=true" \ + -f "required_pull_request_reviews[dismiss_stale_reviews]=true" \ + -f "required_pull_request_reviews[require_code_owner_reviews]=false" \ + -f "required_pull_request_reviews[required_approving_review_count]=1" \ + -F "restrictions=null" \ + -F "allow_force_pushes=false" \ + -F "allow_deletions=false" \ + -F "block_creations=false" \ + -F "required_linear_history=false" \ + -F "required_conversation_resolution=true" \ + > /dev/null 2>&1 + +success "Branch protection rules applied" + +# ───── Summary ───── + +echo "" +echo -e " ${GOLD}═══════════════════════════════════════════════════${NC}" +echo -e " ${GREEN}${BOLD} Branch protection enabled for $BRANCH${NC}" +echo -e " ${GOLD}═══════════════════════════════════════════════════${NC}" +echo "" +echo -e " ${BOLD}Rules applied:${NC}" +echo -e " ${GREEN}✓${NC} Require PR before merging (1 approval)" +echo -e " ${GREEN}✓${NC} Dismiss stale reviews on new pushes" +echo -e " ${GREEN}✓${NC} Require status checks to pass:" +echo -e " • Lint (ruff check + format)" +echo -e " • Test (pytest)" +echo -e " • Frontend Build (tsc + vite)" +echo -e " ${GREEN}✓${NC} Require branches to be up to date" +echo -e " ${GREEN}✓${NC} Require conversations resolved" +echo -e " ${GREEN}✓${NC} Enforce for admins too" +echo -e " ${GREEN}✓${NC} Block force pushes" +echo -e " ${GREEN}✓${NC} Block branch deletion" +echo "" +echo -e " ${BOLD}To verify:${NC}" +echo -e " ${BLUE}https://github.com/$REPO/settings/branches${NC}" +echo ""