mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
Consolidate 3 pipelines into 1 unified gated-pipeline.yml
Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
360
.github/workflows/development.yml
vendored
360
.github/workflows/development.yml
vendored
@@ -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
|
||||
});
|
||||
}
|
||||
617
.github/workflows/gated-deployment.yml
vendored
617
.github/workflows/gated-deployment.yml
vendored
@@ -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
Reference in New Issue
Block a user