
staging-deployment-phase
Deploys features to staging environment with database migrations, health checks, and deployment veri
<quick_start> <staging_deployment_workflow> Prerequisite-driven staging deployment:
- Verify PR merged: Confirm pull request merged to main branch
- Run migrations: Execute database migrations if schema changes exist
- Trigger deployment: Deploy to staging environment via CI/CD or manual trigger
- Monitor deployment: Watch deployment logs and progress
- Run health checks: Verify all services and endpoints operational
- Generate report: Create staging-ship-report.md with deployment metadata
Example workflow:
Verifying PR merged... ✅
Merge commit: abc123f "Merge pull request #47"
Checking for migrations...
Found: migrations/2025_11_add_user_profile.sql
Running migrations... ✅
Triggering staging deployment...
Platform: Vercel/Netlify/Railway
Deployment ID: dpl_abc123
Status: Building... → Deploying... → Live ✅
Running health checks...
API: https://staging.example.com/health → 200 OK ✅
Database: Connected ✅
Cache: Operational ✅
Staging deployment successful!
Ship report: specs/NNN-slug/staging-ship-report.md
Staging URL: https://staging.example.com
</staging_deployment_workflow>
<trigger_conditions> Auto-invoke when:
/ship-stagingcommand executed- User mentions "deploy to staging", "ship to staging", "staging deployment"
- state.yaml shows current_phase: ship-staging
- Deployment model is staging-prod (not direct-prod or local-only)
Prerequisites:
- PR successfully merged to main branch
- CI/CD passing on main branch
- Staging environment configured
- Database migrations prepared (if needed) </trigger_conditions> </quick_start>
Confirm pull request successfully merged before deploying.
Validation:
# Check recent commits for merge commit
git log --oneline -10 | grep "Merge pull request"
# Verify feature branch merged
FEATURE_SLUG="user-profile-editing"
git log --oneline --all | grep -i "$FEATURE_SLUG"
# Confirm on main branch
CURRENT_BRANCH=$(git branch --show-current)
if [ "$CURRENT_BRANCH" != "main" ]; then
echo "❌ Not on main branch (currently on $CURRENT_BRANCH)"
echo "Switch to main: git checkout main && git pull"
exit 1
fi
echo "✅ PR merged to main"
Blocking conditions:
- Not on main branch → Switch to main first
- No recent merge commit → Feature branch not merged yet
- CI failing on main → Fix CI before deploying
Output:
✅ PR merged to main
Merge commit: abc123f "Merge pull request #47: Add user profile editing"
Author: @username
Merged: 2 minutes ago
</step>
<step number="2" name="run_database_migrations">
**2. Run Database Migrations**
Execute database migrations if schema changes exist.
Detection:
# Check for migration files (Node.js example)
if [ -d "migrations" ] && [ -n "$(ls -A migrations/*.sql 2>/dev/null)" ]; then
echo "✅ Migrations detected"
# List migrations
ls -1 migrations/*.sql
# Run migrations
npm run migrate:staging
# Or with migration tool
# knex migrate:latest --env staging
# prisma migrate deploy --schema=./schema.prisma
# Verify migrations applied
echo "✅ Migrations completed"
else
echo "ℹ️ No migrations to run"
fi
Error handling:
# If migration fails
if [ $? -ne 0 ]; then
echo "❌ Migration failed"
echo "Review migration logs for errors"
echo "DO NOT proceed with deployment"
echo ""
echo "Common issues:"
echo " - Syntax errors in SQL"
echo " - Constraint violations"
echo " - Missing columns in dependent tables"
exit 1
fi
Rollback procedure:
# If deployment fails after migration
# Rollback migrations
npm run migrate:rollback:staging
# Or manually revert last migration
# psql -h staging-db.example.com -d mydb -f migrations/rollback/2025_11_revert.sql
</step>
<step number="3" name="trigger_staging_deployment">
**3. Trigger Staging Deployment**
Deploy to staging environment based on deployment platform.
Platform-specific commands:
Vercel:
# Deploy to staging (preview environment)
vercel --yes
# Or trigger via git push
git push origin main
# Vercel auto-deploys main → staging
Netlify:
# Deploy to staging environment
netlify deploy --prod=false
# Or via git push to staging branch
git push origin main:staging
Railway:
# Trigger deployment
railway up --environment staging
GitHub Actions:
# Trigger workflow
gh workflow run deploy-staging.yml
# Monitor workflow
gh run watch
Docker/Custom:
# Build and deploy
docker build -t myapp:staging .
docker push registry.example.com/myapp:staging
# SSH to staging server
ssh staging.example.com
docker pull registry.example.com/myapp:staging
docker-compose up -d
Monitor deployment:
# Get deployment ID/URL
DEPLOYMENT_ID="dpl_abc123xyz"
STAGING_URL="https://staging.example.com"
echo "Deployment ID: $DEPLOYMENT_ID"
echo "Staging URL: $STAGING_URL"
echo ""
echo "Monitoring deployment..."
# Platform-specific monitoring commands
</step>
<step number="4" name="run_health_checks">
**4. Run Health Checks**
Verify all services and endpoints operational after deployment.
Critical checks:
echo "Running health checks..."
# Check 1: API health endpoint
API_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$STAGING_URL/health")
if [ "$API_STATUS" = "200" ]; then
echo " ✅ API: $API_STATUS OK"
else
echo " ❌ API: $API_STATUS FAILED"
HEALTH_FAILED=true
fi
# Check 2: Database connectivity
DB_STATUS=$(curl -s "$STAGING_URL/health" | jq -r '.database')
if [ "$DB_STATUS" = "connected" ]; then
echo " ✅ Database: Connected"
else
echo " ❌ Database: $DB_STATUS"
HEALTH_FAILED=true
fi
# Check 3: Cache/Redis
CACHE_STATUS=$(curl -s "$STAGING_URL/health" | jq -r '.cache')
if [ "$CACHE_STATUS" = "operational" ]; then
echo " ✅ Cache: Operational"
else
echo " ❌ Cache: $CACHE_STATUS"
HEALTH_FAILED=true
fi
# Check 4: Version matches
DEPLOYED_VERSION=$(curl -s "$STAGING_URL/api/version" | jq -r '.version')
EXPECTED_VERSION=$(node -p "require('./package.json').version")
if [ "$DEPLOYED_VERSION" = "$EXPECTED_VERSION" ]; then
echo " ✅ Version: $DEPLOYED_VERSION"
else
echo " ⚠️ Version mismatch: deployed=$DEPLOYED_VERSION, expected=$EXPECTED_VERSION"
fi
# Fail if any health check failed
if [ "$HEALTH_FAILED" = "true" ]; then
echo ""
echo "❌ Health checks failed - deployment may be broken"
echo "Review logs and consider rollback"
exit 1
fi
echo ""
echo "✅ All health checks passed"
Health check endpoints:
/health- Overall system health/api/version- Deployed version/api/status- Detailed service status/api/db/ping- Database connectivity </step>
Create staging-ship-report.md with deployment metadata.
Report generation:
FEATURE_SLUG="user-profile-editing"
REPORT_PATH="specs/001-$FEATURE_SLUG/staging-ship-report.md"
cat > "$REPORT_PATH" <<EOF
# Staging Ship Report: $FEATURE_SLUG
## Deployment Summary
- **Deployed at**: $(date -Iseconds)
- **Deployment ID**: $DEPLOYMENT_ID
- **Staging URL**: $STAGING_URL
- **Branch**: main
- **Commit**: $(git rev-parse HEAD)
- **Author**: $(git log -1 --format='%an <%ae>')
## Health Checks
- API endpoints: ✅ All passing
- Database: ✅ Connected
- Cache: ✅ Operational
- Version: ✅ $DEPLOYED_VERSION
## Database Migrations
$(if [ -d "migrations" ]; then
echo "- Migrations run: $(ls migrations/*.sql | wc -l) files"
ls -1 migrations/*.sql | sed 's/^/ - /'
else
echo "- No migrations required"
fi)
## Next Steps
- Manual validation: Test feature on staging environment
- Run: /validate-staging to approve for production
- Staging URL: $STAGING_URL
## Rollback Procedure
1. Revert deployment: [platform-specific command]
2. Rollback migrations (if any): npm run migrate:rollback:staging
3. Verify rollback successful
---
Generated by /ship-staging on $(date)
EOF
echo "✅ Ship report generated: $REPORT_PATH"
Update workflow state:
# Update state.yaml
STATE_FILE="specs/001-$FEATURE_SLUG/state.yaml"
# Add deployment metadata
yq eval ".deployment.staging.url = \"$STAGING_URL\"" -i "$STATE_FILE"
yq eval ".deployment.staging.deployment_id = \"$DEPLOYMENT_ID\"" -i "$STATE_FILE"
yq eval ".deployment.staging.deployed_at = \"$(date -Iseconds)\"" -i "$STATE_FILE"
yq eval ".current_phase = \"validate-staging\"" -i "$STATE_FILE"
echo "✅ Workflow state updated"
</step>
<step number="6" name="display_summary">
**6. Display Deployment Summary**
Show deployment results and next steps.
Summary format:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🚀 Staging Deployment Successful!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Staging URL: https://staging.example.com
Deployment ID: dpl_abc123xyz
Deployed at: 2025-11-19T22:45:00Z
Health Checks: ✅ All passing
- API: 200 OK
- Database: Connected
- Cache: Operational
- Version: 2.7.0
Ship Report: specs/001-user-profile-editing/staging-ship-report.md
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Next: Manual Validation
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Test feature on staging: https://staging.example.com
2. Verify all acceptance criteria met
3. Run: /validate-staging to approve for production
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
</step>
</workflow>
<anti_patterns> Avoid these staging deployment mistakes:
<pitfall name="deploy_without_pr_merge_verification"> **❌ Deploying without verifying PR merged** ```bash # BAD: Skip merge check, deploy immediately /ship-staging # May deploy incomplete feature if PR not merged ``` **✅ Always verify PR merged before deploying** ```bash # GOOD: Check for merge commit git log --oneline -10 | grep "Merge pull request" # Confirm merge exists, then deploy ``` **Impact**: Deploying feature branch code instead of merged code, missing integration changes **Prevention**: Always check git log for merge commit before triggering deployment </pitfall> <pitfall name="skip_database_migrations"> **❌ Deploying with schema changes without running migrations** ```bash # BAD: Added new column to User table, forgot to run migration git push origin main # Deploys code expecting new column # Staging crashes: "column 'profile_picture' does not exist" ``` **✅ Always check for and run migrations before deployment** ```bash # GOOD: Check for migration files if [ -d "migrations" ] && [ -n "$(ls migrations/*.sql)" ]; then npm run migrate:staging # Run migrations first fi # Then deploy code ``` **Impact**: Application crashes due to schema mismatch, broken staging environment **Prevention**: Make migrations part of deployment checklist, run before code deployment </pitfall> <pitfall name="ignore_health_check_failures"> **❌ Assuming deployment succeeded without verification** ```bash # BAD: Deploy and assume success vercel --yes echo "Deployed!" # But didn't check if it actually works ``` **✅ Always run health checks after deployment** ```bash # GOOD: Verify deployment with health checks vercel --yes sleep 10 # Wait for deployment to stabilizeCheck health endpoint
curl -f https://staging.example.com/health || { echo "❌ Health check failed - deployment broken" exit 1 }
**Impact**: Broken staging deployment goes unnoticed, blocks validation
**Prevention**: Always run automated health checks, verify 200 status and service health
</pitfall>
<pitfall name="deploy_on_failing_ci">
**❌ Deploying when CI is failing on main**
```bash
# BAD: CI shows red, deploy anyway
# "Tests failing but it's probably fine..."
/ship-staging
✅ Always wait for CI to pass before deploying
# GOOD: Check CI status first
gh run list --branch main --limit 1 --json conclusion --jq '.[0].conclusion'
# If "success", proceed with deployment
# If "failure", fix CI first
Impact: Deploying broken code to staging, wasting time debugging obvious failures Prevention: Make CI passing a prerequisite gate for staging deployment </pitfall>
<pitfall name="no_rollback_plan"> **❌ Deploying without knowing how to rollback** ```bash # BAD: Deploy, hope it works vercel --yes # If broken, panic and don't know how to revert ``` **✅ Document rollback procedure before deploying** ```bash # GOOD: Know rollback command for your platform # Vercel: vercel rollback <deployment-url> # Netlify: netlify rollback # Railway: railway rollbackTest rollback works during /ship-staging
Before /ship-prod
**Impact**: Unable to recover quickly from broken deployment, extended downtime
**Prevention**: Document and test rollback procedure, include in ship report
</pitfall>
<pitfall name="missing_deployment_metadata">
**❌ Not recording deployment details**
```bash
# BAD: Deploy but don't save deployment ID, URL, timestamp
vercel --yes
# Later: "What version is on staging? When was it deployed?"
✅ Always generate ship report with metadata
# GOOD: Capture and save deployment info
DEPLOYMENT_ID=$(vercel --yes | grep -oP 'https://[^ ]+')
echo "Deployment ID: $DEPLOYMENT_ID" >> staging-ship-report.md
echo "Deployed at: $(date -Iseconds)" >> staging-ship-report.md
Impact: No audit trail, can't correlate staging issues with deployments Prevention: Always generate staging-ship-report.md with full metadata </pitfall> </anti_patterns>
<success_criteria> Staging deployment successful when:
- ✓ PR successfully merged to main branch (verified via git log)
- ✓ CI/CD passing on main branch
- ✓ Database migrations completed without errors (if applicable)
- ✓ Staging deployment triggered and completed successfully
- ✓ Deployment ID/URL captured
- ✓ All health check endpoints return 200 status
- ✓ API, database, cache services confirmed operational
- ✓ Deployed version matches expected version
- ✓ staging-ship-report.md generated in specs/NNN-slug/ directory
- ✓ state.yaml updated with staging deployment metadata
- ✓ Next action displayed (/validate-staging)
Quality gates passed:
- CI green on main branch before deployment
- No migration errors during schema updates
- All health checks passing post-deployment
- No critical errors in deployment logs
- Rollback procedure documented and tested </success_criteria>