# PreRegister — Development & Deployment Strategy ## Overview ``` ┌──────────────┐ git push ┌──────────────┐ deploy.sh ┌──────────────────┐ │ Local Mac │ ──────────────► │ Gitea │ ─────────────► │ VPS DirectAdmin │ │ (Cursor) │ │ (Git remote) │ │ (Production) │ │ │ │ │ │ │ │ Docker: │ │ Tags: │ │ Apache/PHP-FPM │ │ - MySQL │ │ v1.0.0 │ │ MySQL/MariaDB │ │ - phpMyAdmin│ │ v1.0.1 │ │ Node (fnm) │ │ - Mailpit │ │ v1.1.0 │ │ │ └──────────────┘ └──────────────┘ └──────────────────┘ ``` --- ## 1. Local Development (Mac + Cursor) ### Directory structure ``` ~/Projects/preregister/ ← Laravel project root ├── .cursorrules ├── .env ← local dev config (NOT committed) ├── .env.example ← template (committed) ├── .gitignore ├── docker-compose.yml ← local dev services ├── Makefile ├── deploy.sh ← deployment script (committed) ├── PreRegister-Development-Prompt.md └── ... ``` ### Daily workflow ```bash # Start of day cd ~/Projects/preregister docker compose up -d # Start MySQL, phpMyAdmin, Mailpit make dev # Start Laravel + Vite dev servers # Open Cursor, code away... # End of day git add -A git commit -m "feat: description of what you built" git push origin main ``` --- ## 2. Git Setup with Gitea ### Initial setup (once) ```bash cd ~/Projects/preregister # Initialize git git init git branch -M main # Add your Gitea remote git remote add origin https://your-gitea-server.nl/bert/preregister.git # First push git add -A git commit -m "feat: initial project setup" git push -u origin main ``` ### .gitignore additions Make sure these are in `.gitignore` (Laravel ships most of these, but verify): ```gitignore # Laravel defaults /vendor/ /node_modules/ /public/build/ /public/hot /public/storage /storage/*.key .env .env.backup .phpunit.result.cache Homestead.json Homestead.yaml npm-debug.log yarn-error.log # Docker mysql_data/ # IDE .idea/ .vscode/ *.swp # OS .DS_Store Thumbs.db # Compiled assets are built on the server /public/build/ ``` ### .env.example (committed, safe) ```env APP_NAME=PreRegister APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost:8000 DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3307 DB_DATABASE=preregister DB_USERNAME=preregister DB_PASSWORD=preregister QUEUE_CONNECTION=database MAIL_MAILER=smtp MAIL_HOST=127.0.0.1 MAIL_PORT=1026 ``` --- ## 3. Versioning Strategy Use **Semantic Versioning** (SemVer): `vMAJOR.MINOR.PATCH` | Version part | When to bump | Example | |---|---|---| | PATCH | Bug fixes, small tweaks | v1.0.0 → v1.0.1 | | MINOR | New feature, backward compatible | v1.0.1 → v1.1.0 | | MAJOR | Breaking changes, major rewrite | v1.1.0 → v2.0.0 | ### Creating a release When a version is ready to deploy: ```bash # 1. Make sure everything is committed and pushed git add -A git commit -m "fix: final changes for v1.0.0" git push origin main # 2. Tag the release git tag -a v1.0.0 -m "v1.0.0 - Initial release with pre-registration pages and Mailwizz sync" git push origin v1.0.0 ``` In Gitea you can also create releases via the web UI (Releases → New Release) which lets you add release notes. ### Branch strategy (keep it simple) ``` main ← production-ready code, always deployable ``` You're the sole developer — one branch is fine. If you ever want to experiment without risk: ```bash git checkout -b feature/some-experiment # ... work ... git checkout main git merge feature/some-experiment git branch -d feature/some-experiment ``` --- ## 4. VPS Server Setup (one-time) ### 4.1 SSH access ```bash # From your Mac, set up SSH key access (if not already) ssh-copy-id user@your-vps.nl # Test ssh user@your-vps.nl ``` ### 4.2 Install Node.js via fnm ```bash ssh user@your-vps.nl # Install fnm curl -fsSL https://fnm.vercel.app/install | bash # Add to shell profile (add to ~/.bashrc or ~/.bash_profile) echo 'eval "$(fnm env)"' >> ~/.bashrc source ~/.bashrc # Install Node 20 LTS fnm install 20 fnm default 20 # Verify node -v # v20.x.x npm -v # 10.x.x ``` ### 4.3 Install Composer (if not present) ```bash cd ~ curl -sS https://getcomposer.org/installer | php sudo mv composer.phar /usr/local/bin/composer composer --version ``` ### 4.4 Create the application directory ```bash # Project lives OUTSIDE public_html mkdir -p /home/username/preregister # Symlink public/ to the domain's document root # Adjust the path to match your DirectAdmin domain structure ln -sfn /home/username/preregister/public /home/username/domains/preregister.yourdomain.nl/public_html ``` ### 4.5 Clone the repository ```bash cd /home/username git clone https://your-gitea-server.nl/bert/preregister.git cd preregister # Install dependencies composer install --no-dev --optimize-autoloader npm ci # Build frontend assets npm run build # Create .env for production cp .env.example .env nano .env # Edit with production values (see section 5) # Generate app key php artisan key:generate # Run migrations php artisan migrate --force # Storage link php artisan storage:link # Cache everything php artisan config:cache php artisan route:cache php artisan view:cache ``` ### 4.6 Apache .htaccess Laravel ships with a `public/.htaccess` that works with Apache. Verify `mod_rewrite` is enabled (it usually is on DirectAdmin). ### 4.7 Set up cron for queue worker + scheduler In DirectAdmin → Cron Jobs, add: ``` # Laravel scheduler (every minute) * * * * * cd /home/username/preregister && php artisan schedule:run >> /dev/null 2>&1 # Queue worker - process one job per run (every minute) * * * * * cd /home/username/preregister && php artisan queue:work --once --queue=mailwizz >> /dev/null 2>&1 ``` ### 4.8 Directory permissions ```bash cd /home/username/preregister # Storage and cache must be writable by the web server chmod -R 775 storage bootstrap/cache chown -R username:username . # If Apache runs as a different user (e.g., apache, www-data, nobody): # Check with: ps aux | grep apache # Then set group accordingly: # chgrp -R apache storage bootstrap/cache ``` --- ## 5. Production .env ```env APP_NAME=PreRegister APP_ENV=production APP_KEY= # generated with php artisan key:generate APP_DEBUG=false # NEVER true in production APP_URL=https://preregister.yourdomain.nl LOG_CHANNEL=daily LOG_LEVEL=warning DB_CONNECTION=mysql DB_HOST=localhost DB_PORT=3306 DB_DATABASE=preregister_prod DB_USERNAME=preregister_prod DB_PASSWORD=STRONG_PASSWORD_HERE QUEUE_CONNECTION=database SESSION_DRIVER=database SESSION_LIFETIME=120 MAIL_MAILER=smtp MAIL_HOST=your-smtp-server MAIL_PORT=587 MAIL_USERNAME=your-smtp-user MAIL_PASSWORD=your-smtp-password MAIL_ENCRYPTION=tls MAIL_FROM_ADDRESS="noreply@yourdomain.nl" MAIL_FROM_NAME="${APP_NAME}" ``` Create the production database via DirectAdmin → MySQL Management: - Database: `preregister_prod` - User: `preregister_prod` - Strong password --- ## 6. Deployment Script This is the script you run on the VPS to deploy a new version. Save this as `deploy.sh` in the project root (committed to git): ```bash #!/bin/bash set -e # ────────────────────────────────────────── # PreRegister Deploy Script # Run on VPS: ./deploy.sh [tag] # Examples: # ./deploy.sh → deploys latest main # ./deploy.sh v1.2.0 → deploys specific tag # ────────────────────────────────────────── APP_DIR="/home/username/preregister" TAG="${1:-}" echo "══════════════════════════════════════" echo " PreRegister — Deploy" echo "══════════════════════════════════════" cd "$APP_DIR" # 1. Maintenance mode echo "→ Enabling maintenance mode..." php artisan down --retry=30 || true # 2. Pull latest code echo "→ Pulling from Gitea..." git fetch --all --tags if [ -n "$TAG" ]; then echo "→ Checking out tag: $TAG" git checkout "$TAG" else echo "→ Checking out latest main" git checkout main git pull origin main fi # 3. Install PHP dependencies echo "→ Installing Composer dependencies..." composer install --no-dev --optimize-autoloader --no-interaction # 4. Install Node dependencies and build echo "→ Installing npm packages..." npm ci --production=false echo "→ Building frontend assets..." npm run build # 5. Run migrations echo "→ Running migrations..." php artisan migrate --force # 6. Clear and rebuild caches echo "→ Clearing caches..." php artisan config:cache php artisan route:cache php artisan view:cache php artisan event:cache # 7. Restart queue (process any pending jobs with new code) echo "→ Restarting queue workers..." php artisan queue:restart # 8. Storage link (idempotent) php artisan storage:link 2>/dev/null || true # 9. Disable maintenance mode echo "→ Going live!" php artisan up echo "" echo "══════════════════════════════════════" if [ -n "$TAG" ]; then echo " Deployed: $TAG" else echo " Deployed: main (latest)" fi echo "══════════════════════════════════════" ``` Make it executable: ```bash chmod +x deploy.sh ``` --- ## 7. Deployment Workflow (day-to-day) ### When a release is ready: **On your Mac:** ```bash # 1. Commit and push all changes git add -A git commit -m "feat: add subscriber export" git push origin main # 2. Tag the release git tag -a v1.0.0 -m "Initial release" git push origin v1.0.0 ``` **On the VPS (via SSH):** ```bash ssh user@your-vps.nl cd /home/username/preregister # Deploy a specific version ./deploy.sh v1.0.0 # Or deploy latest main (for quick fixes) ./deploy.sh ``` That's it. One command deploys everything: pulls code, installs dependencies, builds assets, runs migrations, clears caches, and brings the site back up. ### Quick reference ``` Local Gitea VPS ───── ───── ─── code in Cursor ↓ git commit + push ───► main branch ↓ git tag v1.x.x ───► tag stored ssh into VPS ↓ ./deploy.sh v1.x.x ↓ git pull + composer + npm + build + migrate ↓ live! ✓ ``` --- ## 8. Rollback Strategy If a deploy goes wrong: ```bash # Deploy the previous known-good tag ./deploy.sh v1.0.0 # If migrations need to be rolled back (careful!) php artisan migrate:rollback --step=1 ``` To see available tags: ```bash git tag -l --sort=-version:refname ``` --- ## 9. File Overview | File | Committed | Location | Purpose | |---|---|---|---| | `.env` | NO | local + VPS | Environment-specific config | | `.env.example` | YES | repo | Template for .env | | `.cursorrules` | YES | repo | Cursor AI coding rules | | `.gitignore` | YES | repo | Files excluded from git | | `docker-compose.yml` | YES | repo | Local dev services | | `Makefile` | YES | repo | Local dev shortcuts | | `deploy.sh` | YES | repo | VPS deployment script | | `PreRegister-Development-Prompt.md` | YES | repo | Full app specification | | `public/build/` | NO | built on VPS | Compiled CSS/JS | | `vendor/` | NO | installed on VPS | PHP dependencies | | `node_modules/` | NO | installed on VPS | Node dependencies | --- ## 10. Checklist: First Production Deploy - [ ] VPS: SSH key access configured - [ ] VPS: PHP 8.2+ selected in DirectAdmin MultiPHP - [ ] VPS: Node.js 20 installed via fnm - [ ] VPS: Composer installed globally - [ ] VPS: Production database created in DirectAdmin - [ ] VPS: Project directory created, public_html symlinked - [ ] VPS: Repository cloned from Gitea - [ ] VPS: `.env` configured with production values - [ ] VPS: `php artisan key:generate` run - [ ] VPS: `deploy.sh` path updated (APP_DIR) - [ ] VPS: First `./deploy.sh` run successfully - [ ] VPS: Cron jobs added (scheduler + queue worker) - [ ] VPS: Permissions set on storage/ and bootstrap/cache/ - [ ] VPS: SSL certificate active (Let's Encrypt via DirectAdmin) - [ ] VPS: Test the public URL in browser - [ ] VPS: Test login at /admin - [ ] VPS: Change the default superadmin password