Files
hub/CONTRIBUTING.md
Jon Leopard 7fa9b6aff8 docs: add comprehensive guide for keeping feature branches up-to-date
Added new section "Keeping Your Feature Branch Up-to-Date" covering:
- Daily start-of-work routine for syncing with develop
- Merge vs rebase best practices for teams
- Step-by-step conflict resolution guide
- When and how to ask for help with complex conflicts
- Real-world example of multi-day feature work

This addresses common questions from contributors about branch
management and helps prevent large merge conflicts by encouraging
regular syncing with develop.
2025-11-06 15:05:27 -07:00

18 KiB

Contributing to Cannabrands CRM

Philosophy: Balanced Enforcement for Small Teams

Our Team Size: ~5 Developers

We use graduated enforcement - different rules for different checks:

🔒 ENFORCE: Code formatting (<1 second)   → Always automatic
⚠️  ENCOURAGE: Tests (~30 seconds)         → Automatic, can skip
🚫 BLOCK: CI failures (~5 minutes)        → Can't bypass

Why this balance?

  • Not too strict (Google/Facebook have 1000s of developers)
  • Not too lenient (prevents chaos and broken production)
  • Just right (Goldilocks zone for 5-person team)

Real-World Time Savings

Scenario Without Hooks With Hooks Time Saved
Tests fail in CI Wait 5-10 mins Catch in 30s locally 4-9 minutes
Format issues CI fails, fix, wait 5 mins Auto-fixed in 1s 5 minutes
Push WIP Can't (tests fail) Use --no-verify Flexibility gained

Result: Faster feedback, fewer CI failures, happier team.

Cannabis Compliance Benefits

Our workflow provides audit trails regulators love:

  • All code formatted consistently (quality)
  • Tests run before deployment (safety)
  • CI logs every build (compliance record)
  • Conventional commits (clear changelog)
  • Can't deploy broken code (blocked by CI)

Getting Started

