| 1 | args=("$@") |
| 2 | has_profile=false |
| 3 | for ((i = 0; i < ${#args[@]}; i++)); do |
| 4 | case "${args[i]}" in |
| 5 | --) break ;; |
| 6 | --profile) |
| 7 | if [[ -z "${args[i+1]:-}" || "${args[i+1]}" == -* ]]; then |
| 8 | echo "Error: --profile requires a profile name." >&2 |
| 9 | exit 1 |
| 10 | fi |
| 11 | has_profile=true |
| 12 | ((i++)) |
| 13 | ;; |
| 14 | --profile=*) |
| 15 | if [[ -z "${args[i]#--profile=}" ]]; then |
| 16 | echo "Error: --profile requires a profile name." >&2 |
| 17 | exit 1 |
| 18 | fi |
| 19 | has_profile=true |
| 20 | ;; |
| 21 | esac |
| 22 | done |
| 23 | if [[ "$has_profile" != true ]]; then |
| 24 | echo "Error: pass --profile <name> explicitly to nuke resources." >&2 |
| 25 | exit 1 |
| 26 | fi |
| 27 | |
| 28 | # AWS.BackupSearch.SearchJob: AWS retains search-job records ~7 days; no delete API |
| 29 | # Cloudflare.Cache.RegionalTieredCache / Cloudflare.Logs.RetentionFlag / |
| 30 | # Cloudflare.AI.SecuritySettings: zone-singleton settings that always exist |
| 31 | # on entitled zones — delete restores the pre-management value (a no-op |
| 32 | # when enumerated), so the census would flag them forever. |
| 33 | # Cloudflare.AI.Gateway "default": auto-provisioned by Cloudflare when an AI Search |
| 34 | # instance is created; recreated on demand, so deleting it just churns. |
| 35 | # Cloudflare.Email.Address alchemy-list-test@: standing test address — Cloudflare |
| 36 | # refuses to delete an address for ~15 min after creation (code 2032), so the |
| 37 | # EmailAddress test retains and re-adopts it instead of create/destroy churn. |
| 38 | bun alchemy unsafe nuke --config ./stacks/nuke.ts \ |
| 39 | --exclude 'AWS.BackupSearch.SearchJob' \ |
| 40 | --exclude 'Cloudflare.Zone*' \ |
| 41 | --exclude 'Cloudflare.Account*' \ |
| 42 | --exclude 'Cloudflare.DNS*' \ |
| 43 | --exclude 'Cloudflare.Ssl.UniversalSsl' \ |
| 44 | --exclude 'Cloudflare.Cache.RegionalTieredCache' \ |
| 45 | --exclude 'Cloudflare.Logs.RetentionFlag' \ |
| 46 | --exclude 'Cloudflare.AI.SecuritySettings' \ |
| 47 | --exclude 'Cloudflare.ApiToken.*' \ |
| 48 | --exclude 'Cloudflare.Secret*' \ |
| 49 | --exclude 'Cloudflare.Organization.*' \ |
| 50 | --exclude 'AWS.IAM.User' \ |
| 51 | --exclude 'AWS.IAM.SAMLProvider' \ |
| 52 | --exclude 'AWS.IAM.OpenIDConnectProvider' \ |
| 53 | --exclude 'AWS.IAM.AccountAlias' \ |
| 54 | --exclude 'AWS.IAM.AccountPasswordPolicy' \ |
| 55 | --exclude 'AWS.IAM.LoginProfile' \ |
| 56 | --exclude 'AWS.IdentityCenter*' \ |
| 57 | --exclude 'AWS.Organizations.*' \ |
| 58 | --exclude 'AWS.IAM.ServiceLinkedRole' \ |
| 59 | --exclude 'AWS.LakeFormation.*' \ |
| 60 | --exclude 'AWS.Notifications.*' \ |
| 61 | --exclude 'AWS.NotificationsContacts.*' \ |
| 62 | --exclude 'AWS.ApiGateway.Account' \ |
| 63 | --concurrency 32 \ |
| 64 | --timeout 300 \ |
| 65 | --filter 'resource.Type === "Cloudflare.Worker" && (resource.workerName?.startsWith("alchemy-state") || resource.workerName === "Api" || ["alchemy-website-preview","alchemy-website-main","alchemy-website-prod"].includes(resource.workerName))' \ |
| 66 | --filter 'resource.Type === "AWS.IAM.Role" && (["alchemy-github-actions", "distilled-github-oidc-role"].includes(resource.roleName) || resource.roleName?.startsWith("AWSReservedSSO"))' \ |
| 67 | --filter 'resource.Type === "AWS.S3.Bucket" && (String(resource.bucketName).startsWith("alchemy-state") || String(resource.bucketName).startsWith("alchemy-assets"))' \ |
| 68 | --filter 'typeof resource.name === "string" && (resource.name.startsWith("DO-NOT-DELETE") || resource.name.startsWith("AppConfig.") || resource.name.startsWith("system_") || ["primary","AwsDataCatalog","DefaultConfiguration","Default","default","open-access","default.dax1.0"].includes(resource.name))' \ |
| 69 | --filter 'resource.Type === "Cloudflare.AI.Gateway" && resource.gatewayId === "default"' \ |
| 70 | --filter 'resource.Type === "Cloudflare.Email.Address" && resource.email === "alchemy-list-test@alchemy-test-2.us"' \ |
| 71 | --filter 'String(resource.logGroupName).startsWith("/aws/vendedlogs/b2bi/")' \ |
| 72 | --filter 'resource.Type === "Fly.Bucket" && resource.name === "fly-buovnrccm7qpb3be3utsdvlj6x"' \ |
| 73 | "$@" |