@@ -459,35 +459,9 @@ export async function deployStaticSite(config: StaticSiteConfig): Promise<Deploy
459459 const s3 = new S3Client ( region )
460460 const cloudfront = new CloudFrontClient ( )
461461
462- // Check if S3 bucket exists (orphaned from previous non-CloudFormation deployment)
463- let bucketCleanedUp = false
464- try {
465- const headResult = await s3 . headBucket ( bucket )
466- if ( headResult . exists ) {
467- // Bucket exists without a stack - try to clean it up with timeout
468- console . log ( `Found orphaned S3 bucket ${ bucket } , cleaning up...` )
469- try {
470- // Timeout for bucket cleanup (30 seconds)
471- const cleanupPromise = s3 . emptyBucket ( bucket ) . then ( ( ) => s3 . deleteBucket ( bucket ) )
472- const timeoutPromise = new Promise < never > ( ( _ , reject ) =>
473- setTimeout ( ( ) => reject ( new Error ( 'Bucket cleanup timeout' ) ) , 30000 ) ,
474- )
475- await Promise . race ( [ cleanupPromise , timeoutPromise ] )
476- console . log ( `Deleted orphaned S3 bucket ${ bucket } ` )
477- bucketCleanedUp = true
478- }
479- catch ( cleanupErr : any ) {
480- console . log ( `Note: Could not clean up S3 bucket: ${ cleanupErr . message } ` )
481- // If we can't clean up the bucket, use a unique suffix
482- const suffix = Date . now ( ) . toString ( 36 )
483- finalBucket = `${ bucket } -${ suffix } `
484- console . log ( `Using alternative bucket name: ${ finalBucket } ` )
485- }
486- }
487- }
488- catch {
489- // Bucket doesn't exist, good
490- }
462+ // Check for existing CloudFront distribution FIRST before cleaning up any "orphaned" buckets
463+ // This prevents deleting buckets that are actively used by a CloudFront distribution
464+ let hasExistingDistribution = false
491465
492466 // Check for existing CloudFront distribution that WE created for this domain
493467 // Only reuse distributions that have our domain as an alias - NEVER use other projects' resources
@@ -517,6 +491,7 @@ export async function deployStaticSite(config: StaticSiteConfig): Promise<Deploy
517491
518492 // Only use distribution if it has OUR domain as an alias
519493 if ( aliases . includes ( domain ) ) {
494+ hasExistingDistribution = true
520495 console . log ( `Found existing CloudFront distribution ${ dist . Id } for ${ domain } ` )
521496
522497 // Get the origin bucket from the distribution
@@ -631,6 +606,33 @@ export async function deployStaticSite(config: StaticSiteConfig): Promise<Deploy
631606 }
632607 }
633608 }
609+
610+ // Only clean up orphaned S3 buckets when no CloudFront distribution references them
611+ if ( ! hasExistingDistribution ) {
612+ try {
613+ const headResult = await s3 . headBucket ( bucket )
614+ if ( headResult . exists ) {
615+ console . log ( `Found orphaned S3 bucket ${ bucket } , cleaning up...` )
616+ try {
617+ const cleanupPromise = s3 . emptyBucket ( bucket ) . then ( ( ) => s3 . deleteBucket ( bucket ) )
618+ const timeoutPromise = new Promise < never > ( ( _ , reject ) =>
619+ setTimeout ( ( ) => reject ( new Error ( 'Bucket cleanup timeout' ) ) , 30000 ) ,
620+ )
621+ await Promise . race ( [ cleanupPromise , timeoutPromise ] )
622+ console . log ( `Deleted orphaned S3 bucket ${ bucket } ` )
623+ }
624+ catch ( cleanupErr : any ) {
625+ console . log ( `Note: Could not clean up S3 bucket: ${ cleanupErr . message } ` )
626+ const suffix = Date . now ( ) . toString ( 36 )
627+ finalBucket = `${ bucket } -${ suffix } `
628+ console . log ( `Using alternative bucket name: ${ finalBucket } ` )
629+ }
630+ }
631+ }
632+ catch {
633+ // Bucket doesn't exist, good
634+ }
635+ }
634636 }
635637
636638 // Generate CloudFormation template with final bucket name
0 commit comments