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
112 changes: 112 additions & 0 deletions .github/BRANCH_PROTECTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Branch Protection Setup

This document describes how to set up branch protection for the `main` branch using GitHub's API or web interface.

## Option 1: Using GitHub Web Interface (Recommended)

1. Go to your repository on GitHub
2. Navigate to **Settings** → **Branches**
3. Click **Add rule** or edit the existing rule for `main`
4. Configure the following settings:
- **Branch name pattern**: `main`
- ✅ **Require a pull request before merging**
- ✅ Require approvals: `1` (or more)
- ✅ Dismiss stale pull request approvals when new commits are pushed
- ✅ **Require status checks to pass before merging**
- ✅ Require branches to be up to date before merging
- Select required status checks:
- `Test (Python 3.11) / ubuntu-latest`
- `Test (Python 3.11) / macos-latest`
- `Test (Python 3.12) / ubuntu-latest`
- `Test (Python 3.12) / macos-latest`
- `Test (Python 3.13) / ubuntu-latest`
- `Test (Python 3.13) / macos-latest`
- `Lint`
- ✅ **Require conversation resolution before merging**
- ✅ **Do not allow bypassing the above settings**
- ✅ **Restrict who can push to matching branches** (optional, but recommended)
- ✅ **Allow force pushes** (unchecked - disable force pushes)
- ✅ **Allow deletions** (unchecked - prevent branch deletion)

## Option 2: Using GitHub CLI

```bash
# Install GitHub CLI if not already installed
# brew install gh # macOS
# apt install gh # Linux

# Authenticate
gh auth login

# Set branch protection rules
gh api repos/:owner/:repo/branches/main/protection \
--method PUT \
--field required_status_checks='{"strict":true,"contexts":["Test (Python 3.11) / ubuntu-latest","Test (Python 3.11) / macos-latest","Test (Python 3.12) / ubuntu-latest","Test (Python 3.12) / macos-latest","Test (Python 3.13) / ubuntu-latest","Test (Python 3.13) / macos-latest","Lint"]}' \
--field enforce_admins=true \
--field required_pull_request_reviews='{"required_approving_review_count":1,"dismiss_stale_reviews":true}' \
--field restrictions=null \
--field allow_force_pushes=false \
--field allow_deletions=false
```

Replace `:owner` and `:repo` with your GitHub username and repository name.

## Option 3: Using GitHub API with curl

```bash
# Set your GitHub token
export GITHUB_TOKEN=your_token_here

# Set branch protection
curl -X PUT \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/vadimvolk/git-worktree-wrapper/branches/main/protection \
-d '{
"required_status_checks": {
"strict": true,
"contexts": [
"Test (Python 3.11) / ubuntu-latest",
"Test (Python 3.11) / macos-latest",
"Test (Python 3.12) / ubuntu-latest",
"Test (Python 3.12) / macos-latest",
"Test (Python 3.13) / ubuntu-latest",
"Test (Python 3.13) / macos-latest",
"Lint"
]
},
"enforce_admins": true,
"required_pull_request_reviews": {
"required_approving_review_count": 1,
"dismiss_stale_reviews": true
},
"restrictions": null,
"allow_force_pushes": false,
"allow_deletions": false
}'
```

## Verification

After setting up branch protection, verify it works:

1. Create a test branch: `git checkout -b test-branch-protection`
2. Make a change and push: `git push origin test-branch-protection`
3. Create a pull request to `main`
4. Verify that:
- The PR cannot be merged until CI tests pass
- The PR requires at least one approval
- Force push to `main` is blocked

## Notes

- The CI workflow runs on all branches (push events) and on pull requests to `main`
- After merging to `main`, the CI will run again to verify the merge
- Status checks must pass before merging pull requests
- **Important**: After the first CI run completes, check the actual status check names in GitHub:
1. Go to any pull request or commit
2. View the "Checks" tab
3. Note the exact names of the status checks (e.g., "Test (Python 3.11)", "Lint")
4. Update the branch protection settings with the exact check names
- Alternatively, you can require only the job names (`test` and `lint`) if GitHub supports that, or require "any status check" to pass
85 changes: 85 additions & 0 deletions .github/scripts/setup-branch-protection.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/bin/bash
# Setup branch protection for main branch using GitHub CLI

set -e

REPO_OWNER="${GITHUB_REPOSITORY_OWNER:-vadimvolk}"
REPO_NAME="${GITHUB_REPOSITORY_NAME:-git-worktree-wrapper}"
BRANCH="main"

echo "Setting up branch protection for ${REPO_OWNER}/${REPO_NAME}:${BRANCH}"

# Check if gh CLI is installed
if ! command -v gh &> /dev/null; then
echo "Error: GitHub CLI (gh) is not installed."
echo "Install it with: brew install gh # macOS"
echo " apt install gh # Linux"
exit 1
fi

# Check if authenticated
if ! gh auth status &> /dev/null; then
echo "Error: Not authenticated with GitHub CLI."
echo "Run: gh auth login"
exit 1
fi

# Set branch protection
echo "Configuring branch protection rules..."

# Create temporary JSON file for the protection payload
# After first CI run, update the "contexts" array with actual check names
# Example: "contexts": ["Test (Python 3.11)", "Test (Python 3.12)", "Lint"]
TMP_FILE=$(mktemp)
cat > "$TMP_FILE" <<EOF
{
"required_status_checks": {
"strict": true,
"contexts": []
},
"enforce_admins": true,
"required_pull_request_reviews": {
"required_approving_review_count": 1,
"dismiss_stale_reviews": true,
"require_code_owner_reviews": false,
"require_last_push_approval": false
},
"restrictions": null,
"allow_force_pushes": false,
"allow_deletions": false,
"required_linear_history": false,
"required_conversation_resolution": true,
"allow_squash_merge": true,
"allow_merge_commit": true,
"allow_rebase_merge": true
}
EOF

gh api "repos/${REPO_OWNER}/${REPO_NAME}/branches/${BRANCH}/protection" \
--method PUT \
--input "$TMP_FILE"

# Clean up
rm "$TMP_FILE"

echo "✅ Branch protection configured successfully!"
echo ""
echo "Branch protection rules:"
echo " - Require pull request reviews (1 approval, author can merge own PRs)"
echo " - Require conversation resolution before merging"
echo " - Require status checks to pass"
echo " - Require branches to be up to date"
echo " - Enforce admins"
echo " - Disallow force pushes"
echo " - Disallow deletions"
echo ""
echo ""
echo "⚠️ Important: Status checks are currently set to an empty list."
echo " After the first CI run completes:"
echo " 1. Go to any PR or commit and check the 'Checks' tab"
echo " 2. Note the exact status check names (e.g., 'Test (Python 3.11)', 'Lint')"
echo " 3. Update branch protection via GitHub web interface:"
echo " Settings → Branches → Edit rule for 'main'"
echo " → Add the status checks under 'Require status checks to pass'"
echo ""
echo " Or update this script with the correct context names and run it again."
64 changes: 64 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: CI

on:
push:
branches:
- '**' # Run on all branches
pull_request:
branches:
- main
- master

jobs:
test:
name: Test (Python ${{ matrix.python-version }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ['3.11', '3.12', '3.13']

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: uv sync --extra dev

- name: Run tests
run: uv run pytest

lint:
name: Lint
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Install dependencies
run: uv sync --extra dev

- name: Run mypy
run: uv run mypy src/gww
Loading