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.
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
-
Clone the repository
git clone https://code.cannabrands.app/Cannabrands/hub.git cd hub -
Install Git hooks (REQUIRED)
git config core.hooksPath .githooks chmod +x .githooks/* -
Install dependencies
./vendor/bin/sail up -d ./vendor/bin/sail composer install ./vendor/bin/sail npm install -
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:
-
See which files have conflicts:
git status # Look for "both modified:" files -
Open conflicted files - look for conflict markers:
<<<<<<< HEAD // Your code ======= // Code from develop >>>>>>> develop -
Resolve conflicts - edit the file to keep what you need:
// Choose your code, their code, or combine both // Remove the <<<, ===, >>> markers -
Mark as resolved:
git add app/Http/Controllers/OrderController.php -
Complete the merge:
git commit -m "merge: resolve conflicts with develop" -
Run tests to ensure nothing broke:
./vendor/bin/sail artisan test -
Push the merge commit:
git push origin feature/my-feature
When Conflicts Are Too Complex
If conflicts are extensive or you're unsure:
-
Abort the merge:
git merge --abort -
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
-
Pair program the resolution - screen share with the person who made the conflicting changes
-
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 featurefix:- Bug fixdocs:- Documentation onlystyle:- Code style (formatting)refactor:- Code refactoringtest:- Adding testschore:- 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.
Pre-Push Hook (Optional but Recommended)
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:
- Quick Reference - One-page cheat sheet (print this!)
- Release Workflow - Complete walkthrough
- Versioning Strategy - CalVer explained
- Git Branching - Branching evolution
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 detailsdocs/URL_STRUCTURE.md- Routing architecturedocs/- 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
@devopsfor 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.