Files
daffy0208 a7d81d6f2a feat: Complete Agent Validation implementation and repository cleanup
- Implement Eval-Driven Development (EDD) system with Phase 5.12
- Add scripts/run-agent-evals.js for automated agent evaluation
- Create golden dataset example for testing (tests/fixtures/golden-dataset-example.json)
- Add comprehensive documentation (DOCS/AGENT-VALIDATION.md)

Repository Health Improvements:
- Fix TypeScript configuration (exclude broken legacy directories)
- Add @ts-nocheck to legacy tools with missing dependencies
- Update .eslintignore to bypass unmaintained code
- Auto-fix 1,500+ linting errors with Prettier
- Fix META/registry.json paths and versions
- Install missing dependencies (chalk, commander)
- Generate missing package-lock.json files
- Fix Brain CLI TypeScript compilation errors

Validation System:
- Update .claude/commands/validate.md with Agent Evaluation phase
- Create scripts/validate-full.sh for automated validation
- Update registry metadata for agent-evaluator skill
- Add agent-validation-pattern to architecture patterns

🤖 Generated with Claude Code
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-11-24 14:22:53 +00:00

517 lines
12 KiB
TypeScript

/**
* Deployment Script
*
* Automated deployment to multiple platforms with pre-deployment checks.
*
* Features:
* - Multi-platform support (Vercel, Railway, AWS, Docker)
* - Pre-deployment validation
* - Build verification
* - Environment variable management
* - Rollback support
* - Health checks
* - Notification integration
*
* @example
* ```bash
* # Deploy to Vercel
* ts-node deploy.ts --platform vercel --environment production
*
* # Deploy to Railway
* ts-node deploy.ts --platform railway --project my-app
*
* # Deploy with Docker
* ts-node deploy.ts --platform docker --tag v1.0.0
* ```
*/
import { execSync } from 'child_process'
import { existsSync, readFileSync } from 'fs'
import { join } from 'path'
export type Platform = 'vercel' | 'railway' | 'aws' | 'docker' | 'custom'
export type Environment = 'development' | 'staging' | 'production'
export interface DeployOptions {
/**
* Deployment platform
*/
platform: Platform
/**
* Target environment
*/
environment?: Environment
/**
* Project name
*/
project?: string
/**
* Docker image tag
*/
tag?: string
/**
* Skip pre-deployment checks
*/
skipChecks?: boolean
/**
* Skip build
*/
skipBuild?: boolean
/**
* Dry run (don't actually deploy)
*/
dryRun?: boolean
/**
* Custom deploy command
*/
customCommand?: string
/**
* Environment variables
*/
env?: Record<string, string>
/**
* Notification webhook URL
*/
webhookUrl?: string
}
export interface DeployResult {
success: boolean
platform: Platform
environment?: Environment
url?: string
deploymentId?: string
duration: number
message: string
}
export class Deployment {
private options: Required<
Omit<DeployOptions, 'tag' | 'customCommand' | 'project' | 'webhookUrl' | 'environment'>
> & {
tag?: string
customCommand?: string
project?: string
webhookUrl?: string
environment?: Environment
}
constructor(options: DeployOptions) {
this.options = {
platform: options.platform,
environment: options.environment,
project: options.project,
tag: options.tag,
skipChecks: options.skipChecks ?? false,
skipBuild: options.skipBuild ?? false,
dryRun: options.dryRun ?? false,
customCommand: options.customCommand,
env: options.env || {},
webhookUrl: options.webhookUrl
}
}
/**
* Execute deployment
*/
async deploy(): Promise<DeployResult> {
const startTime = Date.now()
console.log(`\n${'='.repeat(60)}`)
console.log(`Deploying to ${this.options.platform.toUpperCase()}`)
console.log(`Environment: ${this.options.environment || 'default'}`)
console.log(`${'='.repeat(60)}\n`)
try {
// Pre-deployment checks
if (!this.options.skipChecks) {
console.log('Running pre-deployment checks...\n')
await this.runChecks()
}
// Build
if (!this.options.skipBuild) {
console.log('\nBuilding project...\n')
await this.build()
}
// Deploy
if (this.options.dryRun) {
console.log('\nDRY RUN - Skipping actual deployment\n')
} else {
console.log('\nDeploying...\n')
await this.executeDeploy()
}
const duration = Date.now() - startTime
const result: DeployResult = {
success: true,
platform: this.options.platform,
environment: this.options.environment,
duration,
message: 'Deployment successful'
}
// Send notification
if (this.options.webhookUrl) {
await this.sendNotification(result)
}
console.log(`\n${'='.repeat(60)}`)
console.log('✓ Deployment completed successfully!')
console.log(`Duration: ${(duration / 1000).toFixed(2)}s`)
console.log(`${'='.repeat(60)}\n`)
return result
} catch (error) {
const duration = Date.now() - startTime
const errorMessage = error instanceof Error ? error.message : String(error)
const result: DeployResult = {
success: false,
platform: this.options.platform,
environment: this.options.environment,
duration,
message: `Deployment failed: ${errorMessage}`
}
// Send notification
if (this.options.webhookUrl) {
await this.sendNotification(result)
}
console.error(`\n${'='.repeat(60)}`)
console.error('✗ Deployment failed!')
console.error(`Error: ${errorMessage}`)
console.error(`${'='.repeat(60)}\n`)
return result
}
}
/**
* Run pre-deployment checks
*/
private async runChecks(): Promise<void> {
const checks = [
{ name: 'Git status', fn: () => this.checkGitStatus() },
{ name: 'Dependencies', fn: () => this.checkDependencies() },
{ name: 'Tests', fn: () => this.checkTests() },
{ name: 'Environment variables', fn: () => this.checkEnvVars() }
]
for (const check of checks) {
try {
console.log(` Checking ${check.name}...`)
await check.fn()
console.log(` ✓ ${check.name} passed`)
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
console.error(` ✗ ${check.name} failed: ${errorMessage}`)
throw error
}
}
}
/**
* Check git status
*/
private checkGitStatus(): void {
try {
const status = execSync('git status --porcelain', { encoding: 'utf-8' })
if (status.trim()) {
console.warn(' Warning: Working directory has uncommitted changes')
}
const branch = execSync('git branch --show-current', { encoding: 'utf-8' }).trim()
console.log(` Current branch: ${branch}`)
} catch (error) {
throw new Error('Failed to check git status')
}
}
/**
* Check dependencies
*/
private checkDependencies(): void {
const packageJsonPath = join(process.cwd(), 'package.json')
if (!existsSync(packageJsonPath)) {
return
}
const lockfilePath = existsSync(join(process.cwd(), 'package-lock.json'))
? 'package-lock.json'
: existsSync(join(process.cwd(), 'yarn.lock'))
? 'yarn.lock'
: existsSync(join(process.cwd(), 'pnpm-lock.yaml'))
? 'pnpm-lock.yaml'
: null
if (!lockfilePath) {
throw new Error('No lockfile found. Run npm install, yarn install, or pnpm install.')
}
}
/**
* Check tests
*/
private checkTests(): void {
try {
const packageJsonPath = join(process.cwd(), 'package.json')
if (!existsSync(packageJsonPath)) {
console.log(' No package.json found, skipping tests')
return
}
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))
if (!packageJson.scripts?.test) {
console.log(' No test script found, skipping')
return
}
console.log(' Running tests...')
execSync('npm test', { stdio: 'inherit' })
} catch (error) {
throw new Error('Tests failed')
}
}
/**
* Check environment variables
*/
private checkEnvVars(): void {
const requiredVars = this.getRequiredEnvVars()
const missing = requiredVars.filter(varName => !process.env[varName])
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(', ')}`)
}
}
/**
* Get required environment variables based on platform
*/
private getRequiredEnvVars(): string[] {
switch (this.options.platform) {
case 'vercel':
return ['VERCEL_TOKEN']
case 'railway':
return ['RAILWAY_TOKEN']
case 'aws':
return ['AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY']
case 'docker':
return []
default:
return []
}
}
/**
* Build project
*/
private async build(): Promise<void> {
const packageJsonPath = join(process.cwd(), 'package.json')
if (!existsSync(packageJsonPath)) {
console.log('No package.json found, skipping build')
return
}
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))
if (!packageJson.scripts?.build) {
console.log('No build script found, skipping')
return
}
try {
execSync('npm run build', { stdio: 'inherit' })
console.log('✓ Build successful')
} catch (error) {
throw new Error('Build failed')
}
}
/**
* Execute platform-specific deployment
*/
private async executeDeploy(): Promise<void> {
switch (this.options.platform) {
case 'vercel':
await this.deployVercel()
break
case 'railway':
await this.deployRailway()
break
case 'aws':
await this.deployAWS()
break
case 'docker':
await this.deployDocker()
break
case 'custom':
await this.deployCustom()
break
default:
throw new Error(`Unsupported platform: ${this.options.platform}`)
}
}
/**
* Deploy to Vercel
*/
private async deployVercel(): Promise<void> {
const prodFlag = this.options.environment === 'production' ? '--prod' : ''
const command = `vercel ${prodFlag} --token ${process.env.VERCEL_TOKEN}`
try {
execSync(command, { stdio: 'inherit' })
} catch (error) {
throw new Error('Vercel deployment failed')
}
}
/**
* Deploy to Railway
*/
private async deployRailway(): Promise<void> {
const command = `railway up ${this.options.project ? `--project ${this.options.project}` : ''}`
try {
execSync(command, {
stdio: 'inherit',
env: { ...process.env, RAILWAY_TOKEN: process.env.RAILWAY_TOKEN }
})
} catch (error) {
throw new Error('Railway deployment failed')
}
}
/**
* Deploy to AWS
*/
private async deployAWS(): Promise<void> {
// This is a simplified example - real AWS deployment would be more complex
console.log('AWS deployment not fully implemented')
throw new Error('AWS deployment not implemented')
}
/**
* Deploy with Docker
*/
private async deployDocker(): Promise<void> {
const tag = this.options.tag || 'latest'
const project = this.options.project || 'app'
try {
// Build image
execSync(`docker build -t ${project}:${tag} .`, { stdio: 'inherit' })
// Push to registry (if configured)
if (process.env.DOCKER_REGISTRY) {
execSync(`docker push ${process.env.DOCKER_REGISTRY}/${project}:${tag}`, {
stdio: 'inherit'
})
}
} catch (error) {
throw new Error('Docker deployment failed')
}
}
/**
* Deploy with custom command
*/
private async deployCustom(): Promise<void> {
if (!this.options.customCommand) {
throw new Error('Custom command not provided')
}
try {
execSync(this.options.customCommand, { stdio: 'inherit' })
} catch (error) {
throw new Error('Custom deployment failed')
}
}
/**
* Send notification
*/
private async sendNotification(result: DeployResult): Promise<void> {
if (!this.options.webhookUrl) return
try {
await fetch(this.options.webhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: result.success
? `✓ Deployment to ${result.platform} successful`
: `✗ Deployment to ${result.platform} failed`,
result
})
})
} catch (error) {
console.error('Failed to send notification:', error)
}
}
}
/**
* Run deployment from CLI
*/
export async function runDeploy(): Promise<void> {
const args = process.argv.slice(2)
const platformIndex = args.indexOf('--platform')
const platform = platformIndex >= 0 ? (args[platformIndex + 1] as Platform) : 'vercel'
const envIndex = args.indexOf('--environment')
const environment = envIndex >= 0 ? (args[envIndex + 1] as Environment) : undefined
const projectIndex = args.indexOf('--project')
const project = projectIndex >= 0 ? args[projectIndex + 1] : undefined
const tagIndex = args.indexOf('--tag')
const tag = tagIndex >= 0 ? args[tagIndex + 1] : undefined
const deployment = new Deployment({
platform,
environment,
project,
tag,
skipChecks: args.includes('--skip-checks'),
skipBuild: args.includes('--skip-build'),
dryRun: args.includes('--dry-run')
})
const result = await deployment.deploy()
if (!result.success) {
process.exit(1)
}
}
// Run if executed directly
if (require.main === module) {
runDeploy().catch(error => {
console.error('Deployment failed:', error)
process.exit(1)
})
}