staging-deployment-phase

Deploys features to staging environment with database migrations, health checks, and deployment verification. Use during /ship-staging phase or when deploying to staging environments for validation before production release.

$ Installer

git clone https://github.com/marcusgoll/Spec-Flow /tmp/Spec-Flow && cp -r /tmp/Spec-Flow/.claude/skills/staging-deployment-phase ~/.claude/skills/Spec-Flow

// tip: Run this command in your terminal to install the skill


name: staging-deployment-phase description: Deploys features to staging environment with database migrations, health checks, and deployment verification. Use during /ship-staging phase or when deploying to staging environments for validation before production release.

<quick_start> <staging_deployment_workflow> Prerequisite-driven staging deployment:

  1. Verify PR merged: Confirm pull request merged to main branch
  2. Run migrations: Execute database migrations if schema changes exist
  3. Trigger deployment: Deploy to staging environment via CI/CD or manual trigger
  4. Monitor deployment: Watch deployment logs and progress
  5. Run health checks: Verify all services and endpoints operational
  6. 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-staging command 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

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

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

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

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"

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

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

<anti_patterns> Avoid these staging deployment mistakes:

Check 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

Test 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 </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>