Skip to content

GitHub Workflow & Branch Protection

This document describes the GitHub repository configuration optimized for solo developer workflow while maintaining code quality.

Branch Model

feature/* ──PR (squash)──▶ staging ──promotion PR (rebase, via release.sh)──▶ main
  • Feature PRs always target staging and are squash-merged. Every merge to staging triggers CI and, when green, an automatic staging deploy.
  • main is human-merged and only receives staging→main promotion PRs (or release/* hot-fix branches). The Enforce PR base workflow (check-base) blocks any other PR against main.
  • There is no production environment. Promoting to main keeps the branches in sync but does not deploy anywhere; the Deploy to Production workflow is disabled.

Promotion: scripts/release.sh

Promotion staging→main runs exclusively via:

scripts/release.sh --promote-only --no-uat

Since June 2026 this is fully automated: the script finds the green CI run with a successful deploy job for the staging HEAD, opens the promotion PR (base=main, head=staging), rebase-merges it (linear history), recreates staging if GitHub's delete_branch_on_merge removed it, and retargets open PRs back to staging. Promotion PRs skip the heavy CI jobs (the precheck job in ci.yml detects them); check-base is the only required check.

Branch Protection Rules

main

Rule Setting Purpose
Required status checks check-base (Enforce PR base workflow) Blocks PRs to main that don't come from staging or release/*
Strict status checks Enabled Branch must be up-to-date with main
Required signatures Disabled Removes GPG signing friction for solo dev
Enforce admins Disabled Owner can bypass rules when needed
Required PR reviews Disabled No approval needed for solo dev
Required conversation resolution Enabled Open review threads must be resolved
Linear history Enabled Clean, readable git history (promotion PRs rebase-merge)
Force pushes Disabled Protects commit history
Deletions Disabled Prevents accidental branch deletion

staging

Rule Setting Purpose
Required status checks None Feature PRs gate on CI informally; deploy job only runs after green tests
Force pushes Disabled Protects deploy history
Deletions Disabled Prevents delete_branch_on_merge from removing staging after a promotion merge

Note: the repository has delete_branch_on_merge: true, so feature branches are cleaned up automatically after merge.

Standard Workflow

# Create feature branch
git checkout staging && git pull
git checkout -b feature/new-feature

# Make changes and push (pre-push hook runs local tests)
git add .
git commit -m "feat: add new feature"
git push -u origin feature/new-feature

# Create PR against staging — CI starts automatically on the PR
gh pr create --base staging --title "feat: add new feature" --body "Description"

# After CI is green, squash-merge
gh pr merge --squash

# CI on the staging push runs the tests again and deploys to staging
# Later, promote staging → main:
scripts/release.sh --promote-only --no-uat

Direct pushes to main are not part of the workflow — main only moves via promotion PRs.

Quality Gates

Local (Pre-push Hook)

Located at .githooks/pre-push, runs before every push:

  1. Backend tests - Django unit tests (subset for speed; requires local PostgreSQL on 5432 and Redis on 6379, e.g. via docker-compose.dev.yml)
  2. TypeScript check - tsc --noEmit
  3. CodeRabbit review - AI code review (non-blocking)
  4. Frontend tests - Jest with coverage

Never bypass the hook with --no-verify.

Remote (GitHub Actions CI)

.github/workflows/ci.yml runs on every push to main and staging, and on every PR against main or staging:

  1. precheck - Detects staging→main promotion PRs and skips the heavy jobs for them (the code was already fully tested on staging)
  2. backend-test - flake8, migration checks, full pytest suite (parallel, coverage ≥60%)
  3. frontend-test - TypeScript check, ESLint, full Jest suite
  4. frontend-build - Next.js production build (artifact reused by the E2E jobs)
  5. e2e-smoke-test - Playwright @smoke tests
  6. e2e-visual-test - Visual regression tests
  7. deploy - Only on pushes to staging: calls deploy-staging.yml (workflow_call) after all jobs above are green

Runs only on schedule (cron, weekly) or manual dispatch (workflow_dispatch):

  1. e2e-full-test - Full Playwright suite
  2. security-scan-dependencies - npm audit, pip-audit, Snyk
  3. docker-build - Validates Docker images build + Trivy scans

GitHub Pro Features Used

Feature Usage
3,000 Actions minutes/month Heavy CI pipeline (tests, E2E, Docker)
Private repo CI All CI runs on private repository
Branch protection Status checks without PR requirement

Changing Branch Protection

Via GitHub CLI

# View current settings
gh api repos/Voorman/webshop_freeze_design/branches/main/protection

# Update settings (current config: check-base is the only required check)
gh api repos/Voorman/webshop_freeze_design/branches/main/protection -X PUT \
  --input - << 'EOF'
{
  "required_status_checks": {
    "strict": true,
    "checks": [
      {"context": "check-base"}
    ]
  },
  "enforce_admins": false,
  "required_pull_request_reviews": null,
  "restrictions": null,
  "required_linear_history": true,
  "allow_force_pushes": false,
  "allow_deletions": false
}
EOF

# Enable/disable signature requirement
gh api repos/OWNER/REPO/branches/main/protection/required_signatures -X POST  # Enable
gh api repos/OWNER/REPO/branches/main/protection/required_signatures -X DELETE # Disable

Warning

The required-status-checks config has been observed to silently revert to an older set of checks (May 2026, cause unknown). If a promotion PR fails with "base branch policy prohibits the merge", verify the current config with gh api .../branches/main/protection/required_status_checks and re-apply check-base via the API — not via the UI.

Via GitHub UI

  1. Go to SettingsBranchesBranch protection rules
  2. Click Edit on main
  3. Modify settings as needed

Future Considerations

When to Add PR Requirements

Consider enabling required PR reviews if:

  • You add team members
  • You want enforced code review
  • Compliance requirements change

When to Add GPG Signing

Consider enabling required signatures if:

  • Working on security-sensitive features
  • Contributing to open source
  • Corporate compliance requires it

To setup GPG signing:

# Generate GPG key
gpg --full-generate-key

# Get key ID
gpg --list-secret-keys --keyid-format=long

# Configure Git
git config --global user.signingkey YOUR_KEY_ID
git config --global commit.gpgsign true

# Add public key to GitHub
gpg --armor --export YOUR_KEY_ID | pbcopy
# Paste at: GitHub → Settings → SSH and GPG keys → New GPG key

Architecture Decisions

Task Queue: Celery (Decision: January 2026)

Decision: Keep Celery + Redis instead of migrating to Django 6.0 Tasks.

Reasons: - Django Tasks backend still in beta (not production guaranteed) - Celery is battle-tested (10+ years) - Current setup works reliably - Low task volume (~50/day) doesn't justify migration risk - €20-40/mo savings not worth beta risk for e-commerce

Revisit when: - Django Tasks backends reach v1.0 stable - Django 6.1+ released with matured ecosystem - Task volume significantly increases

Alternatives considered: - Django Tasks + django-tasks (too new) - Django-Q2 (viable alternative if Celery issues arise)