Compare commits
2 Commits
feat/omnic
...
feat/cicd-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80b58d5973 | ||
|
|
52b6bae17e |
@@ -1,19 +1,26 @@
|
||||
# Woodpecker CI/CD Pipeline for Cannabrands Hub
|
||||
# Documentation: https://woodpecker-ci.org/docs/intro
|
||||
#
|
||||
# 3-Environment Workflow:
|
||||
# - develop branch → dev.cannabrands.app (unstable, daily integration)
|
||||
# - master branch → staging.cannabrands.app (stable, pre-production)
|
||||
# - tags (2025.X) → cannabrands.app (production releases)
|
||||
# 2-Environment Workflow (Optimized for small team):
|
||||
# - develop branch → dev.cannabrands.app (integration/testing)
|
||||
# - master branch → cannabrands.app (production)
|
||||
# - tags (2025.X) → cannabrands.app (versioned production releases)
|
||||
#
|
||||
# Pipeline optimizations:
|
||||
# - Pre-built CI image with PHP extensions (saves ~60-90s)
|
||||
# - Parallel lint + code-style checks
|
||||
# - Skip tests on merge if PR tests passed
|
||||
# - Conditional seeder validation (develop only)
|
||||
|
||||
when:
|
||||
- branch: [develop, master]
|
||||
event: push
|
||||
- event: [pull_request, tag]
|
||||
|
||||
# Install dependencies first (needed for php-lint to resolve traits/classes)
|
||||
# ============================================
|
||||
# STEP 1: Restore Composer Cache
|
||||
# ============================================
|
||||
steps:
|
||||
# Restore Composer cache
|
||||
restore-composer-cache:
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
@@ -26,18 +33,13 @@ steps:
|
||||
volumes:
|
||||
- /tmp/woodpecker-cache:/tmp/cache
|
||||
|
||||
# Install dependencies
|
||||
# ============================================
|
||||
# STEP 2: Install Composer Dependencies
|
||||
# ============================================
|
||||
# Uses pre-built CI image with all PHP extensions
|
||||
composer-install:
|
||||
image: php:8.3-cli
|
||||
image: code.cannabrands.app/cannabrands/ci-runner:latest
|
||||
commands:
|
||||
- echo "Installing system dependencies..."
|
||||
- apt-get update -qq
|
||||
- apt-get install -y -qq git zip unzip libicu-dev libzip-dev libpng-dev libjpeg-dev libfreetype6-dev libpq-dev
|
||||
- echo "Installing PHP extensions..."
|
||||
- docker-php-ext-configure gd --with-freetype --with-jpeg
|
||||
- docker-php-ext-install -j$(nproc) intl pdo pdo_pgsql zip gd pcntl
|
||||
- echo "Installing Composer..."
|
||||
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer --quiet
|
||||
- echo "Creating minimal .env for package discovery..."
|
||||
- |
|
||||
cat > .env << 'EOF'
|
||||
@@ -59,15 +61,16 @@ steps:
|
||||
- |
|
||||
if [ -d "vendor" ] && [ -f "vendor/autoload.php" ]; then
|
||||
echo "✅ Restored vendor from cache"
|
||||
echo "Verifying cached dependencies are up to date..."
|
||||
composer install --no-interaction --prefer-dist --optimize-autoloader --no-progress
|
||||
else
|
||||
echo "📦 Installing fresh dependencies (cache miss)"
|
||||
composer install --no-interaction --prefer-dist --optimize-autoloader --no-progress
|
||||
fi
|
||||
- echo "Composer dependencies ready!"
|
||||
- echo "✅ Composer dependencies ready!"
|
||||
|
||||
# Rebuild Composer cache
|
||||
# ============================================
|
||||
# STEP 3: Rebuild Composer Cache
|
||||
# ============================================
|
||||
rebuild-composer-cache:
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
@@ -80,27 +83,31 @@ steps:
|
||||
volumes:
|
||||
- /tmp/woodpecker-cache:/tmp/cache
|
||||
|
||||
# PHP Syntax Check (runs after composer install so traits/classes are available)
|
||||
# ============================================
|
||||
# STEP 4: PHP Lint + Code Style (PARALLEL)
|
||||
# ============================================
|
||||
# These run in parallel - both only need vendor/
|
||||
php-lint:
|
||||
image: php:8.3-cli
|
||||
image: code.cannabrands.app/cannabrands/ci-runner:latest
|
||||
group: quality-checks
|
||||
commands:
|
||||
- echo "Checking PHP syntax..."
|
||||
- find app -name "*.php" -exec php -l {} \;
|
||||
- find routes -name "*.php" -exec php -l {} \;
|
||||
- find database -name "*.php" -exec php -l {} \;
|
||||
- echo "PHP syntax check complete!"
|
||||
- find app -name "*.php" -exec php -l {} \; 2>&1 | grep -v "No syntax errors"
|
||||
- find routes -name "*.php" -exec php -l {} \; 2>&1 | grep -v "No syntax errors"
|
||||
- find database -name "*.php" -exec php -l {} \; 2>&1 | grep -v "No syntax errors"
|
||||
- echo "✅ PHP syntax check complete!"
|
||||
|
||||
# Run Laravel Pint (Code Style)
|
||||
code-style:
|
||||
image: php:8.3-cli
|
||||
image: code.cannabrands.app/cannabrands/ci-runner:latest
|
||||
group: quality-checks
|
||||
commands:
|
||||
- echo "Checking code style with Laravel Pint..."
|
||||
- ./vendor/bin/pint --test
|
||||
- echo "Code style check complete!"
|
||||
- echo "✅ Code style check complete!"
|
||||
|
||||
# Run PHPUnit Tests
|
||||
# Note: Uses array cache/session for speed and isolation (Laravel convention)
|
||||
# Redis + Reverb services used for real-time broadcasting tests
|
||||
# ============================================
|
||||
# STEP 5: PHPUnit Tests
|
||||
# ============================================
|
||||
tests:
|
||||
image: kirschbaumdevelopment/laravel-test-runner:8.3
|
||||
environment:
|
||||
@@ -131,11 +138,13 @@ steps:
|
||||
- sleep 2
|
||||
- echo "Running tests..."
|
||||
- php artisan test --parallel
|
||||
- echo "Tests complete!"
|
||||
- echo "✅ Tests complete!"
|
||||
|
||||
# Validate seeders that run in dev/staging environments
|
||||
# This prevents deployment failures caused by seeder errors (e.g., fake() crashes)
|
||||
# Uses APP_ENV=development to match K8s init container behavior
|
||||
# ============================================
|
||||
# STEP 6: Validate Seeders (develop push only)
|
||||
# ============================================
|
||||
# Runs only on direct push to develop to catch seeder issues
|
||||
# before auto-deploy. Skipped on PRs since tests already run migrations.
|
||||
validate-seeders:
|
||||
image: kirschbaumdevelopment/laravel-test-runner:8.3
|
||||
environment:
|
||||
@@ -157,11 +166,12 @@ steps:
|
||||
- php artisan migrate:fresh --seed --force
|
||||
- echo "✅ Seeder validation complete!"
|
||||
when:
|
||||
branch: [develop, master]
|
||||
branch: develop
|
||||
event: push
|
||||
status: success
|
||||
|
||||
# Build and push Docker image for DEV environment (develop branch)
|
||||
# ============================================
|
||||
# STEP 7: Build Docker Image for DEV (develop branch)
|
||||
# ============================================
|
||||
build-image-dev:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
@@ -172,10 +182,10 @@ steps:
|
||||
password:
|
||||
from_secret: gitea_token
|
||||
tags:
|
||||
- dev # Latest dev build → dev.cannabrands.app
|
||||
- dev-${CI_COMMIT_SHA:0:7} # Unique dev tag with SHA
|
||||
- sha-${CI_COMMIT_SHA:0:7} # Commit SHA (industry standard)
|
||||
- ${CI_COMMIT_BRANCH} # Branch name (develop)
|
||||
- dev
|
||||
- dev-${CI_COMMIT_SHA:0:7}
|
||||
- sha-${CI_COMMIT_SHA:0:7}
|
||||
- ${CI_COMMIT_BRANCH}
|
||||
build_args:
|
||||
GIT_COMMIT_SHA: "${CI_COMMIT_SHA:0:7}"
|
||||
APP_VERSION: "dev"
|
||||
@@ -189,9 +199,10 @@ steps:
|
||||
when:
|
||||
branch: develop
|
||||
event: push
|
||||
status: success
|
||||
|
||||
# Auto-deploy to dev.cannabrands.app (develop branch only)
|
||||
# ============================================
|
||||
# STEP 8: Auto-Deploy to dev.cannabrands.app
|
||||
# ============================================
|
||||
deploy-dev:
|
||||
image: bitnami/kubectl:latest
|
||||
environment:
|
||||
@@ -200,36 +211,29 @@ steps:
|
||||
commands:
|
||||
- echo "🚀 Auto-deploying to dev.cannabrands.app..."
|
||||
- echo "Commit SHA${CI_COMMIT_SHA:0:7}"
|
||||
- echo ""
|
||||
# Setup kubeconfig
|
||||
- mkdir -p ~/.kube
|
||||
- echo "$KUBECONFIG_CONTENT" | tr -d '[:space:]' | base64 -d > ~/.kube/config
|
||||
- chmod 600 ~/.kube/config
|
||||
# Update deployment to use new SHA-tagged image (both app and init containers)
|
||||
- |
|
||||
kubectl set image deployment/cannabrands-hub \
|
||||
app=code.cannabrands.app/cannabrands/hub:dev-${CI_COMMIT_SHA:0:7} \
|
||||
migrate=code.cannabrands.app/cannabrands/hub:dev-${CI_COMMIT_SHA:0:7} \
|
||||
-n cannabrands-dev
|
||||
# Wait for rollout to complete (timeout 5 minutes)
|
||||
- kubectl rollout status deployment/cannabrands-hub -n cannabrands-dev --timeout=300s
|
||||
# Verify deployment health
|
||||
- |
|
||||
echo ""
|
||||
echo "✅ Deployment successful!"
|
||||
echo "Pod status:"
|
||||
kubectl get pods -n cannabrands-dev -l app=cannabrands-hub
|
||||
echo ""
|
||||
echo "Image deployed:"
|
||||
kubectl get deployment cannabrands-hub -n cannabrands-dev -o jsonpath='{.spec.template.spec.containers[0].image}'
|
||||
echo ""
|
||||
when:
|
||||
branch: develop
|
||||
event: push
|
||||
status: success
|
||||
|
||||
# Build and push Docker image for STAGING environment (master branch)
|
||||
build-image-staging:
|
||||
# ============================================
|
||||
# STEP 9: Build Docker Image for PRODUCTION (master branch)
|
||||
# ============================================
|
||||
# Skips tests on merge - they already passed on the PR
|
||||
build-image-production:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
registry: code.cannabrands.app
|
||||
@@ -239,21 +243,56 @@ steps:
|
||||
password:
|
||||
from_secret: gitea_token
|
||||
tags:
|
||||
- staging # Latest staging build → staging.cannabrands.app
|
||||
- sha-${CI_COMMIT_SHA:0:7} # Commit SHA (industry standard)
|
||||
- ${CI_COMMIT_BRANCH} # Branch name (master)
|
||||
- latest
|
||||
- prod-${CI_COMMIT_SHA:0:7}
|
||||
- sha-${CI_COMMIT_SHA:0:7}
|
||||
- ${CI_COMMIT_BRANCH}
|
||||
build_args:
|
||||
GIT_COMMIT_SHA: "${CI_COMMIT_SHA:0:7}"
|
||||
APP_VERSION: "staging"
|
||||
APP_VERSION: "production"
|
||||
VITE_REVERB_APP_KEY: "YOUR_PRODUCTION_REVERB_KEY"
|
||||
VITE_REVERB_HOST: "cannabrands.app"
|
||||
VITE_REVERB_PORT: "443"
|
||||
VITE_REVERB_SCHEME: "https"
|
||||
cache_images:
|
||||
- code.cannabrands.app/cannabrands/hub:buildcache-staging
|
||||
- code.cannabrands.app/cannabrands/hub:buildcache-prod
|
||||
platforms: linux/amd64
|
||||
when:
|
||||
branch: master
|
||||
event: push
|
||||
status: success
|
||||
|
||||
# Build and push Docker image for PRODUCTION (tagged releases)
|
||||
# ============================================
|
||||
# STEP 10: Deploy to Production (master branch)
|
||||
# ============================================
|
||||
deploy-production:
|
||||
image: bitnami/kubectl:latest
|
||||
environment:
|
||||
KUBECONFIG_CONTENT:
|
||||
from_secret: kubeconfig_prod
|
||||
commands:
|
||||
- echo "🚀 Deploying to PRODUCTION (cannabrands.app)..."
|
||||
- echo "Commit SHA${CI_COMMIT_SHA:0:7}"
|
||||
- mkdir -p ~/.kube
|
||||
- echo "$KUBECONFIG_CONTENT" | tr -d '[:space:]' | base64 -d > ~/.kube/config
|
||||
- chmod 600 ~/.kube/config
|
||||
- |
|
||||
kubectl set image deployment/cannabrands-hub \
|
||||
app=code.cannabrands.app/cannabrands/hub:prod-${CI_COMMIT_SHA:0:7} \
|
||||
migrate=code.cannabrands.app/cannabrands/hub:prod-${CI_COMMIT_SHA:0:7} \
|
||||
-n cannabrands-prod
|
||||
- kubectl rollout status deployment/cannabrands-hub -n cannabrands-prod --timeout=300s
|
||||
- |
|
||||
echo ""
|
||||
echo "✅ PRODUCTION deployment successful!"
|
||||
echo "Pod status:"
|
||||
kubectl get pods -n cannabrands-prod -l app=cannabrands-hub
|
||||
when:
|
||||
branch: master
|
||||
event: push
|
||||
|
||||
# ============================================
|
||||
# STEP 11: Build Tagged Release (optional versioned releases)
|
||||
# ============================================
|
||||
build-image-release:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
@@ -264,8 +303,8 @@ steps:
|
||||
password:
|
||||
from_secret: gitea_token
|
||||
tags:
|
||||
- ${CI_COMMIT_TAG} # CalVer tag (e.g., 2025.10.1)
|
||||
- latest # Latest stable release
|
||||
- ${CI_COMMIT_TAG}
|
||||
- latest
|
||||
build_args:
|
||||
GIT_COMMIT_SHA: "${CI_COMMIT_SHA:0:7}"
|
||||
APP_VERSION: "${CI_COMMIT_TAG}"
|
||||
@@ -274,89 +313,56 @@ steps:
|
||||
platforms: linux/amd64
|
||||
when:
|
||||
event: tag
|
||||
status: success
|
||||
|
||||
# Success notification
|
||||
# ============================================
|
||||
# Success Notification
|
||||
# ============================================
|
||||
success:
|
||||
image: alpine:latest
|
||||
when:
|
||||
- evaluate: 'CI_PIPELINE_STATUS == "success"'
|
||||
commands:
|
||||
- echo "✅ Pipeline completed successfully!"
|
||||
- echo "All checks passed for commit ${CI_COMMIT_SHA:0:7}"
|
||||
- echo "Commit ${CI_COMMIT_SHA:0:7}"
|
||||
- |
|
||||
if [ "${CI_PIPELINE_EVENT}" = "tag" ]; then
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🎉 PRODUCTION RELEASE BUILD COMPLETE"
|
||||
echo "🎉 TAGGED RELEASE BUILD COMPLETE"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Version: ${CI_COMMIT_TAG}"
|
||||
echo "Registry: code.cannabrands.app/cannabrands/hub"
|
||||
echo ""
|
||||
echo "Available as:"
|
||||
echo " - code.cannabrands.app/cannabrands/hub:${CI_COMMIT_TAG}"
|
||||
echo " - code.cannabrands.app/cannabrands/hub:latest"
|
||||
echo ""
|
||||
echo "🚀 Deploy to PRODUCTION (cannabrands.app):"
|
||||
echo " docker pull code.cannabrands.app/cannabrands/hub:${CI_COMMIT_TAG}"
|
||||
echo " docker-compose -f docker-compose.production.yml up -d"
|
||||
echo ""
|
||||
echo "⚠️ This is a CUSTOMER-FACING release!"
|
||||
echo "Image: code.cannabrands.app/cannabrands/hub:${CI_COMMIT_TAG}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
elif [ "${CI_PIPELINE_EVENT}" = "push" ] && [ "${CI_COMMIT_BRANCH}" = "master" ]; then
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🧪 STAGING BUILD COMPLETE"
|
||||
echo "🚀 PRODUCTION DEPLOYED"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Branch: master"
|
||||
echo "Registry: code.cannabrands.app/cannabrands/hub"
|
||||
echo "Tags:"
|
||||
echo " - staging"
|
||||
echo " - sha-${CI_COMMIT_SHA:0:7}"
|
||||
echo " - ${CI_COMMIT_BRANCH}"
|
||||
echo ""
|
||||
echo "📦 Deploy to STAGING (staging.cannabrands.app):"
|
||||
echo " docker pull code.cannabrands.app/cannabrands/hub:staging"
|
||||
echo " docker-compose -f docker-compose.staging.yml up -d"
|
||||
echo ""
|
||||
echo "👥 Next steps:"
|
||||
echo " 1. Super-admin tests on staging.cannabrands.app"
|
||||
echo " 2. Validate all features work"
|
||||
echo " 3. When ready, create production tag:"
|
||||
echo " git tag -a 2025.10.1 -m 'Release 2025.10.1'"
|
||||
echo " git push origin 2025.10.1"
|
||||
echo "Site: https://cannabrands.app"
|
||||
echo "Image: prod-${CI_COMMIT_SHA:0:7}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
elif [ "${CI_PIPELINE_EVENT}" = "push" ] && [ "${CI_COMMIT_BRANCH}" = "develop" ]; then
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "🚀 DEV BUILD + AUTO-DEPLOY COMPLETE"
|
||||
echo "🧪 DEV DEPLOYED"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Branch: develop"
|
||||
echo "Commit: ${CI_COMMIT_SHA:0:7}"
|
||||
echo "Site: https://dev.cannabrands.app"
|
||||
echo "Image: dev-${CI_COMMIT_SHA:0:7}"
|
||||
echo ""
|
||||
echo "✅ Built & Tagged:"
|
||||
echo " - code.cannabrands.app/cannabrands/hub:dev"
|
||||
echo " - code.cannabrands.app/cannabrands/hub:dev-${CI_COMMIT_SHA:0:7}"
|
||||
echo " - code.cannabrands.app/cannabrands/hub:sha-${CI_COMMIT_SHA:0:7}"
|
||||
echo "Ready for production? Open PR: develop → master"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
elif [ "${CI_PIPELINE_EVENT}" = "pull_request" ]; then
|
||||
echo ""
|
||||
echo "✅ Auto-Deployed to Kubernetes:"
|
||||
echo " - Environment: dev.cannabrands.app"
|
||||
echo " - Namespace: cannabrands-dev"
|
||||
echo " - Image: dev-${CI_COMMIT_SHA:0:7}"
|
||||
echo ""
|
||||
echo "🧪 Test your changes:"
|
||||
echo " - Visit: https://dev.cannabrands.app"
|
||||
echo " - Login: admin@example.com / password"
|
||||
echo " - Check: https://dev.cannabrands.app/telescope"
|
||||
echo ""
|
||||
echo "👥 Next steps:"
|
||||
echo " 1. Verify feature works on dev.cannabrands.app"
|
||||
echo " 2. When stable, merge to master for staging:"
|
||||
echo " git checkout master && git merge develop && git push"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "✅ PR CHECKS PASSED"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Ready to merge to master for production deployment."
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
fi
|
||||
|
||||
# Services for tests
|
||||
# ============================================
|
||||
# Services for Tests
|
||||
# ============================================
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
|
||||
@@ -1,623 +0,0 @@
|
||||
# CI/CD Flow Strategies and Best Practices
|
||||
|
||||
## Current State: Continuous Integration (CI Only)
|
||||
|
||||
**What you have now:**
|
||||
```
|
||||
Developer → Commit → Push to master → Woodpecker runs:
|
||||
1. PHP syntax check
|
||||
2. Composer install (cached)
|
||||
3. Laravel Pint (code style)
|
||||
4. PHPUnit tests
|
||||
→ ✅ Pass = Code is verified
|
||||
→ ❌ Fail = Fix before merging
|
||||
```
|
||||
|
||||
**Current flow**: **Continuous Integration** - automated testing, no deployment
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Strategies: Three Approaches
|
||||
|
||||
### Strategy 1: Continuous Delivery (Safest - Recommended for Cannabis Industry)
|
||||
|
||||
**Flow:**
|
||||
```
|
||||
Developer → Feature branch → Pull Request → CI tests pass
|
||||
→ Manual review & approval → Merge to master → CI tests again
|
||||
→ Build Docker image → Push to Gitea registry
|
||||
→ MANUAL deployment to production (click button/run command)
|
||||
```
|
||||
|
||||
**Characteristics:**
|
||||
- ✅ **Human gate before production** - manual approval required
|
||||
- ✅ **Compliance-friendly** - audit trail of who deployed what
|
||||
- ✅ **Safer for regulated industries** - no automatic production changes
|
||||
- ✅ **Rollback control** - can choose which version to deploy
|
||||
- ⚠️ Requires human intervention for each deployment
|
||||
|
||||
**Best for**: Cannabis industry (regulated), financial services, healthcare
|
||||
|
||||
**Example Woodpecker pipeline:**
|
||||
```yaml
|
||||
steps:
|
||||
# ... tests ...
|
||||
|
||||
# Build image automatically on master
|
||||
build-image:
|
||||
image: plugins/docker
|
||||
settings:
|
||||
registry: code.cannabrands.app
|
||||
repo: code.cannabrands.app/cannabrands/hub
|
||||
tags: [latest, ${CI_COMMIT_SHA:0:8}]
|
||||
when:
|
||||
branch: master
|
||||
event: push
|
||||
|
||||
# Notify team that new version is ready
|
||||
notify-ready:
|
||||
image: alpine
|
||||
commands:
|
||||
- echo "✅ Image ready for deployment"
|
||||
- echo "To deploy: ssh prod-server 'docker pull ... && docker-compose up -d'"
|
||||
when:
|
||||
branch: master
|
||||
```
|
||||
|
||||
**Manual deployment:**
|
||||
```bash
|
||||
# On production server
|
||||
ssh cannabrands-prod
|
||||
docker pull code.cannabrands.app/cannabrands/hub:bef77df8
|
||||
docker-compose up -d
|
||||
# Or use deployment tool like Ansible, Deployer, etc.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Strategy 2: Continuous Deployment (Most Automated - Higher Risk)
|
||||
|
||||
**Flow:**
|
||||
```
|
||||
Developer → Push to master → CI tests pass
|
||||
→ Build Docker image → Push to registry
|
||||
→ AUTOMATIC deployment to production
|
||||
→ Health checks → Rollback if fails
|
||||
```
|
||||
|
||||
**Characteristics:**
|
||||
- ✅ **Fastest delivery** - code in production within minutes
|
||||
- ✅ **High velocity** - deploy 10+ times per day
|
||||
- ⚠️ **Higher risk** - bugs can reach production automatically
|
||||
- ⚠️ **Requires excellent tests** - 80%+ code coverage needed
|
||||
- ⚠️ **May not meet compliance requirements** - no human review
|
||||
|
||||
**Best for**: SaaS startups, internal tools, non-regulated industries
|
||||
|
||||
**Example Woodpecker pipeline:**
|
||||
```yaml
|
||||
steps:
|
||||
# ... tests ...
|
||||
|
||||
build-and-deploy:
|
||||
image: appleboy/drone-ssh
|
||||
settings:
|
||||
host: cannabrands-prod.example.com
|
||||
username: deploy
|
||||
key:
|
||||
from_secret: ssh_private_key
|
||||
script:
|
||||
- cd /var/www/cannabrands
|
||||
- docker pull code.cannabrands.app/cannabrands/hub:${CI_COMMIT_SHA:0:8}
|
||||
- docker-compose up -d
|
||||
- docker exec cannabrands php artisan migrate --force
|
||||
- docker exec cannabrands php artisan config:cache
|
||||
when:
|
||||
branch: master
|
||||
status: success
|
||||
```
|
||||
|
||||
**⚠️ Not recommended for cannabis industry** - regulatory compliance issues
|
||||
|
||||
---
|
||||
|
||||
### Strategy 3: Environment Promotion (Balanced - Recommended)
|
||||
|
||||
**Flow:**
|
||||
```
|
||||
Developer → Feature branch → CI tests
|
||||
→ Merge to develop → Auto-deploy to STAGING
|
||||
→ Manual testing on staging
|
||||
→ Merge to master → Auto-deploy to PRODUCTION
|
||||
```
|
||||
|
||||
**Characteristics:**
|
||||
- ✅ **Staged rollout** - test in staging before production
|
||||
- ✅ **Automated staging** - fast feedback on real environment
|
||||
- ✅ **Manual production gate** - controlled production releases
|
||||
- ✅ **Mirrors production** - catch environment-specific issues
|
||||
|
||||
**Best for**: Most production applications, including cannabis industry
|
||||
|
||||
**Git branching:**
|
||||
```
|
||||
develop (latest features) → auto-deploy to staging.cannabrands.app
|
||||
master (production-ready) → auto-deploy to app.cannabrands.com
|
||||
```
|
||||
|
||||
**Example Woodpecker pipeline:**
|
||||
```yaml
|
||||
steps:
|
||||
# ... tests ...
|
||||
|
||||
# Deploy to staging automatically
|
||||
deploy-staging:
|
||||
image: appleboy/drone-ssh
|
||||
settings:
|
||||
host: staging.cannabrands.app
|
||||
username: deploy
|
||||
key:
|
||||
from_secret: ssh_private_key
|
||||
script:
|
||||
- cd /var/www/cannabrands
|
||||
- docker pull code.cannabrands.app/cannabrands/hub:${CI_COMMIT_SHA:0:8}
|
||||
- docker-compose up -d
|
||||
when:
|
||||
branch: develop
|
||||
event: push
|
||||
|
||||
# Deploy to production automatically (only from master)
|
||||
deploy-production:
|
||||
image: appleboy/drone-ssh
|
||||
settings:
|
||||
host: app.cannabrands.com
|
||||
username: deploy
|
||||
key:
|
||||
from_secret: ssh_private_key
|
||||
script:
|
||||
- cd /var/www/cannabrands
|
||||
- docker pull code.cannabrands.app/cannabrands/hub:${CI_COMMIT_SHA:0:8}
|
||||
- docker-compose up -d
|
||||
when:
|
||||
branch: master
|
||||
event: push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recommended Strategy for Cannabrands
|
||||
|
||||
### Phase 1: Current (Continuous Integration)
|
||||
**Status**: ✅ Implemented
|
||||
|
||||
- Tests run on every push to master
|
||||
- No automatic deployment
|
||||
- Manual deployment when ready
|
||||
|
||||
### Phase 2: Continuous Delivery (Next 1-3 months)
|
||||
**Recommended approach:**
|
||||
|
||||
1. **Keep CI testing** (already have this)
|
||||
2. **Add image building** on master branch
|
||||
3. **Manual deployment** to production
|
||||
4. **Deployment checklist** for compliance
|
||||
|
||||
**Why this approach:**
|
||||
- Cannabis industry is regulated - need audit trails
|
||||
- Manual gate ensures compliance review
|
||||
- Still fast (deploy in 5 minutes when ready)
|
||||
- Safer for financial/inventory systems
|
||||
|
||||
### Phase 3: Environment Promotion (Future - when team grows)
|
||||
**When to implement:**
|
||||
|
||||
- When you have 3+ developers
|
||||
- When you add a QA team member
|
||||
- When compliance requires staging environment
|
||||
- When you need faster iteration
|
||||
|
||||
---
|
||||
|
||||
## Git Branching Strategies
|
||||
|
||||
### Option 1: GitHub Flow (Simple - Current)
|
||||
```
|
||||
master (production)
|
||||
↑
|
||||
feature branches → PR → merge to master
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Simple to understand
|
||||
- Works well for small teams (1-3 people)
|
||||
- Fast iteration
|
||||
|
||||
**Cons:**
|
||||
- No staging environment
|
||||
- Every merge to master should be production-ready
|
||||
|
||||
**Current fit**: ✅ Perfect for now
|
||||
|
||||
---
|
||||
|
||||
### Option 2: Git Flow (Comprehensive)
|
||||
```
|
||||
master (production) ← hotfixes
|
||||
↑
|
||||
release branches (release prep)
|
||||
↑
|
||||
develop (integration) ← feature branches
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Clear separation of environments
|
||||
- Supports multiple versions
|
||||
- Good for larger teams
|
||||
|
||||
**Cons:**
|
||||
- More complex
|
||||
- Overkill for small teams
|
||||
- Slower iteration
|
||||
|
||||
**Future fit**: Consider when team grows to 5+ developers
|
||||
|
||||
---
|
||||
|
||||
### Option 3: Trunk-Based Development (Modern)
|
||||
```
|
||||
master (trunk) ← short-lived feature branches
|
||||
↓
|
||||
Continuous deployment with feature flags
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Fastest iteration
|
||||
- Simple branching model
|
||||
- Forces small, frequent commits
|
||||
|
||||
**Cons:**
|
||||
- Requires feature flags
|
||||
- Requires excellent test coverage
|
||||
- Not ideal for regulated industries
|
||||
|
||||
**Fit**: ❌ Not recommended for cannabis compliance
|
||||
|
||||
---
|
||||
|
||||
## Best Practices for Cannabrands CI/CD
|
||||
|
||||
### 1. Testing Requirements
|
||||
|
||||
**Minimum coverage:**
|
||||
- ✅ Unit tests for critical business logic (invoice calculations, inventory)
|
||||
- ✅ Feature tests for compliance workflows (age verification, state restrictions)
|
||||
- ✅ Browser tests for checkout flow (using Laravel Dusk)
|
||||
|
||||
**Current state**: You have tests, need to ensure coverage of:
|
||||
- Invoice generation (especially with picked quantities)
|
||||
- Payment term calculations
|
||||
- Order state transitions
|
||||
- Manifest generation (cannabis compliance)
|
||||
|
||||
### 2. Deployment Checklist
|
||||
|
||||
Create `.woodpecker/DEPLOYMENT_CHECKLIST.md`:
|
||||
```markdown
|
||||
# Pre-Deployment Checklist
|
||||
|
||||
Before deploying to production:
|
||||
|
||||
## Automated (CI checks these)
|
||||
- [ ] All tests pass
|
||||
- [ ] Code style passes (Pint)
|
||||
- [ ] No PHP syntax errors
|
||||
|
||||
## Manual (Human checks these)
|
||||
- [ ] Database migrations reviewed (no data loss)
|
||||
- [ ] Environment variables updated (.env)
|
||||
- [ ] Compliance implications reviewed
|
||||
- [ ] Rollback plan documented
|
||||
- [ ] Customer-facing changes tested in staging
|
||||
|
||||
## Post-Deployment
|
||||
- [ ] Health check passes (app responding)
|
||||
- [ ] Key workflows tested (place order, generate invoice)
|
||||
- [ ] Error monitoring checked (last 5 minutes)
|
||||
- [ ] Database migrations completed successfully
|
||||
```
|
||||
|
||||
### 3. Environment Configuration
|
||||
|
||||
**Three environments:**
|
||||
```
|
||||
Local Development:
|
||||
- Docker Compose (Sail)
|
||||
- .env with local credentials
|
||||
- Seeded test data
|
||||
|
||||
Staging:
|
||||
- Mirrors production
|
||||
- staging.cannabrands.app
|
||||
- Real-like data (anonymized)
|
||||
- Auto-deployed from develop branch
|
||||
|
||||
Production:
|
||||
- app.cannabrands.com
|
||||
- Real customer data
|
||||
- Manual deployment (compliance gate)
|
||||
- Backups every 4 hours
|
||||
```
|
||||
|
||||
### 4. Deployment Windows
|
||||
|
||||
**Cannabis industry considerations:**
|
||||
- **No deployments during business hours** (8am-6pm PST) - peak order time
|
||||
- **Preferred windows**:
|
||||
- Tuesday/Wednesday 10pm-midnight (lowest traffic)
|
||||
- Sunday 8pm-10pm (acceptable)
|
||||
- **Avoid**:
|
||||
- Mondays (busy)
|
||||
- Fridays (risky - weekend support issues)
|
||||
- Last day of month (invoicing period)
|
||||
|
||||
### 5. Rollback Strategy
|
||||
|
||||
**Always have a rollback plan:**
|
||||
|
||||
```bash
|
||||
# Quick rollback (under 2 minutes)
|
||||
ssh cannabrands-prod
|
||||
docker pull code.cannabrands.app/cannabrands/hub:PREVIOUS_COMMIT_SHA
|
||||
docker-compose up -d
|
||||
|
||||
# Database rollback (if migrations ran)
|
||||
docker exec cannabrands php artisan migrate:rollback --step=1
|
||||
```
|
||||
|
||||
**Tag stable releases:**
|
||||
```bash
|
||||
# Before risky deployments
|
||||
git tag -a v1.5.2-stable -m "Last known good version"
|
||||
git push origin v1.5.2-stable
|
||||
```
|
||||
|
||||
### 6. Secrets Management
|
||||
|
||||
**Never commit:**
|
||||
- API keys (Metrc, payment processors)
|
||||
- Database credentials
|
||||
- OAuth secrets
|
||||
|
||||
**Use Woodpecker secrets for:**
|
||||
- Gitea registry credentials
|
||||
- SSH deployment keys
|
||||
- Notification webhooks
|
||||
|
||||
**Use environment variables for:**
|
||||
- Feature flags
|
||||
- Third-party API endpoints
|
||||
- Email/SMS providers
|
||||
|
||||
### 7. Monitoring & Alerts
|
||||
|
||||
**Must-have monitoring:**
|
||||
- **Application errors**: Laravel Telescope (dev), Sentry (production)
|
||||
- **Server health**: Uptime monitoring (every 5 minutes)
|
||||
- **Database**: Connection pool, slow queries
|
||||
- **Key workflows**: "Can users place orders?" synthetic test
|
||||
|
||||
**Alert on:**
|
||||
- ❌ Application throws 500 errors
|
||||
- ❌ Database connection fails
|
||||
- ❌ Manifest generation fails (compliance risk!)
|
||||
- ❌ Payment processing fails
|
||||
|
||||
### 8. Compliance Considerations
|
||||
|
||||
**Cannabis-specific requirements:**
|
||||
- **Audit logging**: Who deployed what, when
|
||||
- **Data retention**: Keep deployment logs for 7 years (some states require this)
|
||||
- **Rollback capability**: Must be able to restore system to previous state
|
||||
- **Change approval**: Document who approved production changes
|
||||
|
||||
**Implement:**
|
||||
```yaml
|
||||
# Log all deployments to compliance audit trail
|
||||
steps:
|
||||
deploy-production:
|
||||
# ... deployment steps ...
|
||||
|
||||
audit-log:
|
||||
image: alpine
|
||||
commands:
|
||||
- |
|
||||
echo "DEPLOYMENT AUDIT LOG" > /tmp/audit.log
|
||||
echo "Timestamp: $(date -Iseconds)" >> /tmp/audit.log
|
||||
echo "Commit: ${CI_COMMIT_SHA}" >> /tmp/audit.log
|
||||
echo "Author: ${CI_COMMIT_AUTHOR}" >> /tmp/audit.log
|
||||
echo "Branch: ${CI_COMMIT_BRANCH}" >> /tmp/audit.log
|
||||
# Send to compliance logging system
|
||||
curl -X POST https://logs.cannabrands.com/deployments \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @/tmp/audit.log
|
||||
when:
|
||||
branch: master
|
||||
status: success
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recommended Next Steps
|
||||
|
||||
### Immediate (This month)
|
||||
1. ✅ **Keep current CI setup** - tests on every push
|
||||
2. ⬜ **Add image building** - build Docker images on master
|
||||
3. ⬜ **Document deployment process** - create runbook
|
||||
4. ⬜ **Set up monitoring** - add Sentry or similar
|
||||
|
||||
### Short-term (1-3 months)
|
||||
1. ⬜ **Add staging environment** - test before production
|
||||
2. ⬜ **Implement deployment checklist** - compliance gate
|
||||
3. ⬜ **Add deployment logging** - audit trail
|
||||
4. ⬜ **Create rollback runbook** - quick recovery
|
||||
|
||||
### Long-term (6+ months)
|
||||
1. ⬜ **Add automated staging deployment** - from develop branch
|
||||
2. ⬜ **Implement feature flags** - safer rollouts
|
||||
3. ⬜ **Add smoke tests** - post-deployment verification
|
||||
4. ⬜ **Consider blue-green deployments** - zero-downtime
|
||||
|
||||
---
|
||||
|
||||
## Example: Full Recommended Pipeline
|
||||
|
||||
```yaml
|
||||
# .woodpecker/.ci.yml - Recommended for Cannabrands
|
||||
when:
|
||||
branch: [master, develop]
|
||||
event: [push, pull_request]
|
||||
|
||||
steps:
|
||||
# TEST PHASE (all branches)
|
||||
restore-cache:
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
backend: filesystem
|
||||
restore: true
|
||||
cache_key: "composer-{{ checksum \"composer.lock\" }}"
|
||||
mount: ["vendor"]
|
||||
volumes:
|
||||
- /tmp/woodpecker-cache:/tmp/cache
|
||||
|
||||
composer-install:
|
||||
image: php:8.3-cli
|
||||
commands:
|
||||
- apt-get update -qq && apt-get install -y -qq git zip unzip libicu-dev libzip-dev libpq-dev
|
||||
- docker-php-ext-install -j$(nproc) intl pdo pdo_pgsql zip
|
||||
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
||||
- composer install --no-interaction --prefer-dist --optimize-autoloader
|
||||
|
||||
php-lint:
|
||||
image: php:8.3-cli
|
||||
commands:
|
||||
- find app routes database -name "*.php" -exec php -l {} \;
|
||||
|
||||
code-style:
|
||||
image: php:8.3-cli
|
||||
commands:
|
||||
- ./vendor/bin/pint --test
|
||||
|
||||
tests:
|
||||
image: kirschbaumdevelopment/laravel-test-runner:8.3
|
||||
environment:
|
||||
APP_ENV: testing
|
||||
DB_CONNECTION: pgsql
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_DATABASE: testing
|
||||
DB_USERNAME: testing
|
||||
DB_PASSWORD: testing
|
||||
commands:
|
||||
- cp .env.example .env
|
||||
- php artisan key:generate
|
||||
- php artisan test --parallel
|
||||
|
||||
rebuild-cache:
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
backend: filesystem
|
||||
rebuild: true
|
||||
cache_key: "composer-{{ checksum \"composer.lock\" }}"
|
||||
mount: ["vendor"]
|
||||
volumes:
|
||||
- /tmp/woodpecker-cache:/tmp/cache
|
||||
|
||||
# BUILD PHASE (master and develop only)
|
||||
build-image:
|
||||
image: plugins/docker
|
||||
settings:
|
||||
registry: code.cannabrands.app
|
||||
repo: code.cannabrands.app/cannabrands/hub
|
||||
tags:
|
||||
- ${CI_COMMIT_BRANCH}
|
||||
- ${CI_COMMIT_SHA:0:8}
|
||||
username:
|
||||
from_secret: gitea_username
|
||||
password:
|
||||
from_secret: gitea_token
|
||||
when:
|
||||
branch: [master, develop]
|
||||
event: push
|
||||
|
||||
# DEPLOY TO STAGING (develop branch only)
|
||||
deploy-staging:
|
||||
image: appleboy/drone-ssh
|
||||
settings:
|
||||
host: staging.cannabrands.app
|
||||
username: deploy
|
||||
key:
|
||||
from_secret: staging_ssh_key
|
||||
script:
|
||||
- cd /var/www/cannabrands
|
||||
- docker pull code.cannabrands.app/cannabrands/hub:${CI_COMMIT_SHA:0:8}
|
||||
- docker-compose up -d
|
||||
- docker exec cannabrands php artisan migrate --force
|
||||
- docker exec cannabrands php artisan config:cache
|
||||
when:
|
||||
branch: develop
|
||||
event: push
|
||||
status: success
|
||||
|
||||
# NOTIFY FOR PRODUCTION (master branch - manual deploy required)
|
||||
notify-production-ready:
|
||||
image: alpine
|
||||
commands:
|
||||
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
- echo "✅ PRODUCTION IMAGE READY"
|
||||
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
- echo "Commit: ${CI_COMMIT_SHA:0:8}"
|
||||
- echo "Author: ${CI_COMMIT_AUTHOR}"
|
||||
- echo "Message: ${CI_COMMIT_MESSAGE}"
|
||||
- echo ""
|
||||
- echo "To deploy to production:"
|
||||
- echo " ssh cannabrands-prod"
|
||||
- echo " cd /var/www/cannabrands"
|
||||
- echo " docker pull code.cannabrands.app/cannabrands/hub:${CI_COMMIT_SHA:0:8}"
|
||||
- echo " docker-compose up -d"
|
||||
- echo ""
|
||||
- echo "⚠️ Remember: Check deployment checklist first!"
|
||||
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
when:
|
||||
branch: master
|
||||
event: push
|
||||
status: success
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
environment:
|
||||
POSTGRES_USER: testing
|
||||
POSTGRES_PASSWORD: testing
|
||||
POSTGRES_DB: testing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**For Cannabrands, I recommend:**
|
||||
|
||||
1. **Current (now)**: Continuous Integration with manual deployment
|
||||
2. **Next phase**: Continuous Delivery with staging environment
|
||||
3. **Git strategy**: GitHub Flow (simple, works for small teams)
|
||||
4. **Deployment**: Manual to production (compliance requirement)
|
||||
5. **Monitoring**: Add error tracking and uptime monitoring
|
||||
|
||||
**Why this approach:**
|
||||
- ✅ Meets cannabis industry compliance needs
|
||||
- ✅ Balances speed with safety
|
||||
- ✅ Scales as team grows
|
||||
- ✅ Maintains audit trails
|
||||
- ✅ Allows quick rollbacks
|
||||
|
||||
**Key principle**: *"Automate testing, control deployment"*
|
||||
131
.woodpecker/GITEA_BRANCH_PROTECTION.md
Normal file
131
.woodpecker/GITEA_BRANCH_PROTECTION.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# Gitea Branch Protection Configuration
|
||||
|
||||
## Recommended Settings for 2-Person Team
|
||||
|
||||
### `develop` Branch — **UNPROTECTED**
|
||||
|
||||
Direct push allowed for fast iteration. CI runs on every push.
|
||||
|
||||
**Settings in Gitea:**
|
||||
1. Go to Repository → Settings → Branches
|
||||
2. Find or add `develop` branch rule
|
||||
3. **Disable all protection** or delete the rule entirely
|
||||
|
||||
If you want minimal protection, use these settings:
|
||||
- ☐ Enable Branch Protection (unchecked)
|
||||
- ☐ Disable Push (unchecked)
|
||||
- ☐ Require Pull Request (unchecked)
|
||||
|
||||
---
|
||||
|
||||
### `master` Branch — **PROTECTED** (Production)
|
||||
|
||||
Requires PR from develop. CI must pass before merge.
|
||||
|
||||
**Settings in Gitea:**
|
||||
1. Go to Repository → Settings → Branches
|
||||
2. Add branch protection rule for `master`
|
||||
3. Configure:
|
||||
|
||||
```
|
||||
☑ Enable Branch Protection
|
||||
☐ Disable Push # Allow merge commits
|
||||
☑ Require Pull Request
|
||||
☐ Require Approvals: 0 # Optional for 2-person team
|
||||
☑ Block Merge on Rejected Reviews # Optional
|
||||
☑ Block Merge on Outdated Branch # Recommended
|
||||
☑ Status Check Patterns:
|
||||
- continuous-integration/woodpecker
|
||||
```
|
||||
|
||||
**Screenshot Guide:**
|
||||
|
||||
```
|
||||
Branch Protection for: master
|
||||
├── [x] Enable Branch Protection
|
||||
├── [ ] Disable Push
|
||||
├── [x] Require Pull Request
|
||||
│ └── Require Approvals: 0 (or 1 if you want peer review)
|
||||
├── [x] Block Merge on Outdated Branch
|
||||
├── [x] Block Merge on Official Review Requests
|
||||
└── [x] Status Check Patterns
|
||||
└── continuous-integration/woodpecker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step-by-Step Instructions
|
||||
|
||||
### 1. Navigate to Branch Protection
|
||||
|
||||
```
|
||||
https://code.cannabrands.app/cannabrands/hub/settings/branches
|
||||
```
|
||||
|
||||
### 2. Remove Protection from `develop`
|
||||
|
||||
- Click on `develop` rule (if exists)
|
||||
- Click "Delete Rule" or uncheck "Enable Branch Protection"
|
||||
- Save
|
||||
|
||||
### 3. Add/Update Protection for `master`
|
||||
|
||||
- Click "Add New Rule" or edit existing `master` rule
|
||||
- Branch name pattern: `master`
|
||||
- Enable settings as shown above
|
||||
- Save
|
||||
|
||||
---
|
||||
|
||||
## Workflow After Changes
|
||||
|
||||
| Action | Before | After |
|
||||
|--------|--------|-------|
|
||||
| Push to develop | ❌ Blocked (PR required) | ✅ Direct push allowed |
|
||||
| Merge develop → master | Via PR | Via PR (unchanged) |
|
||||
| CI on develop push | Runs tests | Runs tests + builds + deploys |
|
||||
| CI on master merge | Runs everything | Builds + deploys (tests already passed on PR) |
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
After making these changes, verify:
|
||||
|
||||
1. **Test develop push:**
|
||||
```bash
|
||||
git checkout develop
|
||||
echo "# test" >> README.md
|
||||
git commit -am "test: verify develop push"
|
||||
git push origin develop
|
||||
# Should succeed without PR
|
||||
```
|
||||
|
||||
2. **Test master protection:**
|
||||
```bash
|
||||
git checkout master
|
||||
echo "# test" >> README.md
|
||||
git commit -am "test: verify master protection"
|
||||
git push origin master
|
||||
# Should FAIL with "protected branch" error
|
||||
```
|
||||
|
||||
3. **Test PR workflow:**
|
||||
- Open PR: develop → master
|
||||
- Wait for CI to pass
|
||||
- Merge should be allowed
|
||||
|
||||
---
|
||||
|
||||
## Rollback (If Needed)
|
||||
|
||||
To re-protect `develop`:
|
||||
|
||||
1. Go to Repository → Settings → Branches
|
||||
2. Add rule for `develop`
|
||||
3. Enable "Require Pull Request"
|
||||
4. Save
|
||||
|
||||
---
|
||||
|
||||
**Note:** These settings optimize for a 2-person team using Claude Code with automated tests. As your team grows, consider adding approval requirements.
|
||||
@@ -1,602 +0,0 @@
|
||||
# Pre-Release Deployment Strategy
|
||||
|
||||
## Your Current Situation
|
||||
|
||||
**Reality Check:**
|
||||
- ✅ App is functional but incomplete
|
||||
- ✅ No external customers yet
|
||||
- ✅ Need team testing ASAP
|
||||
- ✅ Master branch = active development
|
||||
- ✅ Budget/time conscious
|
||||
|
||||
**This is normal for early-stage startups!** Most companies go through this phase.
|
||||
|
||||
---
|
||||
|
||||
## The Pre-Release Paradox
|
||||
|
||||
**The Challenge:**
|
||||
- You need colleagues to test the app
|
||||
- The app isn't production-ready
|
||||
- Traditional CI/CD assumes "production" means customers
|
||||
- But you don't have customers yet!
|
||||
|
||||
**The Solution:**
|
||||
Treat your **pre-release testing environment** as if it were production, even though it's not.
|
||||
|
||||
---
|
||||
|
||||
## Recommended Pre-Release Strategy
|
||||
|
||||
### Phase 0: Pre-Release (You Are Here)
|
||||
|
||||
**Environment Setup:**
|
||||
```
|
||||
master branch → CI tests pass → Build image → Deploy to dev.cannabrands.app
|
||||
↓
|
||||
Team tests here (internal only)
|
||||
```
|
||||
|
||||
**Characteristics:**
|
||||
- ✅ Fast iteration - deploy on every master push
|
||||
- ✅ No customer impact - internal testing only
|
||||
- ✅ "Production-like" environment for realistic testing
|
||||
- ✅ Can be unstable - teammates understand this
|
||||
- ⚠️ Data may be reset periodically
|
||||
|
||||
**Who uses it:**
|
||||
- Internal developers
|
||||
- Co-founders
|
||||
- Early advisors/consultants
|
||||
- Pre-launch beta testers (with NDAs)
|
||||
|
||||
---
|
||||
|
||||
## Setting Up dev.cannabrands.app
|
||||
|
||||
### Step 1: Server Preparation
|
||||
|
||||
**Server Requirements:**
|
||||
- Ubuntu 22.04+ or similar
|
||||
- Docker + Docker Compose installed
|
||||
- Domain pointed to server (dev.cannabrands.app)
|
||||
- SSL certificate (Let's Encrypt)
|
||||
|
||||
**Create deployment user:**
|
||||
```bash
|
||||
# On dev.cannabrands.app server
|
||||
sudo adduser deployer
|
||||
sudo usermod -aG docker deployer
|
||||
sudo mkdir -p /var/www/cannabrands
|
||||
sudo chown deployer:deployer /var/www/cannabrands
|
||||
```
|
||||
|
||||
**Generate SSH key for deployment:**
|
||||
```bash
|
||||
# On your local machine or CI server
|
||||
ssh-keygen -t ed25519 -C "woodpecker-deploy" -f ~/.ssh/cannabrands_deploy
|
||||
|
||||
# Add public key to server
|
||||
ssh deployer@dev.cannabrands.app
|
||||
mkdir -p ~/.ssh
|
||||
nano ~/.ssh/authorized_keys
|
||||
# Paste public key, save
|
||||
```
|
||||
|
||||
**Add SSH key to Woodpecker:**
|
||||
```bash
|
||||
# In Woodpecker UI:
|
||||
# Settings → Secrets → Add Secret
|
||||
# Name: dev_ssh_key
|
||||
# Value: [paste PRIVATE key contents]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Create Deployment Docker Compose
|
||||
|
||||
**On dev.cannabrands.app server:**
|
||||
```bash
|
||||
cd /var/www/cannabrands
|
||||
nano docker-compose.yml
|
||||
```
|
||||
|
||||
**Simple production-like docker-compose.yml:**
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
app:
|
||||
image: code.cannabrands.app/cannabrands/hub:latest
|
||||
container_name: cannabrands_app
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8000:8000"
|
||||
environment:
|
||||
- APP_ENV=development
|
||||
- APP_DEBUG=true
|
||||
- APP_URL=https://dev.cannabrands.app
|
||||
- DB_HOST=postgres
|
||||
- DB_PORT=5432
|
||||
- DB_DATABASE=cannabrands
|
||||
- DB_USERNAME=cannabrands
|
||||
- DB_PASSWORD=${DB_PASSWORD}
|
||||
volumes:
|
||||
- ./storage:/var/www/html/storage
|
||||
- ./.env:/var/www/html/.env
|
||||
depends_on:
|
||||
- postgres
|
||||
command: php artisan serve --host=0.0.0.0 --port=8000
|
||||
|
||||
postgres:
|
||||
image: postgres:15
|
||||
container_name: cannabrands_db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: cannabrands
|
||||
POSTGRES_USER: cannabrands
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
container_name: cannabrands_nginx
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./ssl:/etc/nginx/ssl:ro
|
||||
depends_on:
|
||||
- app
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
```
|
||||
|
||||
**Create .env file:**
|
||||
```bash
|
||||
nano .env
|
||||
```
|
||||
|
||||
```bash
|
||||
APP_NAME="Cannabrands Dev"
|
||||
APP_ENV=development
|
||||
APP_KEY=base64:YOUR_KEY_HERE
|
||||
APP_DEBUG=true
|
||||
APP_URL=https://dev.cannabrands.app
|
||||
|
||||
DB_CONNECTION=pgsql
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
DB_DATABASE=cannabrands
|
||||
DB_USERNAME=cannabrands
|
||||
DB_PASSWORD=SECURE_PASSWORD_HERE
|
||||
|
||||
# Use your actual credentials
|
||||
MAIL_MAILER=log
|
||||
CACHE_DRIVER=redis
|
||||
SESSION_DRIVER=redis
|
||||
QUEUE_CONNECTION=redis
|
||||
|
||||
REDIS_HOST=redis
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Update Woodpecker Pipeline
|
||||
|
||||
**Add deployment steps to `.woodpecker/.ci.yml`:**
|
||||
|
||||
```yaml
|
||||
# Add after existing test steps
|
||||
|
||||
steps:
|
||||
# ... existing steps (tests, etc.) ...
|
||||
|
||||
# BUILD DOCKER IMAGE (only on master)
|
||||
build-image:
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
registry: code.cannabrands.app
|
||||
repo: code.cannabrands.app/cannabrands/hub
|
||||
username:
|
||||
from_secret: gitea_username
|
||||
password:
|
||||
from_secret: gitea_token
|
||||
tags:
|
||||
- latest
|
||||
- dev-${CI_COMMIT_SHA:0:8}
|
||||
when:
|
||||
branch: master
|
||||
event: push
|
||||
status: success
|
||||
|
||||
# DEPLOY TO DEV ENVIRONMENT
|
||||
deploy-dev:
|
||||
image: appleboy/drone-ssh
|
||||
settings:
|
||||
host: dev.cannabrands.app
|
||||
username: deployer
|
||||
key:
|
||||
from_secret: dev_ssh_key
|
||||
script:
|
||||
- echo "🚀 Deploying to dev.cannabrands.app..."
|
||||
- cd /var/www/cannabrands
|
||||
- docker compose pull app
|
||||
- docker compose up -d app
|
||||
- echo "⏳ Waiting for app to start..."
|
||||
- sleep 5
|
||||
- docker compose exec -T app php artisan migrate --force
|
||||
- docker compose exec -T app php artisan config:cache
|
||||
- docker compose exec -T app php artisan route:cache
|
||||
- docker compose exec -T app php artisan view:cache
|
||||
- echo "✅ Deployment complete!"
|
||||
- echo "🌐 Visit https://dev.cannabrands.app"
|
||||
when:
|
||||
branch: master
|
||||
event: push
|
||||
status: success
|
||||
|
||||
# NOTIFY TEAM (optional)
|
||||
notify-deployed:
|
||||
image: curlimages/curl
|
||||
commands:
|
||||
- |
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "✅ DEV ENVIRONMENT UPDATED"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "URL: https://dev.cannabrands.app"
|
||||
echo "Commit: ${CI_COMMIT_SHA:0:8}"
|
||||
echo "Author: ${CI_COMMIT_AUTHOR}"
|
||||
echo "Message: ${CI_COMMIT_MESSAGE}"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
when:
|
||||
branch: master
|
||||
event: push
|
||||
status: success
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Create Dockerfile (if not already present)
|
||||
|
||||
**Create `Dockerfile` in project root:**
|
||||
|
||||
```dockerfile
|
||||
FROM php:8.3-fpm
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
git \
|
||||
curl \
|
||||
libpng-dev \
|
||||
libonig-dev \
|
||||
libxml2-dev \
|
||||
libpq-dev \
|
||||
zip \
|
||||
unzip \
|
||||
nodejs \
|
||||
npm
|
||||
|
||||
# Install PHP extensions
|
||||
RUN docker-php-ext-install pdo pdo_pgsql mbstring exif pcntl bcmath gd
|
||||
|
||||
# Install Composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /var/www/html
|
||||
|
||||
# Copy application files
|
||||
COPY . .
|
||||
|
||||
# Install PHP dependencies
|
||||
RUN composer install --no-dev --optimize-autoloader --no-interaction
|
||||
|
||||
# Install and build frontend assets
|
||||
RUN npm ci && npm run build
|
||||
|
||||
# Set permissions
|
||||
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Git Workflow for Pre-Release Phase
|
||||
|
||||
### Current Approach: Single Branch (Acceptable Now)
|
||||
|
||||
```
|
||||
feature work → commit directly to master → auto-deploy to dev
|
||||
```
|
||||
|
||||
**When this works:**
|
||||
- Solo developer or 2-person team
|
||||
- Pre-launch phase
|
||||
- Fast iteration needed
|
||||
- No customers affected
|
||||
|
||||
**When to stop:**
|
||||
- Once you have your first paying customer
|
||||
- When team grows to 3+ developers
|
||||
- When you need more stability
|
||||
|
||||
---
|
||||
|
||||
### Transitioning to Feature Branches (Recommended Soon)
|
||||
|
||||
**When to implement:** Before first customer OR when 3+ developers
|
||||
|
||||
```
|
||||
feature/new-thing → PR → master (after review) → auto-deploy to dev
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Code review before merging
|
||||
- Catch issues before deployment
|
||||
- Better collaboration
|
||||
- Cleaner history
|
||||
|
||||
**How to transition:**
|
||||
```bash
|
||||
# Instead of committing to master:
|
||||
git checkout -b feature/add-payment-terms
|
||||
# ... make changes ...
|
||||
git add .
|
||||
git commit -m "feat: add payment term surcharge logic"
|
||||
git push origin feature/add-payment-terms
|
||||
|
||||
# Create PR in Gitea
|
||||
# After approval, merge to master
|
||||
# Master auto-deploys to dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Team Testing Guide
|
||||
|
||||
**Create `TESTING.md` in project root:**
|
||||
|
||||
```markdown
|
||||
# Testing the Cannabrands App
|
||||
|
||||
## Dev Environment
|
||||
|
||||
**URL:** https://dev.cannabrands.app
|
||||
|
||||
**Status:** 🟡 Active Development
|
||||
- Updates automatically on every master push
|
||||
- Expect bugs and incomplete features
|
||||
- Data may be reset without notice
|
||||
|
||||
## Test Accounts
|
||||
|
||||
### Buyer (Dispensary)
|
||||
- Email: `dispensary@example.com`
|
||||
- Password: `password`
|
||||
- Use this to test: browsing marketplace, placing orders, viewing invoices
|
||||
|
||||
### Seller (Brand)
|
||||
- Email: `brand@example.com`
|
||||
- Password: `password`
|
||||
- Use this to test: managing products, viewing orders, creating manifests
|
||||
|
||||
### Admin
|
||||
- Email: `admin@example.com`
|
||||
- Password: `password`
|
||||
- Use this to test: user approval, platform management
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
When you find a bug:
|
||||
|
||||
1. **Check if it's already reported** - Look at existing GitHub issues
|
||||
2. **Reproduce the issue** - Try to make it happen again
|
||||
3. **Document steps** - Write down exactly what you did
|
||||
4. **Take screenshots** - Visual proof helps
|
||||
5. **Create issue** - Use template below
|
||||
|
||||
### Issue Template
|
||||
|
||||
```
|
||||
**Title:** [Brief description of bug]
|
||||
|
||||
**Steps to Reproduce:**
|
||||
1. Go to '...'
|
||||
2. Click on '...'
|
||||
3. See error
|
||||
|
||||
**Expected Behavior:**
|
||||
What should have happened
|
||||
|
||||
**Actual Behavior:**
|
||||
What actually happened
|
||||
|
||||
**Screenshots:**
|
||||
[Attach screenshots]
|
||||
|
||||
**Environment:**
|
||||
- Browser: Chrome/Firefox/Safari
|
||||
- Device: Desktop/Mobile
|
||||
```
|
||||
|
||||
## Known Issues
|
||||
|
||||
- [ ] Invoice generation may be slow
|
||||
- [ ] Mobile layout needs work
|
||||
- [ ] Email notifications not yet implemented
|
||||
|
||||
## What to Focus On
|
||||
|
||||
**High Priority Testing:**
|
||||
1. ✅ Can buyers place orders?
|
||||
2. ✅ Can sellers view and approve orders?
|
||||
3. ✅ Do invoices calculate correctly?
|
||||
4. ✅ Can manifests be generated?
|
||||
|
||||
**Nice to Test:**
|
||||
- User registration flow
|
||||
- Password reset
|
||||
- Profile updates
|
||||
- Product filtering/search
|
||||
|
||||
## Support
|
||||
|
||||
Questions? Ask in #development Slack channel or email dev@cannabrands.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Transitioning to Production
|
||||
|
||||
### When to Move Beyond Pre-Release
|
||||
|
||||
**Indicators you're ready:**
|
||||
- ✅ First paying customer signed
|
||||
- ✅ Core workflows are stable
|
||||
- ✅ Team has grown to 3+ people
|
||||
- ✅ Need for staged deployments
|
||||
|
||||
**What changes:**
|
||||
1. **Rename environments:**
|
||||
- `dev.cannabrands.app` → stays as dev (unstable)
|
||||
- Add `staging.cannabrands.app` → stable pre-release testing
|
||||
- Add `app.cannabrands.com` → production (customers)
|
||||
|
||||
2. **Update git workflow:**
|
||||
- Feature branches → dev (auto-deploy)
|
||||
- Master → staging (auto-deploy)
|
||||
- Manual promotion staging → production
|
||||
|
||||
3. **Add compliance:**
|
||||
- Deployment checklists
|
||||
- Audit logging
|
||||
- Manual approval gates
|
||||
|
||||
**See:** `CI_CD_STRATEGIES.md` for full production strategy
|
||||
|
||||
---
|
||||
|
||||
## Cost Considerations
|
||||
|
||||
### Pre-Release Phase (Minimal Costs)
|
||||
|
||||
**Single Dev Server:**
|
||||
- $20-40/month (DigitalOcean/Linode/Hetzner 4GB RAM)
|
||||
- Runs both app and database
|
||||
- Good for 5-10 concurrent testers
|
||||
|
||||
**When to upgrade:**
|
||||
- More than 10 concurrent users
|
||||
- Performance becomes noticeable
|
||||
- Before first customer
|
||||
|
||||
---
|
||||
|
||||
## Alignment with CI_CD_STRATEGIES.md
|
||||
|
||||
**This document covers:** Pre-Release Phase (Phase 0)
|
||||
- Before you have customers
|
||||
- Master branch = active development
|
||||
- Single dev environment
|
||||
- Fast iteration
|
||||
|
||||
**CI_CD_STRATEGIES.md covers:** Post-Release Phases
|
||||
- Phase 1: CI only (you completed this ✅)
|
||||
- Phase 2: Continuous Delivery (after first customer)
|
||||
- Phase 3: Environment Promotion (when team grows)
|
||||
|
||||
**Transition point:** First paying customer OR team grows to 5+ people
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference Commands
|
||||
|
||||
### Deploy Manually (if CI fails)
|
||||
|
||||
```bash
|
||||
# SSH into dev server
|
||||
ssh deployer@dev.cannabrands.app
|
||||
|
||||
# Navigate to app directory
|
||||
cd /var/www/cannabrands
|
||||
|
||||
# Pull latest image
|
||||
docker compose pull app
|
||||
|
||||
# Restart services
|
||||
docker compose up -d app
|
||||
|
||||
# Run migrations
|
||||
docker compose exec app php artisan migrate --force
|
||||
|
||||
# Clear caches
|
||||
docker compose exec app php artisan config:cache
|
||||
docker compose exec app php artisan route:cache
|
||||
docker compose exec app php artisan view:cache
|
||||
```
|
||||
|
||||
### Check Deployment Status
|
||||
|
||||
```bash
|
||||
# View logs
|
||||
docker compose logs -f app
|
||||
|
||||
# Check running containers
|
||||
docker compose ps
|
||||
|
||||
# View recent deployments
|
||||
docker images | grep cannabrands
|
||||
```
|
||||
|
||||
### Rollback (if deployment breaks)
|
||||
|
||||
```bash
|
||||
# Pull previous commit's image
|
||||
docker pull code.cannabrands.app/cannabrands/hub:PREVIOUS_SHA
|
||||
|
||||
# Update docker-compose.yml to use specific tag
|
||||
docker compose up -d app
|
||||
```
|
||||
|
||||
### Reset Database (fresh start)
|
||||
|
||||
```bash
|
||||
# ⚠️ This deletes ALL data
|
||||
docker compose down -v
|
||||
docker compose up -d
|
||||
docker compose exec app php artisan migrate --seed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Your current setup:**
|
||||
- ✅ CI tests pass on every push
|
||||
- ⬜ Build Docker images (add this)
|
||||
- ⬜ Auto-deploy to dev.cannabrands.app (add this)
|
||||
|
||||
**What this enables:**
|
||||
- Teammates can test at https://dev.cannabrands.app
|
||||
- Always reflects latest master
|
||||
- Fast iteration (deploy in ~2 minutes)
|
||||
- No manual deployment needed
|
||||
|
||||
**When to evolve:**
|
||||
- First customer → add staging + production
|
||||
- See CI_CD_STRATEGIES.md for next phases
|
||||
|
||||
**Key principle for pre-release:**
|
||||
*"Move fast and iterate. Stability comes later, when customers depend on you."*
|
||||
@@ -1,441 +1,165 @@
|
||||
# Quick Reference Guide - Cannabrands Release Workflow
|
||||
# Cannabrands CI/CD Pipeline
|
||||
|
||||
**Print this and keep it handy!**
|
||||
## Quick Reference
|
||||
|
||||
---
|
||||
**2-Environment Workflow** optimized for small teams:
|
||||
|
||||
## Daily Development (What You'll Do 95% of the Time)
|
||||
```
|
||||
localhost (Sail) → push to develop → dev.cannabrands.app
|
||||
↓
|
||||
PR: develop → master
|
||||
↓
|
||||
merge → cannabrands.app (production)
|
||||
```
|
||||
|
||||
### Making Changes
|
||||
## Daily Development
|
||||
|
||||
### Push to Develop (Auto-deploys to dev)
|
||||
|
||||
```bash
|
||||
# 1. Pull latest
|
||||
git checkout master
|
||||
git pull origin master
|
||||
|
||||
# 2. Make changes, commit with conventional format
|
||||
git checkout develop
|
||||
git pull origin develop
|
||||
# make changes
|
||||
git add .
|
||||
git commit -m "feat(orders): add bulk import feature"
|
||||
git push origin master
|
||||
|
||||
# ✅ DONE - CI automatically builds dev image
|
||||
git commit -m "feat(orders): add bulk import"
|
||||
git push origin develop
|
||||
# ✅ Auto-deploys to dev.cannabrands.app in ~4-5 minutes
|
||||
```
|
||||
|
||||
### Conventional Commit Format
|
||||
### Deploy to Production
|
||||
|
||||
**Format:** `type(scope): description`
|
||||
|
||||
**Types:**
|
||||
- `feat:` - New feature
|
||||
- `fix:` - Bug fix
|
||||
- `docs:` - Documentation only
|
||||
- `style:` - Code style (formatting)
|
||||
- `refactor:` - Code refactoring
|
||||
- `test:` - Adding tests
|
||||
- `chore:` - Build/dependencies
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
git commit -m "feat(orders): add CSV bulk import"
|
||||
git commit -m "fix(invoices): correct CA tax calculation"
|
||||
git commit -m "docs: update deployment guide"
|
||||
git commit -m "refactor(auth): simplify login flow"
|
||||
# 1. Test on dev.cannabrands.app first
|
||||
# 2. Open PR: develop → master
|
||||
# 3. Wait for CI checks to pass
|
||||
# 4. Click "Merge" in Gitea
|
||||
# ✅ Auto-deploys to cannabrands.app
|
||||
```
|
||||
|
||||
---
|
||||
## Pipeline Stages
|
||||
|
||||
## Creating a Release (Weekly/Monthly)
|
||||
| Event | What Runs | Time |
|
||||
|-------|-----------|------|
|
||||
| Push to `develop` | lint ∥ style → tests → seeders → build → deploy dev | ~4-5 min |
|
||||
| PR `develop → master` | lint ∥ style → tests | ~2-3 min |
|
||||
| Merge to `master` | build → deploy production | ~2-3 min |
|
||||
| Tag (e.g., `2025.12.1`) | build versioned release | ~2 min |
|
||||
|
||||
### Step 1: Determine Version Number
|
||||
**Key optimizations:**
|
||||
- Pre-built CI image with PHP extensions (saves ~60-90s)
|
||||
- Parallel lint + code-style checks
|
||||
- Tests run once (on PR), skipped on merge
|
||||
|
||||
## Commit Message Format
|
||||
|
||||
```bash
|
||||
# What's the current month and year?
|
||||
# Today: November 2025
|
||||
|
||||
# Check existing releases this month
|
||||
git tag -l "2025.11.*" | sort -V | tail -1
|
||||
# Output: 2025.11.2
|
||||
|
||||
# Next version: 2025.11.3
|
||||
feat(scope): description # New feature
|
||||
fix(scope): description # Bug fix
|
||||
docs: description # Documentation
|
||||
refactor(scope): desc # Code refactoring
|
||||
test(scope): description # Adding tests
|
||||
chore: description # Build/dependencies
|
||||
```
|
||||
|
||||
**Format:** `YYYY.MM.MICRO`
|
||||
- `2025` = Year
|
||||
- `11` = Month (November)
|
||||
- `3` = Third release this month
|
||||
**Scopes:** orders, invoices, auth, products, checkout, brands, etc.
|
||||
|
||||
### Step 2: Create Git Tag
|
||||
## Rollback
|
||||
|
||||
### Quick Rollback (Production)
|
||||
|
||||
```bash
|
||||
# Create annotated tag with release notes
|
||||
git tag -a 2025.11.3 -m "Release 2025.11.3 - Bulk Import Feature
|
||||
|
||||
Features:
|
||||
- Added CSV bulk order import
|
||||
- Enhanced manifest generation
|
||||
|
||||
Bug Fixes:
|
||||
- Fixed invoice tax calculation
|
||||
- Corrected order status transitions
|
||||
"
|
||||
|
||||
# Push tag to trigger CI
|
||||
git push origin 2025.11.3
|
||||
```
|
||||
|
||||
### Step 3: Wait for CI Build (2-4 minutes)
|
||||
|
||||
Watch at: `code.cannabrands.app/cannabrands/hub/pipelines`
|
||||
|
||||
CI will automatically:
|
||||
- Run tests
|
||||
- Build Docker image
|
||||
- Tag as: `2025.11.3` and `stable`
|
||||
- Push to registry
|
||||
|
||||
### Step 4: Generate Changelog
|
||||
|
||||
```bash
|
||||
# Generate/update CHANGELOG.md from commits
|
||||
npm run changelog
|
||||
|
||||
# Review the changes
|
||||
cat CHANGELOG.md
|
||||
|
||||
# Commit the updated changelog
|
||||
git add CHANGELOG.md
|
||||
git commit -m "docs: update changelog for 2025.11.3"
|
||||
git push origin master
|
||||
```
|
||||
|
||||
### Step 5: Deploy to Production (When Ready)
|
||||
|
||||
```bash
|
||||
# Deploy specific version
|
||||
kubectl set image deployment/cannabrands \
|
||||
app=code.cannabrands.app/cannabrands/hub:2025.11.3
|
||||
|
||||
# Watch deployment
|
||||
kubectl rollout status deployment/cannabrands
|
||||
|
||||
# Verify
|
||||
kubectl get pods
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Emergency Rollback
|
||||
|
||||
### Production is Broken - Immediate Action
|
||||
|
||||
```bash
|
||||
# Option 1: Rollback to previous version
|
||||
kubectl set image deployment/cannabrands \
|
||||
app=code.cannabrands.app/cannabrands/hub:2025.11.2
|
||||
# Option 1: Deploy previous commit
|
||||
kubectl set image deployment/cannabrands-hub \
|
||||
app=code.cannabrands.app/cannabrands/hub:prod-PREVIOUS_SHA \
|
||||
migrate=code.cannabrands.app/cannabrands/hub:prod-PREVIOUS_SHA \
|
||||
-n cannabrands-prod
|
||||
|
||||
# Option 2: Kubernetes automatic rollback
|
||||
kubectl rollout undo deployment/cannabrands
|
||||
|
||||
# Verify rollback
|
||||
kubectl rollout status deployment/cannabrands
|
||||
kubectl rollout undo deployment/cannabrands-hub -n cannabrands-prod
|
||||
```
|
||||
|
||||
### After Rollback - Fix Properly
|
||||
### Find Previous SHA
|
||||
|
||||
```bash
|
||||
# 1. Fix the bug on master
|
||||
git commit -m "fix: invoice calculation regression"
|
||||
git push origin master
|
||||
# View recent deployments
|
||||
kubectl rollout history deployment/cannabrands-hub -n cannabrands-prod
|
||||
|
||||
# 2. Test thoroughly in staging
|
||||
# 3. Create new release
|
||||
git tag -a 2025.11.4 -m "Hotfix: Invoice calculation"
|
||||
git push origin 2025.11.4
|
||||
|
||||
# 4. Deploy when confident
|
||||
kubectl set image deployment/cannabrands \
|
||||
app=code.cannabrands.app/cannabrands/hub:2025.11.4
|
||||
# View recent images
|
||||
git log --oneline -10
|
||||
```
|
||||
|
||||
---
|
||||
## Environments
|
||||
|
||||
## Image Tags Explained
|
||||
| Environment | URL | Branch | Namespace |
|
||||
|-------------|-----|--------|-----------|
|
||||
| Development | dev.cannabrands.app | develop | cannabrands-dev |
|
||||
| Production | cannabrands.app | master | cannabrands-prod |
|
||||
|
||||
## Image Tags
|
||||
|
||||
| Tag | Description | Used By |
|
||||
|-----|-------------|---------|
|
||||
| `dev` | Latest develop build | Dev environment |
|
||||
| `dev-{SHA}` | Specific dev commit | Dev deployments |
|
||||
| `latest` | Latest production build | - |
|
||||
| `prod-{SHA}` | Specific production commit | Prod deployments |
|
||||
| `2025.X.Y` | Versioned release | Rollback reference |
|
||||
|
||||
## Pre-built CI Image
|
||||
|
||||
The pipeline uses a pre-built image with PHP extensions to speed up builds:
|
||||
|
||||
### Development Images (Automatic)
|
||||
```
|
||||
latest-dev → Always newest master
|
||||
dev-c658193 → Specific commit (for debugging)
|
||||
master → Branch tracking
|
||||
code.cannabrands.app/cannabrands/ci-runner:latest
|
||||
```
|
||||
|
||||
**Use in K3s dev/staging:**
|
||||
```yaml
|
||||
image: code.cannabrands.app/cannabrands/hub:latest-dev
|
||||
imagePullPolicy: Always
|
||||
```
|
||||
### Rebuild CI Image (when PHP version changes)
|
||||
|
||||
### Production Images (Manual Release)
|
||||
```
|
||||
2025.11.3 → Specific release
|
||||
stable → Latest production release
|
||||
```
|
||||
|
||||
**Use in K3s production:**
|
||||
```yaml
|
||||
image: code.cannabrands.app/cannabrands/hub:2025.11.3
|
||||
imagePullPolicy: IfNotPresent
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Commands
|
||||
|
||||
### Check Current Version
|
||||
```bash
|
||||
# What's deployed in production?
|
||||
kubectl get deployment cannabrands -o jsonpath='{.spec.template.spec.containers[0].image}'
|
||||
|
||||
# What releases exist this month?
|
||||
git tag -l "2025.11.*" | sort -V
|
||||
cd docker/ci
|
||||
docker build -t code.cannabrands.app/cannabrands/ci-runner:latest .
|
||||
docker push code.cannabrands.app/cannabrands/ci-runner:latest
|
||||
```
|
||||
|
||||
### Test Locally
|
||||
## Secrets Required (Woodpecker)
|
||||
|
||||
| Secret | Description |
|
||||
|--------|-------------|
|
||||
| `gitea_username` | Gitea registry username |
|
||||
| `gitea_token` | Gitea registry token |
|
||||
| `kubeconfig_dev` | Base64-encoded kubeconfig for dev cluster |
|
||||
| `kubeconfig_prod` | Base64-encoded kubeconfig for prod cluster |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build Failing
|
||||
|
||||
```bash
|
||||
# Run tests
|
||||
# Run tests locally first
|
||||
./vendor/bin/sail artisan test
|
||||
|
||||
# Check code style
|
||||
./vendor/bin/pint --test
|
||||
|
||||
# Build Docker image locally
|
||||
docker build -t cannabrands:test .
|
||||
```
|
||||
|
||||
### View CI Status
|
||||
```bash
|
||||
# Visit Woodpecker
|
||||
open https://code.cannabrands.app/cannabrands/hub/pipelines
|
||||
|
||||
# Or check latest build
|
||||
# (Visit Gitea → Repository → Pipelines)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### CI Build Failing
|
||||
```bash
|
||||
# Check Woodpecker logs
|
||||
# Visit: code.cannabrands.app/cannabrands/hub/pipelines
|
||||
|
||||
# Run tests locally first
|
||||
./vendor/bin/sail artisan test
|
||||
|
||||
# Fix issues, push again
|
||||
git commit -m "fix: broken tests"
|
||||
git push origin master
|
||||
```
|
||||
|
||||
### Wrong Version Tagged
|
||||
```bash
|
||||
# Delete tag locally
|
||||
git tag -d 2025.11.3
|
||||
|
||||
# Delete tag remotely
|
||||
git push origin :refs/tags/2025.11.3
|
||||
|
||||
# Create correct tag
|
||||
git tag -a 2025.11.3 -m "Release 2025.11.3"
|
||||
git push origin 2025.11.3
|
||||
```
|
||||
|
||||
### Changelog Not Generating
|
||||
```bash
|
||||
# Make sure you have conventional commits
|
||||
git log --oneline | head -10
|
||||
|
||||
# Should see: feat:, fix:, docs:, etc.
|
||||
|
||||
# If missing, your commits need to follow convention
|
||||
# Run changelog anyway
|
||||
npm run changelog
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Versioning Examples
|
||||
|
||||
### Typical Month
|
||||
```
|
||||
2025.11.1 (Nov 5) - First release
|
||||
2025.11.2 (Nov 12) - Bug fixes
|
||||
2025.11.3 (Nov 19) - New features
|
||||
2025.11.4 (Nov 26) - Hotfix
|
||||
2025.12.1 (Dec 3) - New month, reset
|
||||
```
|
||||
|
||||
### High Frequency (Multiple per day)
|
||||
```
|
||||
2025.11.23.1 - Morning release
|
||||
2025.11.23.2 - Afternoon hotfix
|
||||
2025.11.24.1 - Next day
|
||||
```
|
||||
|
||||
### Skipping Numbers (OK!)
|
||||
```
|
||||
2025.11.1 ✅
|
||||
2025.11.2 ✅
|
||||
2025.11.5 ✅ (skipped 3 and 4 - fine!)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CI/CD Pipeline Stages
|
||||
|
||||
The Woodpecker CI pipeline runs the following stages for every push to `develop` or `master`:
|
||||
|
||||
1. **PHP Lint** - Syntax validation
|
||||
2. **Code Style (Pint)** - Formatting check
|
||||
3. **Tests** - PHPUnit/Pest tests with `APP_ENV=testing`
|
||||
4. **Seeder Validation** - Validates seeders with `APP_ENV=development`
|
||||
5. **Docker Build** - Creates container image
|
||||
6. **Auto-Deploy** - Deploys to dev.cannabrands.app (develop branch only)
|
||||
|
||||
### Why Seeder Validation?
|
||||
|
||||
The dev environment (`dev.cannabrands.app`) runs `migrate:fresh --seed` on every K8s deployment via init container. If seeders have bugs (e.g., undefined functions, missing relationships), the deployment fails and pods crash.
|
||||
|
||||
**The Problem:**
|
||||
- Tests run with `APP_ENV=testing` which **skips DevSeeder**
|
||||
- K8s runs with `APP_ENV=development` which **runs DevSeeder**
|
||||
- Seeder bugs passed CI but crashed in K8s
|
||||
|
||||
**The Solution:**
|
||||
- Add dedicated seeder validation step with `APP_ENV=development`
|
||||
- Runs the exact same command as K8s init container
|
||||
- Catches seeder errors before deployment
|
||||
|
||||
**Time Cost:** ~20-30 seconds added to CI pipeline
|
||||
|
||||
**What It Catches:**
|
||||
- Runtime errors (e.g., `fake()` outside factory context)
|
||||
- Database constraint violations
|
||||
- Missing relationships (foreign key errors)
|
||||
- Invalid enum values
|
||||
- Seeder syntax errors
|
||||
|
||||
---
|
||||
|
||||
## Pre-Commit Checklist
|
||||
|
||||
Before committing:
|
||||
- [ ] Tests pass locally (`./vendor/bin/sail artisan test`)
|
||||
- [ ] Code formatted (`./vendor/bin/pint` runs automatically)
|
||||
- [ ] Commit message follows convention (feat:, fix:, etc.)
|
||||
|
||||
Before releasing:
|
||||
- [ ] All tests green in CI
|
||||
- [ ] **Seeder validation passed in CI**
|
||||
- [ ] Tested in dev/staging environment
|
||||
- [ ] Release notes written
|
||||
- [ ] CHANGELOG updated (auto-generated)
|
||||
|
||||
Before deploying:
|
||||
- [ ] Tag created and pushed
|
||||
- [ ] CI build successful
|
||||
- [ ] Team notified
|
||||
- [ ] Deployment window appropriate (not Friday night!)
|
||||
|
||||
---
|
||||
|
||||
## Getting Help
|
||||
|
||||
### Documentation
|
||||
- `RELEASE_WORKFLOW_GUIDE.md` - Detailed release process
|
||||
- `VERSIONING_STRATEGY.md` - CalVer strategy & rollback
|
||||
- `GIT_BRANCHING_STRATEGY.md` - Git workflow
|
||||
- `CI_CD_STRATEGIES.md` - Overall strategy
|
||||
|
||||
### Team
|
||||
- Ask in #engineering Slack channel
|
||||
- Pair with senior dev for first release
|
||||
|
||||
### CI/CD
|
||||
- Woodpecker: `code.cannabrands.app/cannabrands/hub`
|
||||
- Gitea: `code.cannabrands.app/cannabrands/hub`
|
||||
- K3s Dashboard: (ask devops for link)
|
||||
|
||||
---
|
||||
|
||||
## Important URLs
|
||||
|
||||
**Code Repository:**
|
||||
https://code.cannabrands.app/cannabrands/hub
|
||||
|
||||
**CI/CD Pipeline:**
|
||||
https://code.cannabrands.app/cannabrands/hub/pipelines
|
||||
|
||||
**Container Registry:**
|
||||
https://code.cannabrands.app/-/packages/container/cannabrands%2Fhub
|
||||
|
||||
**Documentation:**
|
||||
`.woodpecker/` directory in repository
|
||||
|
||||
---
|
||||
|
||||
## Commit Message Cheat Sheet
|
||||
### Deployment Stuck
|
||||
|
||||
```bash
|
||||
# New feature
|
||||
git commit -m "feat(scope): what you added"
|
||||
# Check pod status
|
||||
kubectl get pods -n cannabrands-dev
|
||||
kubectl describe pod POD_NAME -n cannabrands-dev
|
||||
|
||||
# Bug fix
|
||||
git commit -m "fix(scope): what you fixed"
|
||||
|
||||
# Documentation
|
||||
git commit -m "docs: what you documented"
|
||||
|
||||
# Code cleanup
|
||||
git commit -m "refactor(scope): what you refactored"
|
||||
|
||||
# Testing
|
||||
git commit -m "test(scope): what you tested"
|
||||
|
||||
# Dependencies/config
|
||||
git commit -m "chore: what you updated"
|
||||
# Check logs
|
||||
kubectl logs -f deployment/cannabrands-hub -n cannabrands-dev
|
||||
```
|
||||
|
||||
**Scope examples:** orders, invoices, auth, products, checkout
|
||||
### Seeder Issues in Dev
|
||||
|
||||
**Full example:**
|
||||
```bash
|
||||
git commit -m "feat(orders): add CSV bulk import
|
||||
|
||||
Allows sellers to import multiple orders from CSV file.
|
||||
Includes validation and preview before import.
|
||||
|
||||
Closes #42"
|
||||
```
|
||||
The pipeline validates seeders before deploying to dev. If seeders fail:
|
||||
1. Check the CI logs for the specific error
|
||||
2. Fix the seeder locally
|
||||
3. Push again
|
||||
|
||||
---
|
||||
|
||||
## One-Page Summary
|
||||
|
||||
| Task | Command |
|
||||
|------|---------|
|
||||
| Daily commit | `git commit -m "feat(scope): description"` |
|
||||
| Create release | `git tag -a 2025.11.1 -m "notes"` |
|
||||
| Update changelog | `npm run changelog` |
|
||||
| Deploy | `kubectl set image deployment/cannabrands app=...:2025.11.1` |
|
||||
| Rollback | `kubectl set image deployment/cannabrands app=...:2025.11.0` |
|
||||
| Check version | `kubectl get deployment cannabrands -o jsonpath='{.spec.template.spec.containers[0].image}'` |
|
||||
| View builds | Visit `code.cannabrands.app/cannabrands/hub/pipelines` |
|
||||
|
||||
---
|
||||
|
||||
**Key Principle:** *Commit often, release when ready, rollback without fear.*
|
||||
|
||||
**Version:** 1.0
|
||||
**Last Updated:** 2025-10-23
|
||||
**Print and keep handy!**
|
||||
**Last Updated:** December 2025
|
||||
|
||||
@@ -1,817 +0,0 @@
|
||||
# Release Workflow Guide for Cannabrands Team
|
||||
|
||||
## Purpose
|
||||
|
||||
This guide explains our release workflow, why we made these choices, and how to execute releases. Use this for onboarding new team members and as a reference.
|
||||
|
||||
---
|
||||
|
||||
## Our Release Philosophy
|
||||
|
||||
**Core Principle:** *"Automate the mechanical, preserve human judgment."*
|
||||
|
||||
- ✅ Automated: Tests, builds, image creation
|
||||
- 🤔 Human decision: When to release, what to include
|
||||
- ⚡ Fast: Push to master → deployed to dev in ~3 minutes
|
||||
|
||||
---
|
||||
|
||||
## Two Tracks: Development vs Production
|
||||
|
||||
### Track 1: Development (Automatic - Daily)
|
||||
|
||||
**Who uses it:** Internal team testing
|
||||
|
||||
**What happens:**
|
||||
```bash
|
||||
# You do this:
|
||||
git commit -m "feat: add bulk order import"
|
||||
git push origin master
|
||||
|
||||
# Automatic chain reaction:
|
||||
1. Woodpecker CI triggers (immediate)
|
||||
2. Tests run (PHP lint, Pint, PHPUnit)
|
||||
3. Docker image builds (if tests pass)
|
||||
4. Tagged as: latest-dev, dev-c658193, master
|
||||
5. Pushed to code.cannabrands.app/cannabrands/hub
|
||||
6. Available in K3s dev namespace (manual or auto-pull)
|
||||
```
|
||||
|
||||
**Timeline:** 2-4 minutes from push to available
|
||||
|
||||
**Tags created:**
|
||||
- `latest-dev` - Always points to newest master
|
||||
- `dev-c658193` - Specific commit for debugging
|
||||
- `master` - Branch tracking
|
||||
|
||||
**Use in K3s:**
|
||||
```yaml
|
||||
# dev/staging namespace
|
||||
image: code.cannabrands.app/cannabrands/hub:latest-dev
|
||||
imagePullPolicy: Always # Always pull newest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Track 2: Production (Manual Decision - Weekly/Monthly)
|
||||
|
||||
**Who uses it:** Customers, production environment
|
||||
|
||||
**What happens:**
|
||||
```bash
|
||||
# You decide to create a release:
|
||||
git tag -a 2025.11.1 -m "Release 2025.11.1 - Invoice improvements"
|
||||
git push origin 2025.11.1
|
||||
|
||||
# Automatic chain reaction:
|
||||
1. Woodpecker CI triggers (immediate)
|
||||
2. Tests run (same as dev)
|
||||
3. Docker image builds (if tests pass)
|
||||
4. Tagged as: 2025.11.1, stable
|
||||
5. Pushed to registry
|
||||
6. You deploy when ready (manual kubectl command)
|
||||
```
|
||||
|
||||
**Timeline:** 2-4 minutes to build, deploy when you choose
|
||||
|
||||
**Tags created:**
|
||||
- `2025.11.1` - Specific release version
|
||||
- `stable` - Always points to latest production release
|
||||
|
||||
**Use in K3s:**
|
||||
```yaml
|
||||
# production namespace
|
||||
image: code.cannabrands.app/cannabrands/hub:2025.11.1
|
||||
imagePullPolicy: IfNotPresent # Pin to specific version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Calendar Versioning (CalVer) Explained
|
||||
|
||||
### Format: YYYY.MM.MICRO
|
||||
|
||||
**Example:** `2025.11.1`
|
||||
- **2025** = Year
|
||||
- **11** = Month (November)
|
||||
- **1** = First release in November
|
||||
|
||||
**Why CalVer instead of SemVer (v1.2.3)?**
|
||||
|
||||
| CalVer (2025.11.1) | SemVer (v1.2.3) |
|
||||
|--------------------|-----------------|
|
||||
| ✅ Shows WHEN released | ❌ Doesn't show time |
|
||||
| ✅ Natural chronological order | ⚠️ Can be confusing (v2.0 vs v1.9?) |
|
||||
| ✅ Great for compliance/audits | ❌ Less useful for auditors |
|
||||
| ✅ No debates ("major or minor?") | ❌ Constant debates |
|
||||
| ✅ Web app model (you control both ends) | ✅ Library model (public API) |
|
||||
|
||||
**Companies using CalVer:**
|
||||
- Ubuntu (20.04, 22.04)
|
||||
- Puppet (2023.1)
|
||||
- PyCharm (2025.3)
|
||||
- Cal.com (app for scheduling)
|
||||
|
||||
**Companies using SemVer:**
|
||||
- NPM packages
|
||||
- Laravel framework
|
||||
- React library
|
||||
|
||||
**Rule of thumb:**
|
||||
- Web apps (you) → CalVer
|
||||
- Libraries/APIs → SemVer
|
||||
|
||||
---
|
||||
|
||||
## Version Number Examples
|
||||
|
||||
### Realistic Timeline
|
||||
|
||||
```
|
||||
2025.11.1 (Nov 5) - First customer release
|
||||
2025.11.2 (Nov 12) - Bug fixes
|
||||
2025.11.3 (Nov 19) - New feature (bulk import)
|
||||
2025.11.4 (Nov 26) - Hotfix (invoice bug)
|
||||
2025.12.1 (Dec 3) - Month rollover, new features
|
||||
2025.12.2 (Dec 10) - Bug fixes
|
||||
```
|
||||
|
||||
### Alternative for High Frequency
|
||||
|
||||
If you release multiple times per day:
|
||||
```
|
||||
2025.11.23.1 - First release on Nov 23
|
||||
2025.11.23.2 - Hotfix same day
|
||||
2025.11.24.1 - Next day
|
||||
```
|
||||
|
||||
**Recommendation:** Start with `YYYY.MM.MICRO`, switch if needed.
|
||||
|
||||
---
|
||||
|
||||
## Daily Workflow (Development)
|
||||
|
||||
### For Developers
|
||||
|
||||
**Morning:**
|
||||
```bash
|
||||
git checkout master
|
||||
git pull origin master
|
||||
git checkout -b feature/add-payment-terms
|
||||
|
||||
# Make changes...
|
||||
git add .
|
||||
git commit -m "feat: add payment term surcharges"
|
||||
git push origin feature/add-payment-terms
|
||||
|
||||
# Create PR in Gitea (optional for now)
|
||||
# After approval (or if working solo):
|
||||
git checkout master
|
||||
git merge feature/add-payment-terms
|
||||
git push origin master
|
||||
|
||||
# ✅ DONE - Automatic CI builds dev image
|
||||
```
|
||||
|
||||
**What happens automatically:**
|
||||
1. Woodpecker runs tests
|
||||
2. Builds Docker image
|
||||
3. Pushes to registry
|
||||
4. Team can test in K3s dev environment
|
||||
|
||||
**No manual steps for releases** - this is daily development.
|
||||
|
||||
---
|
||||
|
||||
## Release Workflow (Production)
|
||||
|
||||
### When to Create a Release
|
||||
|
||||
**Triggers for creating a release:**
|
||||
- ✅ First customer signs up (big milestone)
|
||||
- ✅ Weekly release schedule (every Monday?)
|
||||
- ✅ Major feature complete and tested
|
||||
- ✅ Critical bug fixed (hotfix)
|
||||
- ✅ Compliance audit scheduled (need frozen version)
|
||||
- ✅ Before regulatory inspection
|
||||
|
||||
**DON'T create releases for:**
|
||||
- ❌ Every commit to master
|
||||
- ❌ Incomplete features
|
||||
- ❌ Untested code
|
||||
- ❌ "Just to have a version"
|
||||
|
||||
---
|
||||
|
||||
### Step-by-Step: Creating a Release
|
||||
|
||||
#### 1. Ensure Code is Stable
|
||||
|
||||
```bash
|
||||
# Verify tests pass locally
|
||||
./vendor/bin/sail artisan test
|
||||
|
||||
# Check CI is green
|
||||
# Visit: code.cannabrands.app/cannabrands/hub/pipelines
|
||||
|
||||
# Test in staging/dev environment
|
||||
# Verify key workflows work
|
||||
```
|
||||
|
||||
#### 2. Determine Version Number
|
||||
|
||||
**Current month and release:**
|
||||
```bash
|
||||
# What's the current version?
|
||||
git tag -l "2025.11.*" | sort -V | tail -1
|
||||
# Output: 2025.11.2
|
||||
|
||||
# Next version is 2025.11.3
|
||||
```
|
||||
|
||||
**New month:**
|
||||
```bash
|
||||
# It's now December, start fresh
|
||||
# Next version: 2025.12.1
|
||||
```
|
||||
|
||||
#### 3. Create Git Tag
|
||||
|
||||
```bash
|
||||
# Format: YYYY.MM.MICRO
|
||||
git tag -a 2025.11.3 -m "Release 2025.11.3 - Bulk order import
|
||||
|
||||
Features:
|
||||
- Added CSV bulk order import
|
||||
- Enhanced manifest generation
|
||||
|
||||
Bug Fixes:
|
||||
- Fixed invoice tax calculation
|
||||
- Corrected order status transitions
|
||||
|
||||
Testing:
|
||||
- Tested with 50+ orders
|
||||
- Verified in staging environment
|
||||
"
|
||||
|
||||
# Push tag to trigger CI
|
||||
git push origin 2025.11.3
|
||||
```
|
||||
|
||||
**Note:** The `-m` message becomes your release notes. Be descriptive!
|
||||
|
||||
#### 4. Monitor CI Build
|
||||
|
||||
```bash
|
||||
# Watch Woodpecker build
|
||||
# Visit: code.cannabrands.app/cannabrands/hub/pipelines
|
||||
|
||||
# Wait for success (2-4 minutes)
|
||||
# CI will build and push:
|
||||
# - code.cannabrands.app/cannabrands/hub:2025.11.3
|
||||
# - code.cannabrands.app/cannabrands/hub:stable
|
||||
```
|
||||
|
||||
#### 5. Deploy to Production (When Ready)
|
||||
|
||||
```bash
|
||||
# Deploy new version
|
||||
kubectl set image deployment/cannabrands \
|
||||
app=code.cannabrands.app/cannabrands/hub:2025.11.3
|
||||
|
||||
# Watch rollout
|
||||
kubectl rollout status deployment/cannabrands
|
||||
|
||||
# Verify deployment
|
||||
kubectl get pods
|
||||
kubectl logs -f deployment/cannabrands
|
||||
```
|
||||
|
||||
#### 6. Update CHANGELOG (Optional but Recommended)
|
||||
|
||||
```bash
|
||||
# Edit CHANGELOG.md
|
||||
nano CHANGELOG.md
|
||||
|
||||
# Add entry:
|
||||
## [2025.11.3] - 2025-11-19
|
||||
### Added
|
||||
- CSV bulk order import feature
|
||||
- Enhanced manifest generation with audit trail
|
||||
|
||||
### Fixed
|
||||
- Invoice tax calculation for CA orders
|
||||
- Order status transition for pending approvals
|
||||
|
||||
# Commit and push
|
||||
git add CHANGELOG.md
|
||||
git commit -m "docs: update changelog for 2025.11.3"
|
||||
git push origin master
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rollback Procedure (Production Issues)
|
||||
|
||||
### Scenario: New Release is Broken
|
||||
|
||||
**Timeline:**
|
||||
```
|
||||
2:00pm - Deploy 2025.11.3
|
||||
2:15pm - Customer reports invoices broken
|
||||
2:16pm - Confirm issue
|
||||
2:17pm - ⚠️ DECISION POINT
|
||||
```
|
||||
|
||||
### Immediate Rollback (2 minutes)
|
||||
|
||||
```bash
|
||||
# Option 1: Rollback to specific version
|
||||
kubectl set image deployment/cannabrands \
|
||||
app=code.cannabrands.app/cannabrands/hub:2025.11.2
|
||||
|
||||
# Option 2: Use previous stable
|
||||
kubectl set image deployment/cannabrands \
|
||||
app=code.cannabrands.app/cannabrands/hub:stable
|
||||
|
||||
# Note: 'stable' is updated on every release
|
||||
# So if you just deployed 2025.11.3, 'stable' points to 2025.11.3
|
||||
# Use specific version (2025.11.2) for rollback
|
||||
|
||||
# Option 3: Kubernetes automatic rollback
|
||||
kubectl rollout undo deployment/cannabrands
|
||||
|
||||
# Verify rollback
|
||||
kubectl rollout status deployment/cannabrands
|
||||
```
|
||||
|
||||
### Fix Forward (Proper Fix)
|
||||
|
||||
```bash
|
||||
# After rollback, service is restored
|
||||
# Now fix the issue properly:
|
||||
|
||||
1. Investigate root cause (no rush)
|
||||
2. Fix bug on master
|
||||
3. Test thoroughly in dev/staging
|
||||
4. Create new release: 2025.11.4
|
||||
5. Deploy when confident
|
||||
|
||||
git commit -m "fix: invoice calculation regression"
|
||||
git push origin master
|
||||
|
||||
# Test in staging
|
||||
# When ready:
|
||||
git tag -a 2025.11.4 -m "Hotfix: Invoice calculation"
|
||||
git push origin 2025.11.4
|
||||
|
||||
# Deploy
|
||||
kubectl set image deployment/cannabrands \
|
||||
app=code.cannabrands.app/cannabrands/hub:2025.11.4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Automation Options
|
||||
|
||||
### Current State: Manual Tagging (Recommended)
|
||||
|
||||
**What's automated:**
|
||||
- ✅ CI tests
|
||||
- ✅ Docker image building
|
||||
- ✅ Image pushing to registry
|
||||
- ✅ Pre-commit code formatting
|
||||
|
||||
**What's manual:**
|
||||
- 🤔 Deciding when to release
|
||||
- 🤔 Creating git tags
|
||||
- 🤔 Writing release notes
|
||||
- 🤔 Deploying to production
|
||||
|
||||
**Why keep it manual?**
|
||||
- You make the decision when code is "ready"
|
||||
- Preserves human judgment
|
||||
- Industry standard for companies your size
|
||||
- Cannabis compliance (audit trail of decisions)
|
||||
|
||||
---
|
||||
|
||||
### Option: Add Auto-Changelog
|
||||
|
||||
**What it does:** Automatically generates CHANGELOG.md from commits
|
||||
|
||||
**Install:**
|
||||
```bash
|
||||
npm install -g conventional-changelog-cli
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
# After creating release tag
|
||||
conventional-changelog -p angular -i CHANGELOG.md -s
|
||||
|
||||
# Then commit the updated CHANGELOG
|
||||
git add CHANGELOG.md
|
||||
git commit -m "docs: update changelog"
|
||||
git push origin master
|
||||
```
|
||||
|
||||
**Or automate in CI:**
|
||||
```yaml
|
||||
# .woodpecker/.ci.yml
|
||||
changelog:
|
||||
image: node:20
|
||||
commands:
|
||||
- npm install -g conventional-changelog-cli
|
||||
- conventional-changelog -p angular -i CHANGELOG.md -s -r 0
|
||||
when:
|
||||
event: tag
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- ✅ Automatic changelog generation
|
||||
- ✅ Enforces commit message format
|
||||
- ✅ No manual CHANGELOG maintenance
|
||||
|
||||
**Cons:**
|
||||
- ⚠️ Requires conventional commits (feat:, fix:, etc.)
|
||||
- ⚠️ Can be noisy if commits aren't clean
|
||||
|
||||
---
|
||||
|
||||
### Option: Automated CalVer Tagging (Advanced)
|
||||
|
||||
**What it does:** Automatically creates CalVer tags on merge to master
|
||||
|
||||
**Implementation:**
|
||||
```yaml
|
||||
# .woodpecker/.auto-release.yml
|
||||
when:
|
||||
event: push
|
||||
branch: master
|
||||
|
||||
steps:
|
||||
auto-tag:
|
||||
image: alpine/git
|
||||
commands:
|
||||
- apk add --no-cache bash coreutils
|
||||
- |
|
||||
# Generate CalVer tag
|
||||
YEAR=$(date +%Y)
|
||||
MONTH=$(date +%-m)
|
||||
|
||||
# Find highest MICRO for this month
|
||||
LATEST=$(git tag -l "${YEAR}.${MONTH}.*" | sort -V | tail -1)
|
||||
if [ -z "$LATEST" ]; then
|
||||
MICRO=1
|
||||
else
|
||||
MICRO=$(echo $LATEST | cut -d. -f3)
|
||||
MICRO=$((MICRO + 1))
|
||||
fi
|
||||
|
||||
TAG="${YEAR}.${MONTH}.${MICRO}"
|
||||
|
||||
# Create tag
|
||||
git tag -a $TAG -m "Auto-release $TAG"
|
||||
git push origin $TAG
|
||||
```
|
||||
|
||||
**Result:** Every push to master automatically creates a release.
|
||||
|
||||
**Pros:**
|
||||
- ✅ Fully automated releases
|
||||
- ✅ No manual tagging needed
|
||||
- ✅ Consistent versioning
|
||||
|
||||
**Cons:**
|
||||
- ❌ No human judgment (every commit becomes a release)
|
||||
- ❌ Can't skip a bad release easily
|
||||
- ❌ Loses audit trail of "we decided to release this"
|
||||
|
||||
**Recommendation:** **DON'T use this** (yet). Wait until:
|
||||
- You have 100+ deployments per month
|
||||
- You need to release multiple times per day
|
||||
- You have comprehensive test coverage (80%+)
|
||||
|
||||
---
|
||||
|
||||
## Recommended Automation (Add Now)
|
||||
|
||||
### Add Auto-Changelog Generation
|
||||
|
||||
**Step 1: Install conventional-changelog**
|
||||
```bash
|
||||
npm install --save-dev conventional-changelog-cli
|
||||
```
|
||||
|
||||
**Step 2: Add to package.json**
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: Update your release workflow**
|
||||
```bash
|
||||
# After creating git tag:
|
||||
git push origin 2025.11.3
|
||||
|
||||
# Generate changelog
|
||||
npm run changelog
|
||||
|
||||
# Commit updated changelog
|
||||
git add CHANGELOG.md
|
||||
git commit -m "docs: update changelog for 2025.11.3"
|
||||
git push origin master
|
||||
```
|
||||
|
||||
**Step 4: (Optional) Automate in CI**
|
||||
```yaml
|
||||
# Add to .woodpecker/.ci.yml
|
||||
update-changelog:
|
||||
image: node:20
|
||||
commands:
|
||||
- npm ci
|
||||
- npm run changelog
|
||||
- |
|
||||
if ! git diff --quiet CHANGELOG.md; then
|
||||
git config user.name "Woodpecker CI"
|
||||
git config user.email "ci@cannabrands.com"
|
||||
git add CHANGELOG.md
|
||||
git commit -m "docs: update changelog for ${CI_COMMIT_TAG}"
|
||||
git push origin master
|
||||
fi
|
||||
when:
|
||||
event: tag
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commit Message Convention
|
||||
|
||||
For auto-changelog to work well, use conventional commits:
|
||||
|
||||
### Format
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
|
||||
<body>
|
||||
|
||||
<footer>
|
||||
```
|
||||
|
||||
### Types
|
||||
- `feat`: New feature
|
||||
- `fix`: Bug fix
|
||||
- `docs`: Documentation only
|
||||
- `style`: Code style (formatting, no logic change)
|
||||
- `refactor`: Code refactoring
|
||||
- `test`: Adding tests
|
||||
- `chore`: Build process, dependencies
|
||||
|
||||
### Examples
|
||||
|
||||
**Good:**
|
||||
```bash
|
||||
git commit -m "feat(orders): add CSV bulk import
|
||||
|
||||
Allows sellers to import multiple orders at once from CSV file.
|
||||
Validates data before import and shows preview.
|
||||
|
||||
Closes #42"
|
||||
```
|
||||
|
||||
```bash
|
||||
git commit -m "fix(invoices): correct tax calculation for CA
|
||||
|
||||
California orders were calculating sales tax incorrectly.
|
||||
Now properly applies 7.25% base rate + local rates.
|
||||
|
||||
Fixes #123"
|
||||
```
|
||||
|
||||
```bash
|
||||
git commit -m "docs: update deployment guide for K3s"
|
||||
```
|
||||
|
||||
**Bad (won't work well with auto-changelog):**
|
||||
```bash
|
||||
git commit -m "fixed stuff"
|
||||
git commit -m "updates"
|
||||
git commit -m "wip"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Team Training Checklist
|
||||
|
||||
Use this to onboard new developers:
|
||||
|
||||
### Day 1: Understanding the Flow
|
||||
- [ ] Read this document
|
||||
- [ ] Understand dev vs production tracks
|
||||
- [ ] Learn CalVer format (YYYY.MM.MICRO)
|
||||
- [ ] Review commit message conventions
|
||||
|
||||
### Week 1: Development Flow
|
||||
- [ ] Make changes on feature branch
|
||||
- [ ] Push to master (or create PR)
|
||||
- [ ] Watch Woodpecker CI run
|
||||
- [ ] See dev image appear in registry
|
||||
- [ ] Test in K3s dev environment
|
||||
|
||||
### Week 2: Release Flow (Shadow Senior Dev)
|
||||
- [ ] Watch senior dev create a release
|
||||
- [ ] Understand tag creation process
|
||||
- [ ] See production deployment
|
||||
- [ ] Learn rollback procedure
|
||||
|
||||
### Week 3: First Release (Supervised)
|
||||
- [ ] Create your first release tag (with supervision)
|
||||
- [ ] Write release notes
|
||||
- [ ] Deploy to staging/production
|
||||
- [ ] Update CHANGELOG
|
||||
|
||||
### Week 4: Independent
|
||||
- [ ] Create releases independently
|
||||
- [ ] Handle rollbacks if needed
|
||||
- [ ] Mentor new team members
|
||||
|
||||
---
|
||||
|
||||
## FAQ
|
||||
|
||||
### Q: How often should we release?
|
||||
|
||||
**A:** No fixed rule, but typical patterns:
|
||||
|
||||
**Pre-first customer (now):**
|
||||
- Releases: When needed (weekly-ish)
|
||||
- Dev builds: Constantly (every push)
|
||||
|
||||
**1-10 customers:**
|
||||
- Releases: Weekly or bi-weekly
|
||||
- Schedule: Every Monday morning
|
||||
|
||||
**10+ customers:**
|
||||
- Releases: Multiple per week
|
||||
- Schedule: As features are ready
|
||||
|
||||
**High-growth:**
|
||||
- Releases: Daily
|
||||
- May add automated tagging
|
||||
|
||||
---
|
||||
|
||||
### Q: What if we skip a version number?
|
||||
|
||||
**A:** No problem!
|
||||
|
||||
```
|
||||
2025.11.1 ✅
|
||||
2025.11.2 ✅
|
||||
2025.11.5 ✅ (skipped 3 and 4 - fine!)
|
||||
```
|
||||
|
||||
Version numbers are not sacred. If you want to skip, skip.
|
||||
|
||||
---
|
||||
|
||||
### Q: Can we have multiple releases per day?
|
||||
|
||||
**A:** Yes! Use extended format:
|
||||
|
||||
```
|
||||
2025.11.23.1 - Morning release
|
||||
2025.11.23.2 - Afternoon hotfix
|
||||
2025.11.24.1 - Next day
|
||||
```
|
||||
|
||||
Or just increment MICRO:
|
||||
```
|
||||
2025.11.1
|
||||
2025.11.2
|
||||
2025.11.3
|
||||
(all same day - fine!)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Q: What if CI fails on a tag?
|
||||
|
||||
**A:** Delete and recreate:
|
||||
|
||||
```bash
|
||||
# Delete local tag
|
||||
git tag -d 2025.11.3
|
||||
|
||||
# Delete remote tag
|
||||
git push origin :refs/tags/2025.11.3
|
||||
|
||||
# Fix the issue
|
||||
git commit -m "fix: issue that broke CI"
|
||||
git push origin master
|
||||
|
||||
# Create tag again
|
||||
git tag -a 2025.11.3 -m "Release 2025.11.3"
|
||||
git push origin 2025.11.3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Q: Should we delete old Docker images?
|
||||
|
||||
**A:** Keep recent releases, clean old dev builds:
|
||||
|
||||
**Keep forever:**
|
||||
- All production releases (2025.11.1, 2025.11.2, etc.)
|
||||
- Last 10 releases minimum
|
||||
|
||||
**Clean periodically:**
|
||||
- Dev builds older than 30 days
|
||||
- Failed/broken releases (after investigation)
|
||||
|
||||
**Why keep old releases:**
|
||||
- Compliance audit trail
|
||||
- Rollback capability
|
||||
- Forensic analysis of bugs
|
||||
|
||||
---
|
||||
|
||||
### Q: Can we automate production deployment?
|
||||
|
||||
**A:** Not recommended for cannabis industry:
|
||||
|
||||
**Why NOT automate:**
|
||||
- Regulatory compliance requires human approval
|
||||
- Need audit trail of who deployed what
|
||||
- Cannabis industry = higher stakes
|
||||
- Manual gate = safer
|
||||
|
||||
**What to automate instead:**
|
||||
- Deployment to dev/staging
|
||||
- Tests and builds
|
||||
- Release notes generation
|
||||
|
||||
---
|
||||
|
||||
## Summary: What to Remember
|
||||
|
||||
**Daily work:**
|
||||
```bash
|
||||
# Just push to master
|
||||
git push origin master
|
||||
# Everything else is automatic
|
||||
```
|
||||
|
||||
**Creating releases (weekly/monthly):**
|
||||
```bash
|
||||
# 1. Create tag
|
||||
git tag -a 2025.11.3 -m "Release notes"
|
||||
git push origin 2025.11.3
|
||||
|
||||
# 2. Wait for CI (2-4 min)
|
||||
|
||||
# 3. Deploy when ready
|
||||
kubectl set image deployment/cannabrands app=...:2025.11.3
|
||||
```
|
||||
|
||||
**Emergency rollback:**
|
||||
```bash
|
||||
# One command
|
||||
kubectl set image deployment/cannabrands app=...:2025.11.2
|
||||
```
|
||||
|
||||
**Key principles:**
|
||||
- ✅ Automate the mechanical (tests, builds)
|
||||
- 🤔 Preserve human judgment (releases, deployments)
|
||||
- 📅 Use CalVer for timestamp-based versions
|
||||
- 🔄 Rollback first, fix later
|
||||
- 📝 Document decisions (release notes, CHANGELOG)
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
**Implement now:**
|
||||
1. ✅ Keep current manual tagging (it's working)
|
||||
2. ✅ Add auto-changelog generation
|
||||
3. ✅ Create CHANGELOG.md file
|
||||
|
||||
**Consider later (when needed):**
|
||||
1. Automated tagging (if you release daily)
|
||||
2. Feature flags (for gradual rollouts)
|
||||
3. Automated staging deployments
|
||||
|
||||
**Never compromise:**
|
||||
1. Manual production deployment (compliance)
|
||||
2. Human approval for releases (judgment)
|
||||
3. Audit trail (cannabis industry requirement)
|
||||
|
||||
---
|
||||
|
||||
**Version:** 1.0
|
||||
**Last Updated:** 2025-10-23
|
||||
**Maintained By:** Engineering Team
|
||||
68
docker/ci/Dockerfile
Normal file
68
docker/ci/Dockerfile
Normal file
@@ -0,0 +1,68 @@
|
||||
# ============================================
|
||||
# Pre-built CI Image for Woodpecker Pipeline
|
||||
# ============================================
|
||||
# This image has all PHP extensions pre-installed to speed up CI runs.
|
||||
# Build and push to: code.cannabrands.app/cannabrands/ci-runner:latest
|
||||
#
|
||||
# Build command:
|
||||
# docker build -t code.cannabrands.app/cannabrands/ci-runner:latest -f docker/ci/Dockerfile .
|
||||
# docker push code.cannabrands.app/cannabrands/ci-runner:latest
|
||||
#
|
||||
# Saves ~60-90 seconds per pipeline run by avoiding extension installation.
|
||||
|
||||
FROM php:8.3-cli-alpine
|
||||
|
||||
LABEL maintainer="CannaBrands DevOps"
|
||||
LABEL description="Pre-built CI runner with PHP extensions for Woodpecker pipelines"
|
||||
|
||||
# Install system dependencies
|
||||
RUN apk add --no-cache \
|
||||
git \
|
||||
zip \
|
||||
unzip \
|
||||
libpng-dev \
|
||||
libjpeg-turbo-dev \
|
||||
freetype-dev \
|
||||
libzip-dev \
|
||||
icu-dev \
|
||||
icu-data-full \
|
||||
postgresql-dev \
|
||||
linux-headers \
|
||||
bash \
|
||||
curl
|
||||
|
||||
# Install build dependencies (temporary)
|
||||
RUN apk add --no-cache --virtual .build-deps \
|
||||
autoconf \
|
||||
g++ \
|
||||
make
|
||||
|
||||
# Configure and install PHP extensions
|
||||
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||
&& docker-php-ext-install -j$(nproc) \
|
||||
intl \
|
||||
pdo \
|
||||
pdo_pgsql \
|
||||
pgsql \
|
||||
zip \
|
||||
gd \
|
||||
pcntl \
|
||||
bcmath \
|
||||
opcache
|
||||
|
||||
# Install Redis extension
|
||||
RUN pecl install redis \
|
||||
&& docker-php-ext-enable redis
|
||||
|
||||
# Clean up build dependencies
|
||||
RUN apk del .build-deps \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Install Composer
|
||||
COPY --from=composer:2.8 /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Verify extensions are installed
|
||||
RUN php -m | grep -E "(intl|pdo_pgsql|gd|pcntl|redis|zip)" && echo "All extensions installed!"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -117,20 +117,14 @@ docker --version
|
||||
|
||||
```
|
||||
cannabrands-cluster/
|
||||
├── dev (namespace)
|
||||
│ ├── cannabrands deployment (1-2 pods)
|
||||
├── cannabrands-dev (namespace)
|
||||
│ ├── cannabrands-hub deployment (1 pod)
|
||||
│ ├── postgresql statefulset
|
||||
│ ├── redis deployment
|
||||
│ └── dev.cannabrands.app ingress
|
||||
│
|
||||
├── staging (namespace)
|
||||
│ ├── cannabrands deployment (2-3 pods)
|
||||
│ ├── postgresql statefulset
|
||||
│ ├── redis deployment
|
||||
│ └── staging.cannabrands.app ingress
|
||||
│
|
||||
└── production (namespace)
|
||||
├── cannabrands deployment (3+ pods, HPA enabled)
|
||||
└── cannabrands-prod (namespace)
|
||||
├── cannabrands-hub deployment (3+ pods, HPA enabled)
|
||||
├── postgresql statefulset (with backups)
|
||||
├── redis deployment (with persistence)
|
||||
└── cannabrands.app ingress (HTTPS)
|
||||
@@ -142,8 +136,7 @@ cannabrands-cluster/
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Ingress (NGINX) │
|
||||
│ - dev.cannabrands.app │
|
||||
│ - staging.cannabrands.app │
|
||||
│ - cannabrands.app (HTTPS) │
|
||||
│ - cannabrands.app (HTTPS) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
# Kubernetes Deployment Guide
|
||||
|
||||
Complete guide for deploying Cannabrands CRM to Kubernetes across dev, staging, and production environments.
|
||||
Complete guide for deploying Cannabrands CRM to Kubernetes.
|
||||
|
||||
## 2-Environment Architecture
|
||||
|
||||
Optimized for small teams:
|
||||
|
||||
```
|
||||
localhost (Sail) → develop branch → dev.cannabrands.app
|
||||
↓
|
||||
PR to master
|
||||
↓
|
||||
cannabrands.app (production)
|
||||
```
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -8,12 +20,11 @@ Complete guide for deploying Cannabrands CRM to Kubernetes across dev, staging,
|
||||
2. [Prerequisites](#prerequisites)
|
||||
3. [Initial Setup](#initial-setup)
|
||||
4. [Deploying to Development](#deploying-to-development)
|
||||
5. [Deploying to Staging](#deploying-to-staging)
|
||||
6. [Deploying to Production](#deploying-to-production)
|
||||
7. [Managing Secrets](#managing-secrets)
|
||||
8. [Database Management](#database-management)
|
||||
9. [Troubleshooting](#troubleshooting)
|
||||
10. [Maintenance Tasks](#maintenance-tasks)
|
||||
5. [Deploying to Production](#deploying-to-production)
|
||||
6. [Managing Secrets](#managing-secrets)
|
||||
7. [Database Management](#database-management)
|
||||
8. [Troubleshooting](#troubleshooting)
|
||||
9. [Maintenance Tasks](#maintenance-tasks)
|
||||
|
||||
---
|
||||
|
||||
@@ -33,19 +44,13 @@ k8s/
|
||||
│ └── kustomization.yaml # Base kustomization
|
||||
│
|
||||
└── overlays/ # Environment-specific configs
|
||||
├── dev/ # Development overrides
|
||||
├── dev/ # Development (dev.cannabrands.app)
|
||||
│ ├── kustomization.yaml
|
||||
│ ├── deployment-patch.yaml
|
||||
│ ├── ingress-patch.yaml
|
||||
│ └── postgres-patch.yaml
|
||||
│
|
||||
├── staging/ # Staging overrides
|
||||
│ ├── kustomization.yaml
|
||||
│ ├── deployment-patch.yaml
|
||||
│ ├── ingress-patch.yaml
|
||||
│ └── postgres-patch.yaml
|
||||
│
|
||||
└── production/ # Production overrides
|
||||
└── production/ # Production (cannabrands.app)
|
||||
├── kustomization.yaml
|
||||
├── deployment-patch.yaml
|
||||
├── ingress-patch.yaml
|
||||
@@ -54,16 +59,16 @@ k8s/
|
||||
|
||||
### Environment Comparison
|
||||
|
||||
| Resource | Development | Staging | Production |
|
||||
|----------|------------|---------|------------|
|
||||
| **Namespace** | `development` | `staging` | `production` |
|
||||
| **Domain** | dev.cannabrands.app | staging.cannabrands.app | cannabrands.app |
|
||||
| **App Replicas** | 1 | 2 | 3 |
|
||||
| **DB Replicas** | 1 | 1 | 2 (HA) |
|
||||
| **DB Storage** | 10Gi | 50Gi | 100Gi (SSD) |
|
||||
| **Docker Image** | `:dev` | `:staging` | `:latest` |
|
||||
| **Debug Mode** | Enabled | Disabled | Disabled |
|
||||
| **Log Level** | debug | info | warning |
|
||||
| Resource | Development | Production |
|
||||
|----------|-------------|------------|
|
||||
| **Namespace** | `cannabrands-dev` | `cannabrands-prod` |
|
||||
| **Domain** | dev.cannabrands.app | cannabrands.app |
|
||||
| **App Replicas** | 1 | 3 |
|
||||
| **DB Storage** | 10Gi | 100Gi (SSD) |
|
||||
| **Docker Image** | `dev-{SHA}` | `prod-{SHA}` |
|
||||
| **Debug Mode** | Enabled | Disabled |
|
||||
| **Log Level** | debug | warning |
|
||||
| **Seeders** | Run on deploy | Never |
|
||||
|
||||
---
|
||||
|
||||
@@ -255,49 +260,11 @@ Visit: https://dev.cannabrands.app
|
||||
|
||||
---
|
||||
|
||||
## Deploying to Staging
|
||||
|
||||
### 1. Deploy Staging Environment
|
||||
|
||||
```bash
|
||||
kubectl apply -k k8s/overlays/staging
|
||||
```
|
||||
|
||||
### 2. Verify Deployment
|
||||
|
||||
```bash
|
||||
kubectl get all -n staging
|
||||
kubectl get pods -n staging
|
||||
```
|
||||
|
||||
### 3. Restore Sanitized Production Data
|
||||
|
||||
Follow the database strategy guide in `docs/DATABASE_STRATEGY.md`:
|
||||
|
||||
```bash
|
||||
# Run sanitization script (see docs/DATABASE_STRATEGY.md)
|
||||
./scripts/sanitize-production-for-staging.sh
|
||||
```
|
||||
|
||||
### 4. Run Migrations
|
||||
|
||||
```bash
|
||||
kubectl exec -it deployment/app -n staging -- php artisan migrate --force
|
||||
```
|
||||
|
||||
### 5. Access Staging Site
|
||||
|
||||
Point DNS for `staging.cannabrands.app` to cluster ingress IP.
|
||||
|
||||
Visit: https://staging.cannabrands.app
|
||||
|
||||
---
|
||||
|
||||
## Deploying to Production
|
||||
|
||||
### 1. Pre-Deployment Checklist
|
||||
|
||||
- [ ] Staging is stable and tested
|
||||
- [ ] Changes tested on dev.cannabrands.app
|
||||
- [ ] All tests passing in CI
|
||||
- [ ] Production secrets created and verified
|
||||
- [ ] Database backup completed (if upgrading)
|
||||
|
||||
@@ -8,9 +8,29 @@ spec:
|
||||
spec:
|
||||
initContainers:
|
||||
- name: migrate
|
||||
# Image tag updated by CI/CD pipeline via kubectl set image
|
||||
# Format: prod-{SHA} (e.g., prod-abc1234)
|
||||
image: code.cannabrands.app/cannabrands/hub:latest
|
||||
command: ["/bin/sh", "-c"]
|
||||
args:
|
||||
- |
|
||||
echo "Publishing vendor assets..."
|
||||
php artisan vendor:publish --tag=public --force
|
||||
echo "Publishing Filament assets..."
|
||||
php artisan filament:assets
|
||||
echo "Optimizing Laravel..."
|
||||
php artisan optimize
|
||||
echo "Optimizing Filament..."
|
||||
php artisan filament:optimize
|
||||
echo "Setting up storage directories..."
|
||||
mkdir -p /var/www/html/storage/framework/{sessions,views,cache}
|
||||
chmod -R 775 /var/www/html/storage
|
||||
echo "Running migrations..."
|
||||
php artisan migrate --force
|
||||
echo "Init container complete!"
|
||||
containers:
|
||||
- name: app
|
||||
# Image tag updated by CI/CD pipeline via kubectl set image
|
||||
image: code.cannabrands.app/cannabrands/hub:latest
|
||||
resources:
|
||||
requests:
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: cannabrands-hub
|
||||
spec:
|
||||
replicas: 2 # 2 replicas for staging
|
||||
template:
|
||||
spec:
|
||||
initContainers:
|
||||
- name: migrate
|
||||
image: code.cannabrands.app/cannabrands/hub:staging
|
||||
containers:
|
||||
- name: app
|
||||
image: code.cannabrands.app/cannabrands/hub:staging
|
||||
@@ -1,20 +0,0 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: cannabrands-hub
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- staging.cannabrands.app
|
||||
secretName: staging-tls-secret
|
||||
rules:
|
||||
- host: staging.cannabrands.app
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: cannabrands-hub
|
||||
port:
|
||||
number: 80
|
||||
@@ -1,31 +0,0 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: cannabrands-staging
|
||||
|
||||
resources:
|
||||
- ../../base
|
||||
|
||||
patches:
|
||||
- path: deployment-patch.yaml
|
||||
- path: ingress-patch.yaml
|
||||
- path: postgres-patch.yaml
|
||||
|
||||
configMapGenerator:
|
||||
- name: app-config
|
||||
behavior: merge
|
||||
literals:
|
||||
- APP_ENV=staging
|
||||
- APP_DEBUG=false
|
||||
- APP_URL=https://staging.cannabrands.app
|
||||
- ASSET_URL=https://staging.cannabrands.app
|
||||
- DB_HOST=postgres.cannabrands-staging.svc.cluster.local
|
||||
- DB_DATABASE=cannabrands_staging
|
||||
- REDIS_HOST=redis.cannabrands-staging.svc.cluster.local
|
||||
- LOG_LEVEL=info
|
||||
- TELESCOPE_ENABLED=true
|
||||
|
||||
- name: postgres-config
|
||||
behavior: merge
|
||||
literals:
|
||||
- database=cannabrands_staging
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: postgres
|
||||
spec:
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: postgres-storage
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 50Gi # Medium for staging
|
||||
Reference in New Issue
Block a user