Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -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
42 changes: 42 additions & 0 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
112 changes: 112 additions & 0 deletions scripts/setup-branch-protection.sh
Original file line number Diff line number Diff line change
@@ -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 ""
Loading