First Time Setup

  1. Clone the repository

    git clone https://code.cannabrands.app/Cannabrands/hub.git
    cd hub
    
  2. Install Git hooks (REQUIRED)

    git config core.hooksPath .githooks
    chmod +x .githooks/*
    
  3. Install dependencies

    ./vendor/bin/sail up -d
    ./vendor/bin/sail composer install
    ./vendor/bin/sail npm install
    
  4. Run tests (verify setup)

    ./vendor/bin/sail artisan test
    

Branch Protection & Pull Request Workflow

IMPORTANT: The develop and master branches are protected - you cannot push directly to them.

Standard Workflow:

# 1. Create a feature branch
git checkout -b feature/my-feature-name

# 2. Make changes and commit
git add .
git commit -m "feat: add new feature"

# 3. Push to your feature branch
git push origin feature/my-feature-name

# 4. Create Pull Request on Gitea
#    - Navigate to https://code.cannabrands.app
#    - Create PR to merge your branch into develop
#    - CI will run automatically
#    - Request review from team

# 5. After approval and passing CI
#    - Merge PR via Gitea interface
#    - Delete feature branch

Branch Naming Conventions:

  • feature/ - New features (e.g., feature/bulk-import)
  • fix/ - Bug fixes (e.g., fix/tax-calculation)
  • chore/ - Maintenance tasks (e.g., chore/upgrade-php)
  • docs/ - Documentation changes (e.g., docs/update-readme)

Real-World Team Scenarios

Scenario 1: Normal Feature Development

Developer Jon adds bulk import feature

$ git checkout -b feature/bulk-import   # Create feature branch
$ vim app/Orders.php                    # Make changes
$ git add .
$ git commit -m "feat(orders): add bulk import"
   🎨 Pre-commit: Pint formats code (1s) ✅

$ git push origin feature/bulk-import
   🧪 Pre-push: Tests run (30s) ✅
   ✅ All tests passed! Pushing...
   🚀 Create PR → merge to develop → CI verifies (5min)

Time cost: 31 seconds (vs 5+ minutes if tests failed in CI)

Scenario 2: Quick Documentation Fix

Developer Sarah updates README

$ vim README.md                         # Fix typo
$ git commit -m "docs: fix installation steps"
   🎨 Pre-commit: Skipped (no PHP files) ✅

$ git push --no-verify                 # Skip tests (just docs)
   ✅ Pushed instantly
   🚀 CI: Verifies anyway ✅

Time cost: 5 seconds (minimal friction for non-code changes)

Scenario 3: Work in Progress (WIP)

Developer Mike working on complex feature

$ vim app/NewFeature.php                # Halfway done, tests broken
$ git commit -m "wip: payment integration (incomplete)"
   🎨 Pre-commit: Pint formats ✅

$ git push --no-verify                  # Skip tests intentionally
   ✅ Pushed to share with team
   ⚠️  CI: Tests fail (expected for WIP)
   → Will fix before merge to master ✅

Flexibility: Can share WIP without blocking the team

Scenario 4: Emergency Hotfix

Developer Emma fixes production bug

$ git checkout -b fix/tax-calculation    # Create hotfix branch
$ vim app/Invoice.php                    # Critical bug fix
$ git commit -m "fix(invoices): correct tax calculation"
   🎨 Pre-commit: Formats ✅

$ git push origin fix/tax-calculation
   🧪 Pre-push: Tests run (30s) ✅
   🚀 Create PR → fast-track review → merge to develop ✅
   📦 CI: Passes (5min) → Safe to release ✅

Safety: Tests caught regression before it reached production

Scenario 5: Dockerfile Changes

Developer Alex updates dependencies

$ git checkout -b chore/php-8.3-upgrade # Create branch
$ vim Dockerfile                         # Update PHP version

# Test locally FIRST (best practice)
$ docker build -t cannabrands:test .
   ⏱️  Build completes (3min)# Then push
$ git commit -m "chore: upgrade PHP to 8.3"
$ git push origin chore/php-8.3-upgrade
   🚀 Create PR → CI rebuilds (8min)

Time saved: 5 minutes by catching Docker issues locally


Development Workflow

The Three-Layer Safety Net

We use a "graduated enforcement" model:

Layer 1: Pre-commit  (ENFORCED)  → Code formatting   (~1 second)
Layer 2: Pre-push    (OPTIONAL)  → Run tests         (~30 seconds)
Layer 3: CI          (REQUIRED)  → Final verification (~5 minutes)

Daily Development

For most changes:

# 1. Create feature branch
git checkout -b feature/my-feature

# 2. Make your changes
vim app/SomeFile.php

# 3. Commit (formatting happens automatically)
git add .
git commit -m "feat(scope): description"
   → Pre-commit runs Laravel Pint ✅
   → Code formatted automatically ✅

# 4. Push (tests run automatically)
git push origin feature/my-feature
   → Pre-push runs tests (30 seconds) ✅
   → If tests pass, push continues ✅

# 5. Create Pull Request
   → Open PR on Gitea to merge into develop
   → CI verifies everything (5 minutes) ✅
   → After review, merge PR

For quick documentation changes:

# Skip tests when not changing code
git push --no-verify

Keeping Your Feature Branch Up-to-Date

Best practice for teams: Sync your feature branch with develop regularly to avoid large merge conflicts.

Daily Start-of-Work Routine

# 1. Get latest changes from develop
git checkout develop
git pull origin develop

# 2. Update your feature branch
git checkout feature/my-feature
git merge develop

# 3. If there are conflicts (see below), resolve them
# 4. Continue working

How often?

  • Minimum: Once per day (start of work)
  • Better: Multiple times per day if develop is active
  • Always: Before creating your Pull Request

Merge vs Rebase: Which to Use?

For teams of 5+ developers, use merge (not rebase):

git checkout feature/my-feature
git merge develop

Why merge over rebase?

  • Safer: Preserves your commit history
  • Collaborative: Works when multiple people work on the same feature branch
  • Transparent: Shows when you integrated upstream changes
  • No force-push: Once you've pushed to origin, merge won't require --force

When to use rebase:

  • ⚠️ Only if you haven't pushed yet
  • ⚠️ Only if you're the sole developer on the branch
  • ⚠️ You want a cleaner, linear history
# Only do this if you haven't pushed yet!
git checkout feature/my-feature
git rebase develop

Never rebase after pushing - it rewrites history and breaks collaboration.

Handling Merge Conflicts

When you run git merge develop and see conflicts:

$ git merge develop
Auto-merging app/Http/Controllers/OrderController.php
CONFLICT (content): Merge conflict in app/Http/Controllers/OrderController.php
Automatic merge failed; fix conflicts and then commit the result.

Step-by-step resolution:

  1. See which files have conflicts:

    git status
    # Look for "both modified:" files
    
  2. Open conflicted files - look for conflict markers:

    <<<<<<< HEAD
    // Your code
    =======
    // Code from develop
    >>>>>>> develop
    
  3. Resolve conflicts - edit the file to keep what you need:

    // Choose your code, their code, or combine both
    // Remove the <<<, ===, >>> markers
    
  4. Mark as resolved:

    git add app/Http/Controllers/OrderController.php
    
  5. Complete the merge:

    git commit -m "merge: resolve conflicts with develop"
    
  6. Run tests to ensure nothing broke:

    ./vendor/bin/sail artisan test
    
  7. Push the merge commit:

    git push origin feature/my-feature
    

When Conflicts Are Too Complex

If conflicts are extensive or you're unsure:

  1. Abort the merge:

    git merge --abort
    
  2. Ask for help in #engineering Slack:

    • "I'm merging develop into feature/X and have conflicts in OrderController"
    • Someone might have context on the upstream changes
  3. Pair program the resolution - screen share with the person who made the conflicting changes

  4. Alternative: Start fresh (last resort):

    # Create new branch from latest develop
    git checkout develop
    git pull origin develop
    git checkout -b feature/my-feature-v2
    
    # Cherry-pick your commits
    git cherry-pick <commit-hash>
    

Example: Multi-Day Feature Work

# Monday morning
git checkout develop && git pull origin develop
git checkout feature/payment-integration
git merge develop  # Get latest changes
# Work all day, make commits

# Tuesday morning
git checkout develop && git pull origin develop
git checkout feature/payment-integration
git merge develop  # Sync again (someone added auth changes)
# Continue working

# Wednesday
git checkout develop && git pull origin develop
git checkout feature/payment-integration
git merge develop  # Final sync before PR
git push origin feature/payment-integration
# Create Pull Request

Result: Small, manageable syncs instead of one huge conflict on PR day.

When to Test Locally

Always run tests before pushing if you:

  • Changed application logic
  • Added new features
  • Fixed bugs
  • Modified database migrations
./vendor/bin/sail artisan test

Test Docker builds locally if you:

  • Changed Dockerfile
  • Updated dependencies (composer.json, package.json)
  • Modified build process
docker build -t cannabrands:test .

Commit Message Format

We use Conventional Commits:

type(scope): description

[optional body]

[optional footer]

Types:

  • feat: - New feature
  • fix: - Bug fix
  • docs: - Documentation only
  • style: - Code style (formatting)
  • refactor: - Code refactoring
  • test: - Adding tests
  • chore: - Build/dependencies

Examples:

git commit -m "feat(orders): add bulk CSV import"
git commit -m "fix(invoices): correct tax calculation for CA"
git commit -m "docs: update API documentation"
git commit -m "chore: upgrade Laravel to 11.x"

Why? These conventions enable auto-changelog generation for releases.


When CI Fails

Step 1: Check What Failed

Visit: https://ci.cannabrands.app/repos/1

Step 2: Reproduce Locally

# If tests failed:
./vendor/bin/sail artisan test

# If code style failed:
./vendor/bin/pint --test

# If Docker build failed:
docker build -t cannabrands:test .

Step 3: Fix and Push

# Fix the issue
vim app/SomeFile.php

# Verify fix locally
./vendor/bin/sail artisan test

# Push fix
git add .
git commit -m "fix: resolve test failure"
git push origin feature/my-feature  # Push to your feature branch

Bypassing Hooks (When & Why)

When it's OK to bypass:

Pushing WIP to share with team:

git push --no-verify  # Skip pre-push tests

Quick documentation fixes:

git push --no-verify  # Skip tests for README changes

Emergency hotfixes (with teammate approval):

git commit --no-verify  # Skip formatting (fix in next commit)

When it's NOT OK:

Skipping because tests fail → Fix the tests instead Skipping to avoid formatting → Let Pint format it Skipping to merge PR to develop/master → CI will block you anyway

Remember: CI can't be bypassed, and develop/master are protected branches requiring PRs and passing CI.


The pre-push hook runs your test suite automatically before pushing.

Enable it (one-time):

git config core.hooksPath .githooks

How it works:

$ git push origin feature/my-feature

🧪 Running tests before push...
   (Use 'git push --no-verify' to skip)

✅ All tests passed! Pushing...

Skip when needed:

git push --no-verify

Docker Build Testing

Before pushing Dockerfile changes, always test locally first:

# Build image
docker build -t cannabrands:test .

# If successful, test run it
docker run --rm cannabrands:test php -v

# Then push to feature branch
git push origin feature/my-feature

Why? Docker builds take 5-10 minutes in CI vs 2-3 minutes locally.


Code Review Checklist

Before requesting review, ensure:

  • All tests pass locally
  • Code formatted (automatic via pre-commit)
  • Conventional commit messages used
  • No sensitive data in commit (.env, tokens, etc.)
  • CI build is passing
  • Changes tested in local environment

Release Process (For Maintainers)

Most developers don't need this section. Release management is handled by 1-2 designated team members.

If you're responsible for creating releases, see:

TL;DR for releases:

# 1. Determine version (CalVer: YYYY.MM.MICRO)
git tag -l "2025.11.*" | sort -V | tail -1  # Check latest

# 2. Ensure you're on master and up-to-date
git checkout master
git pull origin master

# 3. Create release tag on master
git tag -a 2025.11.1 -m "Release notes here"
git push origin 2025.11.1

# 4. CI builds production image automatically

# 5. Generate changelog (create PR for this)
git checkout -b chore/changelog-2025.11.1
npm run changelog
git add CHANGELOG.md
git commit -m "docs: update changelog for 2025.11.1"
git push origin chore/changelog-2025.11.1
# Create PR to merge into master

Getting Help

Documentation

For daily development:

  • CONTRIBUTING.md - This file (daily workflow)
  • .githooks/README.md - Git hooks details
  • docs/URL_STRUCTURE.md - Routing architecture
  • docs/ - API and architecture docs

For releases and operations:

  • .woodpecker/QUICK_REFERENCE.md - Release cheat sheet
  • .woodpecker/RELEASE_WORKFLOW_GUIDE.md - Full release guide
  • .woodpecker/VERSIONING_STRATEGY.md - Versioning & rollback

Team

  • Ask in #engineering Slack channel
  • Tag @devops for CI/CD issues
  • Pair program for complex changes

Services

  • Woodpecker CI: https://ci.cannabrands.app
  • Gitea: https://code.cannabrands.app
  • Production: https://app.cannabrands.com (future)

Philosophy

"Make the right thing easy, and the wrong thing possible (but audited)."

We balance:

  • Automation (pre-commit formatting)
  • Guidance (pre-push tests)
  • Freedom (can bypass with --no-verify)
  • Safety (CI as final gate)

Trust the process, and the process will catch your mistakes before they reach production. 🚀


Team Growth: Scaling from 5 to 50 Developers

Current Setup (5 developers)

🔒 ENFORCE: Formatting only
⚠️  ENCOURAGE: Tests (can skip)
🚫 BLOCK: CI failures

Why: Small team knows each other, high trust, fast iteration

Future (10-20 developers)

Consider adding:

  • Code review requirement for certain files
  • Protected branches already in place (develop/master require PRs)
  • Mandatory tests on pre-push (harder to skip)

If You Reach 50+ developers

You might need:

  • Stricter enforcement (can't skip tests)
  • Automated code review tools
  • Monorepo tooling
  • Feature flags for gradual rollouts

Good news: The foundations we built today (graduated enforcement, git hooks, CI gates) scale perfectly. You'll just tighten the screws, not rebuild everything.


The Perfect Balance Formula

For small teams (5-10 developers):

Success =
    Fast automatic checks (no thinking required)
  + Optional slower checks (can skip when needed)
  + Required CI gate (can't bypass)
  + Clear documentation (everyone knows the rules)
  + Trust + flexibility (not a police state)

You now have all 5 pieces!

This setup respects developer time, prevents production disasters, and provides compliance audit trails. Adjust enforcement levels as your team grows, but the core principles stay the same.


Feedback & Iteration

After 2 weeks, ask the team:

  • Is this helping or hurting?
  • Are hooks too strict or too lenient?
  • Should we enforce tests on certain files?
  • What's causing the most friction?

Then adjust: The beauty of this system is it's easy to tighten or loosen enforcement without rebuilding everything.