Consolidate 3 pipelines into 1 unified gated-pipeline.yml

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-08 23:20:00 +00:00
parent 49cfffbb2f
commit 01b639b1e0
3 changed files with 1287 additions and 2025 deletions

View File

@@ -1,360 +0,0 @@
name: Development Assistance
on:
pull_request:
types: [opened, synchronize, ready_for_review]
issue_comment:
types: [created]
permissions:
contents: read
issues: write
pull-requests: write
jobs:
code-quality-feedback:
name: Continuous Quality Feedback
runs-on: ubuntu-latest
if: |
github.event_name == 'pull_request' && !github.event.pull_request.draft
defaults:
run:
working-directory: frontends/nextjs
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Analyze code metrics (no redundant checks)
id: quality
run: |
# Note: Lint/build/tests are handled by gated-ci.yml
# This job only collects metrics for architectural feedback
# Count TypeScript files and their sizes
TOTAL_TS_FILES=$(find src -name "*.ts" -o -name "*.tsx" 2>/dev/null | wc -l)
LARGE_FILES=$(find src -name "*.ts" -o -name "*.tsx" -exec wc -l {} \; 2>/dev/null | awk '$1 > 150 {print $2}' | wc -l)
echo "total_ts_files=$TOTAL_TS_FILES" >> $GITHUB_OUTPUT
echo "large_files=$LARGE_FILES" >> $GITHUB_OUTPUT
# Check for declarative vs imperative balance
JSON_FILES=$(find src packages -name "*.json" 2>/dev/null | wc -l)
LUA_SCRIPTS=$(find src packages -name "*.lua" 2>/dev/null | wc -l)
echo "json_files=$JSON_FILES" >> $GITHUB_OUTPUT
echo "lua_scripts=$LUA_SCRIPTS" >> $GITHUB_OUTPUT
- name: Check architectural compliance
id: architecture
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const path = require('path');
let issues = [];
let suggestions = [];
// Get changed files
let changedFiles = [];
if (context.eventName === 'pull_request') {
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
changedFiles = files.map(f => f.filename);
}
// Check for hardcoded components outside ui/
const hardcodedComponents = changedFiles.filter(f =>
f.endsWith('.tsx') &&
f.includes('src/components/') &&
!f.includes('src/components/ui/') &&
!f.includes('src/components/shared/') &&
!['RenderComponent', 'FieldRenderer', 'GenericPage'].some(g => f.includes(g))
);
if (hardcodedComponents.length > 0) {
suggestions.push(`Consider if these components could be declarative: ${hardcodedComponents.join(', ')}`);
}
// Check for database changes without seed data
const schemaChanged = changedFiles.some(f => f.includes('schema.prisma'));
const seedChanged = changedFiles.some(f => f.includes('seed'));
if (schemaChanged && !seedChanged) {
suggestions.push('Database schema changed but no seed data updates detected. Consider updating seed data.');
}
// Check for new routes without PageRoutes table updates
const routeFiles = changedFiles.filter(f => f.includes('Route') || f.includes('route'));
if (routeFiles.length > 0) {
suggestions.push('Route changes detected. Ensure PageRoutes table is updated for dynamic routing.');
}
// Check for large TypeScript files
const largeFiles = parseInt('${{ steps.quality.outputs.large_files }}');
if (largeFiles > 0) {
issues.push(`${largeFiles} TypeScript files exceed 150 lines. Consider breaking them into smaller components.`);
}
return { issues, suggestions };
- name: Provide development feedback
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const analysis = JSON.parse('${{ steps.architecture.outputs.result }}');
const totalFiles = parseInt('${{ steps.quality.outputs.total_ts_files }}');
const largeFiles = parseInt('${{ steps.quality.outputs.large_files }}');
const jsonFiles = parseInt('${{ steps.quality.outputs.json_files }}');
const luaScripts = parseInt('${{ steps.quality.outputs.lua_scripts }}');
let comment = `## 💻 Development Quality Feedback\n\n`;
comment += `### 📊 Code Metrics\n\n`;
comment += `- TypeScript files: ${totalFiles}\n`;
comment += `- Files >150 LOC: ${largeFiles} ${largeFiles > 0 ? '⚠️' : '✅'}\n`;
comment += `- JSON config files: ${jsonFiles}\n`;
comment += `- Lua scripts: ${luaScripts}\n`;
comment += `- Declarative ratio: ${((jsonFiles + luaScripts) / Math.max(totalFiles, 1) * 100).toFixed(1)}%\n\n`;
if (analysis.issues.length > 0) {
comment += `### ⚠️ Architectural Issues\n\n`;
analysis.issues.forEach(issue => comment += `- ${issue}\n`);
comment += '\n';
}
if (analysis.suggestions.length > 0) {
comment += `### 💡 Suggestions\n\n`;
analysis.suggestions.forEach(suggestion => comment += `- ${suggestion}\n`);
comment += '\n';
}
comment += `### 🎯 Project Goals Reminder\n\n`;
comment += `- **Declarative First:** Prefer JSON + Lua over TypeScript\n`;
comment += `- **Component Size:** Keep files under 150 LOC\n`;
comment += `- **Generic Renderers:** Use RenderComponent for dynamic components\n`;
comment += `- **Database-Driven:** Store configuration in database, not code\n`;
comment += `- **Package-Based:** Organize features as importable packages\n\n`;
comment += `**@copilot** can help refactor code to better align with these principles.\n\n`;
comment += `📖 See [Architecture Guidelines](/.github/copilot-instructions.md)`;
// Check if we already commented
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(c =>
c.user.type === 'Bot' && c.body.includes('Development Quality Feedback')
);
if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: comment
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: comment
});
}
copilot-interaction:
name: Handle Copilot Mentions
runs-on: ubuntu-latest
if: |
github.event_name == 'issue_comment' &&
contains(github.event.comment.body, '@copilot')
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Parse Copilot request
uses: actions/github-script@v7
with:
script: |
const comment = context.payload.comment.body.toLowerCase();
const issue = context.payload.issue;
let response = `## 🤖 Copilot Assistance\n\n`;
// Determine what the user is asking for
if (comment.includes('implement') || comment.includes('fix this')) {
response += `To implement this with Copilot assistance:\n\n`;
response += `1. **Create a branch:** \`git checkout -b feature/issue-${issue.number}\`\n`;
response += `2. **Use Copilot in your IDE** to generate code with context from:\n`;
response += ` - [Copilot Instructions](/.github/copilot-instructions.md)\n`;
response += ` - [PRD.md](/PRD.md)\n`;
response += ` - Existing package structure in \`/packages/\`\n`;
response += `3. **Follow the architectural principles:**\n`;
response += ` - Declarative over imperative\n`;
response += ` - Database-driven configuration\n`;
response += ` - Generic renderers vs hardcoded components\n`;
response += `4. **Test your changes:** \`npm run lint && npm run test:e2e\`\n`;
response += `5. **Create a PR** - The automated workflows will review it\n\n`;
}
if (comment.includes('review') || comment.includes('check')) {
response += `Copilot can review this through:\n\n`;
response += `- **Automated Code Review** workflow (runs on PRs)\n`;
response += `- **Development Assistance** workflow (runs on pushes)\n`;
response += `- **Planning & Design** workflow (runs on feature requests)\n\n`;
response += `Create a PR to trigger comprehensive review!\n\n`;
}
if (comment.includes('architecture') || comment.includes('design')) {
response += `### 🏗️ Architectural Guidance\n\n`;
response += `MetaBuilder follows these principles:\n\n`;
response += `1. **5-Level Architecture:** User → Admin → God → SuperGod levels\n`;
response += `2. **Multi-Tenant:** Isolated tenant instances with independent configs\n`;
response += `3. **Declarative Components:** JSON config + Lua scripts, not TSX\n`;
response += `4. **Package System:** Self-contained, importable feature bundles\n`;
response += `5. **Database-First:** All config in Prisma, not hardcoded\n\n`;
response += `📖 Full details: [PRD.md](/PRD.md)\n\n`;
}
if (comment.includes('test') || comment.includes('e2e')) {
response += `### 🧪 Testing with Copilot\n\n`;
response += `\`\`\`bash\n`;
response += `# Run E2E tests\n`;
response += `npm run test:e2e\n\n`;
response += `# Run with UI\n`;
response += `npm run test:e2e:ui\n\n`;
response += `# Run linter\n`;
response += `npm run lint\n`;
response += `\`\`\`\n\n`;
response += `Use Copilot in your IDE to:\n`;
response += `- Generate test cases based on user stories\n`;
response += `- Write Playwright selectors and assertions\n`;
response += `- Create mock data for tests\n\n`;
}
if (comment.includes('help') || (!comment.includes('implement') && !comment.includes('review') && !comment.includes('architecture') && !comment.includes('test'))) {
response += `### 🆘 How to Use Copilot\n\n`;
response += `Mention **@copilot** in comments with:\n\n`;
response += `- \`@copilot implement this\` - Get implementation guidance\n`;
response += `- \`@copilot review this\` - Request code review\n`;
response += `- \`@copilot architecture\` - Get architectural guidance\n`;
response += `- \`@copilot test this\` - Get testing guidance\n`;
response += `- \`@copilot fix this issue\` - Request automated fix\n\n`;
response += `**In your IDE:**\n`;
response += `- Use GitHub Copilot with context from [Copilot Instructions](/.github/copilot-instructions.md)\n`;
response += `- Reference the [PRD](/PRD.md) when prompting\n`;
response += `- Follow patterns from existing packages in \`/packages/\`\n\n`;
}
response += `---\n`;
response += `*This is an automated response. For detailed Copilot assistance, use the extension in your IDE with project context.*`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: response
});
suggest-refactoring:
name: Suggest Refactoring Opportunities
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && !github.event.pull_request.draft
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Analyze refactoring opportunities
uses: actions/github-script@v7
with:
script: |
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
let opportunities = [];
// Look for opportunities in changed files
for (const file of files) {
const patch = file.patch || '';
// Check for repeated code patterns
if (patch.split('\n').length > 100) {
opportunities.push({
file: file.filename,
type: 'Size',
suggestion: 'Large changeset - consider breaking into smaller PRs or extracting common utilities'
});
}
// Check for hardcoded values
if (patch.match(/['"][A-Z_]{3,}['"]\s*:/)) {
opportunities.push({
file: file.filename,
type: 'Configuration',
suggestion: 'Hardcoded constants detected - consider moving to database configuration'
});
}
// Check for new TSX components
if (file.filename.includes('components/') && file.filename.endsWith('.tsx') && file.status === 'added') {
opportunities.push({
file: file.filename,
type: 'Architecture',
suggestion: 'New component added - could this be implemented declaratively with JSON + Lua?'
});
}
// Check for inline styles or complex class strings
if (patch.includes('style={{') || patch.match(/className="[^"]{50,}"/)) {
opportunities.push({
file: file.filename,
type: 'Styling',
suggestion: 'Complex styling detected - consider extracting to theme configuration'
});
}
}
if (opportunities.length > 0) {
let comment = `## 🔄 Refactoring Opportunities\n\n`;
comment += `**@copilot** identified potential improvements:\n\n`;
const grouped = {};
opportunities.forEach(opp => {
if (!grouped[opp.type]) grouped[opp.type] = [];
grouped[opp.type].push(opp);
});
for (const [type, opps] of Object.entries(grouped)) {
comment += `### ${type}\n\n`;
opps.forEach(opp => {
comment += `- **${opp.file}**: ${opp.suggestion}\n`;
});
comment += '\n';
}
comment += `---\n`;
comment += `These are suggestions, not requirements. Consider them as part of continuous improvement.\n\n`;
comment += `Use **@copilot** in your IDE to help implement these refactorings.`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: comment
});
}

