Skip to content

Commit 9afd99c

Browse files
committed
chore: wip
1 parent c33ffd4 commit 9afd99c

File tree

2 files changed

+84
-98
lines changed

2 files changed

+84
-98
lines changed

storage/framework/core/actions/deploy.ts

Lines changed: 67 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -869,8 +869,9 @@ function getShortResourceType(resourceType: string): string {
869869

870870
/**
871871
* Create CDK-style progress callback
872+
* @param filter - Optional filter: 'delete' to only show DELETE events, 'create' for CREATE, etc.
872873
*/
873-
function createProgressCallback(): (event: {
874+
function createProgressCallback(filter?: 'delete' | 'create' | 'update'): (event: {
874875
resourceId: string
875876
resourceType: string
876877
status: string
@@ -879,8 +880,27 @@ function createProgressCallback(): (event: {
879880
}) => void {
880881
const maxIdLength = 35
881882
const maxTypeLength = 30
883+
const seenEvents = new Set<string>()
882884

883885
return (event) => {
886+
// Filter events based on type if specified
887+
if (filter === 'delete' && !event.status.includes('DELETE')) {
888+
return
889+
}
890+
if (filter === 'create' && !event.status.includes('CREATE')) {
891+
return
892+
}
893+
if (filter === 'update' && !event.status.includes('UPDATE')) {
894+
return
895+
}
896+
897+
// Deduplicate events (same resource + status)
898+
const eventKey = `${event.resourceId}:${event.status}`
899+
if (seenEvents.has(eventKey)) {
900+
return
901+
}
902+
seenEvents.add(eventKey)
903+
884904
const resourceId = event.resourceId.padEnd(maxIdLength).substring(0, maxIdLength)
885905
const resourceType = getShortResourceType(event.resourceType).padEnd(maxTypeLength).substring(0, maxTypeLength)
886906
const status = formatResourceStatus(event.status)
@@ -1333,9 +1353,8 @@ export async function undeployStack(options: UndeployStackOptions): Promise<void
13331353
const projectConfig = await getProjectConfig()
13341354
const stackName = `stacks-cloud-${environment}`
13351355

1336-
log.info(`Undeploying infrastructure from ${environment} in ${region}...`)
1337-
log.info(`Stack: ${stackName}`)
1338-
log.info('')
1356+
console.log(`Undeploying ${stackName} from ${region}...`)
1357+
console.log('')
13391358

13401359
try {
13411360
const { CloudFormationClient, AWSClient } = await import('ts-cloud/aws')
@@ -1347,7 +1366,7 @@ export async function undeployStack(options: UndeployStackOptions): Promise<void
13471366
const describeResult = await cf.describeStacks({ stackName })
13481367
if (describeResult.Stacks && describeResult.Stacks.length > 0) {
13491368
const stack = describeResult.Stacks[0]
1350-
log.info(`Current stack status: ${formatResourceStatus(stack.StackStatus)}`)
1369+
console.log(`Current status: ${formatResourceStatus(stack.StackStatus)}`)
13511370
stackExists = true
13521371
}
13531372
}
@@ -1356,12 +1375,12 @@ export async function undeployStack(options: UndeployStackOptions): Promise<void
13561375
}
13571376

13581377
if (!stackExists) {
1359-
log.info(`Stack ${stackName} does not exist. Nothing to undeploy.`)
1378+
console.log(`Stack ${stackName} does not exist. Nothing to undeploy.`)
13601379
return
13611380
}
13621381

13631382
// Clean up any HTTPS listeners before deletion to avoid DELETE_FAILED
1364-
log.info('Checking for resources to clean up before deletion...')
1383+
if (verbose) console.log('Checking for resources to clean up before deletion...')
13651384
try {
13661385
const client = new AWSClient()
13671386

@@ -1397,7 +1416,7 @@ export async function undeployStack(options: UndeployStackOptions): Promise<void
13971416
let deletedListeners = 0
13981417
for (const listener of listenerList) {
13991418
if (listener.Protocol === 'HTTPS') {
1400-
log.info(` Removing HTTPS listener on port ${listener.Port}...`)
1419+
if (verbose) console.log(` Removing HTTPS listener on port ${listener.Port}...`)
14011420
const deleteParams = {
14021421
Action: 'DeleteListener',
14031422
ListenerArn: listener.ListenerArn,
@@ -1415,62 +1434,58 @@ export async function undeployStack(options: UndeployStackOptions): Promise<void
14151434
}
14161435
}
14171436

1418-
if (deletedListeners > 0) {
1419-
log.success(` Cleaned up ${deletedListeners} HTTPS listener(s)`)
1437+
if (deletedListeners > 0 && verbose) {
1438+
console.log(`✓ Cleaned up ${deletedListeners} HTTPS listener(s)`)
14201439
await new Promise(resolve => setTimeout(resolve, 2000))
14211440
}
14221441
}
14231442
}
14241443
catch (cleanupError: any) {
14251444
if (verbose) {
1426-
log.warn(`Cleanup warning: ${cleanupError.message}`)
1445+
console.log(`Warning: ${cleanupError.message}`)
14271446
}
14281447
}
14291448

14301449
// Initiate stack deletion
1431-
log.info('')
1432-
log.info('Initiating stack deletion...')
1433-
log.info('')
1450+
console.log('Deleting stack...')
1451+
console.log('')
14341452

14351453
await cf.deleteStack(stackName)
14361454

14371455
// Print header for resource status table
1438-
console.log(` ${'ResourceId'.padEnd(35)} ${'ResourceType'.padEnd(30)} Status`)
1439-
console.log(' ' + '-'.repeat(80))
1440-
1441-
// Wait for deletion with progress callback
1442-
await cf.waitForStackWithProgress(stackName, 'stack-delete-complete', createProgressCallback())
1443-
1444-
log.info('')
1445-
log.success('Infrastructure undeployed successfully!')
1446-
log.info('')
1447-
log.info('═══════════════════════════════════════════════════════════════')
1448-
log.info(' UNDEPLOY SUMMARY')
1449-
log.info('═══════════════════════════════════════════════════════════════')
1450-
log.info('')
1451-
log.info(` Stack: ${stackName}`)
1452-
log.info(` Status: DELETED`)
1453-
log.info(` Environment: ${environment}`)
1454-
log.info(` Region: ${region}`)
1455-
log.info('')
1456-
log.info('═══════════════════════════════════════════════════════════════')
1457-
log.info('')
1456+
console.log(` ${'Resource'.padEnd(35)} ${'Type'.padEnd(30)} Status`)
1457+
console.log(' ' + '─'.repeat(85))
1458+
1459+
// Wait for deletion with progress callback - only show DELETE events
1460+
await cf.waitForStackWithProgress(stackName, 'stack-delete-complete', createProgressCallback('delete'))
1461+
1462+
console.log('')
1463+
console.log('═══════════════════════════════════════════════════════════════════════════════════════')
1464+
console.log('')
1465+
console.log(' ✓ Infrastructure removed successfully')
1466+
console.log('')
1467+
console.log(` Stack: ${stackName}`)
1468+
console.log(` Environment: ${environment}`)
1469+
console.log(` Region: ${region}`)
1470+
console.log('')
1471+
console.log('═══════════════════════════════════════════════════════════════════════════════════════')
1472+
console.log('')
14581473
}
14591474
catch (error: any) {
14601475
const errorStr = String(error.message || error)
14611476

14621477
// Handle stack doesn't exist
14631478
if (errorStr.includes('does not exist')) {
1464-
log.info('')
1465-
log.success('Stack already deleted or does not exist.')
1479+
console.log('')
1480+
console.log('✓ Stack already deleted or does not exist.')
14661481
return
14671482
}
14681483

14691484
// Handle DELETE_FAILED - need to retain some resources
14701485
if (error.code === 'DELETE_FAILED' || errorStr.includes('DELETE_FAILED')) {
1471-
log.warn('')
1472-
log.warn('Some resources could not be deleted automatically')
1473-
log.info('Identifying resources to retain...')
1486+
console.log('')
1487+
console.log('Some resources could not be deleted automatically')
1488+
console.log('Identifying resources to retain...')
14741489

14751490
const { CloudFormationClient } = await import('ts-cloud/aws')
14761491
const cf = new CloudFormationClient(region)
@@ -1483,35 +1498,35 @@ export async function undeployStack(options: UndeployStackOptions): Promise<void
14831498
.map((r: any) => r.LogicalResourceId)
14841499

14851500
if (failedResources.length > 0) {
1486-
log.info(`Retaining ${failedResources.length} resource(s):`)
1487-
failedResources.forEach((r: string) => log.info(` - ${r}`))
1488-
log.info('')
1501+
console.log(`Retaining ${failedResources.length} resource(s):`)
1502+
failedResources.forEach((r: string) => console.log(` - ${r}`))
1503+
console.log('')
14891504

14901505
// Retry with retained resources
1491-
log.info('Retrying deletion with retained resources...')
1506+
console.log('Retrying deletion with retained resources...')
14921507
await cf.deleteStack(stackName, undefined, failedResources)
1493-
await cf.waitForStackWithProgress(stackName, 'stack-delete-complete', createProgressCallback())
1508+
await cf.waitForStackWithProgress(stackName, 'stack-delete-complete', createProgressCallback('delete'))
14941509

1495-
log.info('')
1496-
log.success('Stack removed (with retained resources)')
1497-
log.info('')
1498-
log.info('Note: Some resources were retained and may need manual cleanup.')
1499-
log.info('Run `./buddy cloud:cleanup` to clean up remaining resources.')
1510+
console.log('')
1511+
console.log('✓ Stack removed (with retained resources)')
1512+
console.log('')
1513+
console.log('Note: Some resources were retained and may need manual cleanup.')
1514+
console.log('Run `./buddy cloud:cleanup` to clean up remaining resources.')
15001515
return
15011516
}
15021517
}
15031518
catch (retryError: any) {
15041519
const retryErrorStr = String(retryError.message || retryError)
15051520
if (retryErrorStr.includes('does not exist')) {
1506-
log.info('')
1507-
log.success('Stack deleted successfully!')
1521+
console.log('')
1522+
console.log('✓ Stack deleted successfully!')
15081523
return
15091524
}
15101525
throw retryError
15111526
}
15121527
}
15131528

1514-
log.error(`Stack deletion failed: ${error.message}`)
1529+
console.error(`Stack deletion failed: ${error.message}`)
15151530
throw error
15161531
}
15171532
}

storage/framework/core/buddy/src/commands/cloud.ts

Lines changed: 17 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -377,14 +377,10 @@ export function cloud(buddy: CLI): void {
377377
}
378378

379379
console.log('')
380-
log.info('Removing your cloud infrastructure...')
381-
console.log(` ${italic('This process typically takes 3-5 minutes to complete.')}`)
382-
console.log(` ${italic('Your backups will be retained for 30 days before automatic deletion.')}`)
380+
console.log('Removing cloud infrastructure...')
381+
console.log(` ${italic('This typically takes 2-5 minutes.')}`)
383382
console.log('')
384383

385-
// Give user time to read the message
386-
await new Promise(resolve => setTimeout(resolve, 1500))
387-
388384
// Load AWS credentials from .env.production if not already set
389385
if (!process.env.AWS_ACCESS_KEY_ID || !process.env.AWS_SECRET_ACCESS_KEY) {
390386
const { existsSync, readFileSync } = await import('node:fs')
@@ -407,10 +403,6 @@ export function cloud(buddy: CLI): void {
407403
process.env[key] = value
408404
}
409405
}
410-
411-
if (process.env.AWS_ACCESS_KEY_ID) {
412-
log.success('Loaded AWS credentials from .env.production')
413-
}
414406
}
415407
}
416408

@@ -430,64 +422,43 @@ export function cloud(buddy: CLI): void {
430422
verbose: options.verbose,
431423
})
432424

433-
// Run cleanup for any retained resources
434-
console.log('')
435-
log.info('Running cleanup for any retained resources...')
436-
437-
try {
438-
await runCommand('./buddy cloud:cleanup', {
439-
...options,
440-
cwd: p.projectPath(),
441-
stdin: 'inherit',
442-
})
443-
}
444-
catch (e: any) {
445-
// Cleanup failures are non-fatal since main stack was deleted
446-
const errStr = String(e.message || e)
447-
if (errStr.includes('AuthFailure') || errStr.includes('credentials') || errStr.includes('not authorized')) {
448-
log.warn('Cleanup skipped due to credential issues')
449-
}
450-
else {
451-
log.warn('Some cleanup tasks may need manual completion')
452-
}
453-
}
425+
// Cleanup is already handled by CloudFormation - retained resources (like S3)
426+
// are intentional and can be cleaned up separately with `./buddy cloud:cleanup`
454427

455-
console.log('')
456428
await outro('Cloud infrastructure removed', { startTime, useSeconds: true })
457429
process.exit(ExitCode.Success)
458430
}
459431
catch (error: any) {
460432
console.log('')
461-
log.error('Failed to remove cloud infrastructure')
433+
console.error('Failed to remove cloud infrastructure')
462434

463435
// Check for common error patterns
464436
const errorStr = String(error.message || error)
465437
if (errorStr.includes('security token') || errorStr.includes('credentials')) {
466438
console.log('')
467-
log.error('AWS credentials are invalid or expired')
468-
log.info('Check your AWS credentials in .env.production:')
469-
log.info(' - AWS_ACCESS_KEY_ID')
470-
log.info(' - AWS_SECRET_ACCESS_KEY')
439+
console.error(' AWS credentials are invalid or expired')
440+
console.log(' Check your AWS credentials in .env.production:')
441+
console.log(' - AWS_ACCESS_KEY_ID')
442+
console.log(' - AWS_SECRET_ACCESS_KEY')
471443
}
472444
else if (errorStr.includes('region') || errorStr.includes('AWS_REGION')) {
473445
console.log('')
474-
log.error('AWS Region not configured')
475-
log.info('Add AWS_REGION to your .env.production file')
446+
console.error(' AWS Region not configured')
447+
console.log(' Add AWS_REGION to your .env.production file')
476448
}
477449
else if (errorStr.includes('AccessDenied')) {
478450
console.log('')
479-
log.error('Access denied')
480-
log.info('Your AWS credentials may not have permission to delete stacks')
481-
log.info('Required permissions: CloudFormation:DeleteStack, IAM:*, EC2:*, S3:*')
451+
console.error(' Access denied')
452+
console.log(' Your AWS credentials may not have permission to delete stacks')
482453
}
483454
else {
484-
log.error(errorStr)
455+
console.error(` ${errorStr}`)
485456
}
486457

487458
console.log('')
488-
log.info('Troubleshooting:')
489-
log.info(' ./buddy cloud:cleanup - Clean up resources manually')
490-
log.info(' --verbose - Show detailed error information')
459+
console.log('Troubleshooting:')
460+
console.log(' ./buddy cloud:cleanup - Clean up resources manually')
461+
console.log(' --verbose - Show detailed error information')
491462
console.log('')
492463

493464
if (options.verbose) {

0 commit comments

Comments
 (0)