View File

@@ -1,617 +0,0 @@
name: Enterprise Gated Deployment
on:
push:
branches:
- main
- master
release:
types: [published]
workflow_dispatch:
inputs:
environment:
description: 'Target deployment environment'
required: true
type: choice
options:
- staging
- production
skip_tests:
description: 'Skip pre-deployment tests (emergency only)'
required: false
type: boolean
default: false
permissions:
contents: read
issues: write
pull-requests: write
deployments: write
# Enterprise Deployment with Environment Gates
# Staging: Automatic deployment after main branch push
# Production: Requires manual approval
jobs:
# ============================================================================
# Pre-Deployment Validation
# ============================================================================
pre-deployment-validation:
name: Pre-Deployment Checks
runs-on: ubuntu-latest
defaults:
run:
working-directory: frontends/nextjs
outputs:
has-breaking-changes: ${{ steps.breaking.outputs.has_breaking }}
deployment-environment: ${{ steps.determine-env.outputs.environment }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Determine target environment
id: determine-env
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT
elif [ "${{ github.event_name }}" == "release" ]; then
echo "environment=production" >> $GITHUB_OUTPUT
else
echo "environment=staging" >> $GITHUB_OUTPUT
fi
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm install
- name: Generate Prisma Client
run: npm run db:generate
env:
DATABASE_URL: file:./dev.db
- name: Validate database schema
run: npx prisma validate --schema=../../prisma/schema.prisma
env:
DATABASE_URL: file:./dev.db
- name: Check for breaking changes
id: breaking
uses: actions/github-script@v7
with:
script: |
const commits = await github.rest.repos.listCommits({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 10
});
let hasBreaking = false;
let breakingChanges = [];
for (const commit of commits.data) {
const message = commit.commit.message.toLowerCase();
if (message.includes('breaking') || message.includes('breaking:') || message.startsWith('!')) {
hasBreaking = true;
breakingChanges.push({
sha: commit.sha.substring(0, 7),
message: commit.commit.message.split('\n')[0]
});
}
}
core.setOutput('has_breaking', hasBreaking);
if (hasBreaking) {
console.log('⚠️ Breaking changes detected:');
breakingChanges.forEach(c => console.log(` - ${c.sha}: ${c.message}`));
core.warning('Breaking changes detected in recent commits');
}
- name: Security audit
run: npm audit --audit-level=moderate
continue-on-error: true
- name: Check package size
run: |
npm run build
SIZE=$(du -sm .next/ | cut -f1)
echo "Build size: ${SIZE}MB"
if [ $SIZE -gt 50 ]; then
echo "::warning::Build size is ${SIZE}MB (>50MB). Consider optimizing."
fi
# ============================================================================
# Staging Deployment (Automatic)
# ============================================================================
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: pre-deployment-validation
if: |
needs.pre-deployment-validation.outputs.deployment-environment == 'staging' &&
(github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.environment == 'staging'))
environment:
name: staging
url: https://staging.metabuilder.example.com
defaults:
run:
working-directory: frontends/nextjs
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm install
- name: Generate Prisma Client
run: npm run db:generate
env:
DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}
- name: Build for staging
run: npm run build
env:
DATABASE_URL: ${{ secrets.STAGING_DATABASE_URL }}
NEXT_PUBLIC_ENV: staging
- name: Deploy to staging
run: |
echo "🚀 Deploying to staging environment..."
echo "Build artifacts ready for deployment"
echo "Note: Replace this with actual deployment commands"
echo "Examples:"
echo " - docker build/push"
echo " - kubectl apply"
echo " - terraform apply"
echo " - vercel deploy"
- name: Run smoke tests
run: |
echo "🧪 Running smoke tests on staging..."
echo "Basic health checks:"
echo " ✓ Application starts"
echo " ✓ Database connection"
echo " ✓ API endpoints responding"
echo "Note: Implement actual smoke tests here"
- name: Post deployment summary
uses: actions/github-script@v7
with:
script: |
const summary = `## 🚀 Staging Deployment Successful
**Environment:** staging
**Commit:** ${context.sha.substring(0, 7)}
**Time:** ${new Date().toISOString()}
### Deployment Details
- ✅ Pre-deployment validation passed
- ✅ Build completed
- ✅ Deployed to staging
- ✅ Smoke tests passed
### Next Steps
- Monitor staging environment for issues
- Run integration tests
- Request QA validation
- If stable, promote to production with manual approval
**Staging URL:** https://staging.metabuilder.example.com
`;
console.log(summary);
# ============================================================================
# Production Deployment Gate (Manual Approval Required)
# ============================================================================
production-approval-gate:
name: Production Deployment Gate
runs-on: ubuntu-latest
needs: [pre-deployment-validation]
if: |
needs.pre-deployment-validation.outputs.deployment-environment == 'production' &&
(github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.environment == 'production'))
steps:
- name: Pre-production checklist
uses: actions/github-script@v7
with:
script: |
const hasBreaking = '${{ needs.pre-deployment-validation.outputs.has-breaking-changes }}' === 'true';
let checklist = `## 🚨 Production Deployment Gate
### Pre-Deployment Checklist
#### Automatic Checks
- ✅ All CI/CD gates passed
- ✅ Code merged to main branch
- ✅ Pre-deployment validation completed
${hasBreaking ? '- ⚠️ **Breaking changes detected** - review required' : '- ✅ No breaking changes detected'}
#### Manual Verification Required
- [ ] Staging environment validated
- [ ] QA sign-off received
- [ ] Database migrations reviewed
- [ ] Rollback plan prepared
- [ ] Monitoring alerts configured
- [ ] On-call engineer notified
${hasBreaking ? '- [ ] **Breaking changes documented and communicated**' : ''}
### Approval Process
This deployment requires manual approval from authorized personnel.
**To approve:** Use the GitHub Actions UI to approve this deployment.
**To reject:** Cancel the workflow run.
### Emergency Override
If this is an emergency hotfix, the skip_tests option was set to: ${{ inputs.skip_tests || false }}
`;
console.log(checklist);
if (hasBreaking) {
core.warning('Breaking changes detected - extra caution required for production deployment');
}
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: [pre-deployment-validation, production-approval-gate]
if: |
needs.pre-deployment-validation.outputs.deployment-environment == 'production' &&
(github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.environment == 'production'))
environment:
name: production
url: https://metabuilder.example.com
defaults:
run:
working-directory: frontends/nextjs
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm install
- name: Generate Prisma Client
run: npm run db:generate
env:
DATABASE_URL: ${{ secrets.PRODUCTION_DATABASE_URL }}
- name: Build for production
run: npm run build
env:
DATABASE_URL: ${{ secrets.PRODUCTION_DATABASE_URL }}
NEXT_PUBLIC_ENV: production
NODE_ENV: production
- name: Pre-deployment backup
run: |
echo "📦 Creating pre-deployment backup..."
echo "Note: Implement actual backup commands"
echo " - Database backup"
echo " - File system backup"
echo " - Configuration backup"
- name: Run database migrations
run: |
echo "🗄️ Running database migrations..."
echo "Note: Implement actual migration commands"
echo "npx prisma migrate deploy"
env:
DATABASE_URL: ${{ secrets.PRODUCTION_DATABASE_URL }}
- name: Deploy to production
run: |
echo "🚀 Deploying to production environment..."
echo "Build artifacts ready for deployment"
echo "Note: Replace this with actual deployment commands"
echo "Examples:"
echo " - docker build/push"
echo " - kubectl apply"
echo " - terraform apply"
echo " - vercel deploy --prod"
- name: Run smoke tests
run: |
echo "🧪 Running smoke tests on production..."
echo "Basic health checks:"
echo " ✓ Application starts"
echo " ✓ Database connection"
echo " ✓ API endpoints responding"
echo " ✓ Critical user flows working"
echo "Note: Implement actual smoke tests here"
- name: Post deployment summary
uses: actions/github-script@v7
with:
script: |
const hasBreaking = '${{ needs.pre-deployment-validation.outputs.has-breaking-changes }}' === 'true';
const summary = `## 🎉 Production Deployment Successful
**Environment:** production
**Commit:** ${context.sha.substring(0, 7)}
**Time:** ${new Date().toISOString()}
${hasBreaking ? '**⚠️ Contains Breaking Changes**' : ''}
### Deployment Details
- ✅ Manual approval received
- ✅ Pre-deployment validation passed
- ✅ Database migrations completed
- ✅ Build completed
- ✅ Deployed to production
- ✅ Smoke tests passed
### Post-Deployment Monitoring
- 🔍 Monitor error rates for 1 hour
- 📊 Check performance metrics
- 👥 Monitor user feedback
- 🚨 Keep rollback plan ready
**Production URL:** https://metabuilder.example.com
### Emergency Contacts
- On-call engineer: Check PagerDuty
- Rollback procedure: See docs/deployment/rollback.md
`;
console.log(summary);
// Create deployment tracking issue
const issue = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `🚀 Production Deployment - ${new Date().toISOString().split('T')[0]}`,
body: summary,
labels: ['deployment', 'production', 'monitoring']
});
console.log(`Created monitoring issue #${issue.data.number}`);
# ============================================================================
# Post-Deployment Monitoring
# ============================================================================
post-deployment-health:
name: Post-Deployment Health Check
runs-on: ubuntu-latest
needs: [pre-deployment-validation, deploy-staging, deploy-production]
if: always() && (needs.deploy-staging.result == 'success' || needs.deploy-production.result == 'success')
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Determine deployed environment
id: env
run: |
if [ "${{ needs.deploy-production.result }}" == "success" ]; then
echo "environment=production" >> $GITHUB_OUTPUT
else
echo "environment=staging" >> $GITHUB_OUTPUT
fi
- name: Wait for application warm-up
run: |
echo "⏳ Waiting 30 seconds for application to warm up..."
sleep 30
- name: Run health checks
run: |
ENV="${{ steps.env.outputs.environment }}"
echo "🏥 Running health checks for $ENV environment..."
echo ""
echo "Checking:"
echo " - Application availability"
echo " - Database connectivity"
echo " - API response times"
echo " - Error rates"
echo " - Memory usage"
echo " - CPU usage"
echo ""
echo "Note: Implement actual health check commands"
echo "Examples:"
echo " curl -f https://$ENV.metabuilder.example.com/api/health"
echo " npm run health-check --env=$ENV"
- name: Schedule 24h monitoring
uses: actions/github-script@v7
with:
script: |
const env = '${{ steps.env.outputs.environment }}';
const deploymentTime = new Date().toISOString();
console.log(`📅 Scheduling 24-hour monitoring for ${env} deployment`);
console.log(`Deployment time: ${deploymentTime}`);
console.log('');
console.log('Monitoring checklist:');
console.log(' - Hour 1: Active monitoring of error rates');
console.log(' - Hour 6: Check performance metrics');
console.log(' - Hour 24: Full health assessment');
console.log('');
console.log('Note: Set up actual monitoring alerts in your observability platform');
# ============================================================================
# Deployment Failure Handler - Prefer Roll Forward
# ============================================================================
deployment-failure-handler:
name: Handle Deployment Failure
runs-on: ubuntu-latest
needs: [pre-deployment-validation, deploy-production]
if: |
failure() &&
(needs.pre-deployment-validation.result == 'failure' || needs.deploy-production.result == 'failure')
steps:
- name: Determine failure stage
id: failure-stage
run: |
if [ "${{ needs.pre-deployment-validation.result }}" == "failure" ]; then
echo "stage=pre-deployment" >> $GITHUB_OUTPUT
echo "severity=low" >> $GITHUB_OUTPUT
else
echo "stage=production" >> $GITHUB_OUTPUT
echo "severity=high" >> $GITHUB_OUTPUT
fi
- name: Display roll-forward guidance
run: |
echo "⚡ DEPLOYMENT FAILURE DETECTED"
echo "================================"
echo ""
echo "Failure Stage: ${{ steps.failure-stage.outputs.stage }}"
echo "Severity: ${{ steps.failure-stage.outputs.severity }}"
echo ""
echo "🎯 RECOMMENDED APPROACH: ROLL FORWARD"
echo "────────────────────────────────────────"
echo ""
echo "Rolling forward is preferred because it:"
echo " ✅ Fixes the root cause permanently"
echo " ✅ Maintains forward progress"
echo " ✅ Builds team capability"
echo " ✅ Prevents recurrence"
echo ""
echo "Steps to roll forward:"
echo " 1. Review failure logs (link below)"
echo " 2. Identify and fix the root cause"
echo " 3. Test the fix locally"
echo " 4. Push fix to trigger new deployment"
echo ""
echo "⚠️ ROLLBACK ONLY IF:"
echo "────────────────────────"
echo " • Production is actively broken"
echo " • Users are experiencing outages"
echo " • Critical security vulnerability"
echo " • Data integrity at risk"
echo ""
if [ "${{ steps.failure-stage.outputs.stage }}" == "pre-deployment" ]; then
echo "✅ GOOD NEWS: Failure occurred pre-deployment"
echo " → Production is NOT affected"
echo " → Safe to fix and retry"
echo " → No rollback needed"
else
echo "🚨 Production deployment failed"
echo " → Assess production impact immediately"
echo " → Check monitoring dashboards"
echo " → Verify user-facing functionality"
fi
- name: Create fix-forward issue
uses: actions/github-script@v7
with:
script: |
const stage = '${{ steps.failure-stage.outputs.stage }}';
const severity = '${{ steps.failure-stage.outputs.severity }}';
const isProd = stage === 'production';
const title = isProd
? '🚨 Production Deployment Failed - Fix Required'
: '⚠️ Pre-Deployment Validation Failed';
const body = `## Deployment Failure - ${stage === 'production' ? 'Production' : 'Pre-Deployment'}
**Time:** ${new Date().toISOString()}
**Commit:** ${context.sha.substring(0, 7)}
**Workflow Run:** [View Logs](${context.payload.repository.html_url}/actions/runs/${context.runId})
**Failure Stage:** ${stage}
**Severity:** ${severity}
${!isProd ? '✅ **Good News:** Production is NOT affected. The failure occurred during pre-deployment checks.\n' : '🚨 **Alert:** Production deployment failed. Assess impact immediately.\n'}
### 🎯 Recommended Action: Roll Forward (Fix and Re-deploy)
Rolling forward is the preferred approach because it:
- ✅ Fixes the root cause permanently
- ✅ Maintains development momentum
- ✅ Prevents the same issue from recurring
- ✅ Builds team problem-solving skills
### 📋 Fix-Forward Checklist
- [ ] **Investigate:** Review [workflow logs](${context.payload.repository.html_url}/actions/runs/${context.runId})
- [ ] **Diagnose:** Identify root cause of failure
- [ ] **Fix:** Implement fix in a new branch/commit
- [ ] **Test:** Verify fix locally (run relevant tests/builds)
- [ ] **Deploy:** Push fix to trigger new deployment
- [ ] **Verify:** Monitor deployment and confirm success
- [ ] **Document:** Update this issue with resolution details
${isProd ? `
### 🚨 Production Impact Assessment
**Before proceeding, verify:**
- [ ] Check monitoring dashboards for errors/alerts
- [ ] Verify critical user flows are working
- [ ] Check application logs for issues
- [ ] Assess if immediate rollback is needed
` : ''}
### ⚠️ When to Rollback Instead
**Only rollback if:**
- 🔴 Production is actively broken with user impact
- 🔴 Critical security vulnerability exposed
- 🔴 Data integrity at risk
- 🔴 Cannot fix forward within acceptable timeframe
${isProd ? `
### 🔄 Rollback Procedure (if absolutely necessary)
1. **Re-run workflow** with previous stable commit SHA
2. **OR use manual rollback:**
- Rollback specific migration: \`npx prisma migrate resolve --rolled-back MIGRATION_NAME --schema=prisma/schema.prisma\`
- Deploy previous Docker image/build
- Restore from pre-deployment backup if needed
- ⚠️ Avoid \`prisma migrate reset\` in production (causes data loss)
3. **Notify:** Update team and status page
4. **Document:** Create post-mortem issue
See [Rollback Procedure](docs/deployment/rollback.md) for details.
` : `
### 💡 Common Pre-Deployment Failures
- **Prisma Generate:** Check schema.prisma syntax and DATABASE_URL
- **Build Failure:** Review TypeScript errors or missing dependencies
- **Test Failure:** Fix failing tests or update test snapshots
- **Lint Errors:** Run \`npm run lint:fix\` locally
`}
### 📚 Resources
- [Workflow Run Logs](${context.payload.repository.html_url}/actions/runs/${context.runId})
- [Commit Details](${context.payload.repository.html_url}/commit/${context.sha})
- [Deployment Documentation](docs/deployment/)
`;
const labels = isProd
? ['deployment', 'production', 'incident', 'high-priority', 'fix-forward']
: ['deployment', 'pre-deployment', 'ci-failure', 'fix-forward'];
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: title,
body: body,
labels: labels
});

File diff suppressed because it is too large Load Diff