diff --git a/package-lock.json b/package-lock.json index 207088c..db73161 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,15 +10,54 @@ "license": "UNLICENSED", "dependencies": { "@aws-sdk/client-s3": "^3.525.0", - "@aws-sdk/credential-providers": "^3.525.0" + "@aws-sdk/credential-providers": "^3.525.0", + "command-line-args": "^5.2.1" }, "bin": { "s3-empty-bucket": "dist/s3-empty-bucket-cli-exec.js" }, "devDependencies": { "@liquid-labs/sdlc-resource-babel-and-rollup": "^1.0.0-alpha.8", - "@liquid-labs/sdlc-resource-eslint": "^1.0.0-alpha.11", + "@liquid-labs/sdlc-resource-eslint": "file:.yalc/@liquid-labs/sdlc-resource-eslint", "@liquid-labs/sdlc-resource-jest": "^1.0.0-alpha.7" + }, + "engines": { + "node": ">=18.0.0" + } + }, + ".yalc/@liquid-labs/sdlc-resource-eslint": { + "version": "1.0.0-alpha.11", + "dev": true, + "license": "UNLICENSED", + "dependencies": { + "@babel/eslint-parser": "^7.22.15", + "@eslint/js": "^8.56.0", + "@liquid-labs/sdlc-resource-babel-and-rollup": "^1.0.0-alpha.6", + "eslint": "^8.50.0", + "eslint-config-standard": "^17.1.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jsdoc": "^48.0.4", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-react": "^7.33.2", + "globals": "^14.0.0", + "jest": "^29.7.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + ".yalc/@liquid-labs/sdlc-resource-eslint/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -3896,40 +3935,8 @@ } }, "node_modules/@liquid-labs/sdlc-resource-eslint": { - "version": "1.0.0-alpha.11", - "resolved": "https://registry.npmjs.org/@liquid-labs/sdlc-resource-eslint/-/sdlc-resource-eslint-1.0.0-alpha.11.tgz", - "integrity": "sha512-0x61jD28Oa8Y7tCGqJlAFNq9i5VlGl/nz0R1ODR7zQW8QXwOhB8A6Fh5uD0Y9C300neyn7uL3k8w6d23LSPR9A==", - "dev": true, - "dependencies": { - "@babel/eslint-parser": "^7.22.15", - "@eslint/js": "^8.56.0", - "@liquid-labs/sdlc-resource-babel-and-rollup": "^1.0.0-alpha.6", - "eslint": "^8.50.0", - "eslint-config-standard": "^17.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jsdoc": "^48.0.4", - "eslint-plugin-n": "^16.6.2", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-react": "^7.33.2", - "globals": "^14.0.0", - "jest": "^29.7.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@liquid-labs/sdlc-resource-eslint/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "resolved": ".yalc/@liquid-labs/sdlc-resource-eslint", + "link": true }, "node_modules/@liquid-labs/sdlc-resource-jest": { "version": "1.0.0-alpha.7", @@ -5344,6 +5351,14 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -6082,6 +6097,20 @@ "node": ">= 0.8" } }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -7523,6 +7552,17 @@ "node": ">=8" } }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -10397,6 +10437,11 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -12270,6 +12315,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "engines": { + "node": ">=8" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/package.json b/package.json index 04eb796..cf36f87 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "homepage": "https://github.com/liquid-labs/s3-empty-bucket#readme", "dependencies": { "@aws-sdk/client-s3": "^3.525.0", - "@aws-sdk/credential-providers": "^3.525.0" + "@aws-sdk/credential-providers": "^3.525.0", + "command-line-args": "^5.2.1" }, "devDependencies": { "@liquid-labs/sdlc-resource-babel-and-rollup": "^1.0.0-alpha.8", diff --git a/src/cli/s3-empty-bucket.mjs b/src/cli/s3-empty-bucket.mjs index 1016818..bf1968e 100644 --- a/src/cli/s3-empty-bucket.mjs +++ b/src/cli/s3-empty-bucket.mjs @@ -1,16 +1,27 @@ import { S3Client } from '@aws-sdk/client-s3' +import commandLineArgs from 'command-line-args' + import { getCredentials } from './lib/get-credentials' import { emptyBucket } from '../lib/s3-empty-bucket' +const cliSpec = { + mainCommand : 's3-empty-bucket', + mainOptions : [ + { name : 'bucketName', defaultOption : true, description : 'The name of the bucket to empty.' }, + { name : 'profile', alias : 'p', description : 'The SSO profile to use.' }, + { name : 'quiet', alias : 'q', type : Boolean, description : 'Suppresses output.' } + ] +} + const s3EmptyBucket = () => { - const bucketName = process.argv[2] - const ssoProfile = process.argv[3] + const options = commandLineArgs(cliSpec.mainOptions) + const { bucketName, profile, quiet } = options - const credentials = getCredentials({ ssoProfile }) + const credentials = getCredentials({ ssoProfile : profile }) const s3Client = new S3Client({ credentials }) - emptyBucket({ bucketName, s3Client }) + emptyBucket({ bucketName, s3Client, verbose : !quiet }) } export { s3EmptyBucket } diff --git a/src/lib/s3-empty-bucket.mjs b/src/lib/s3-empty-bucket.mjs index c5ce21d..3c7fa00 100644 --- a/src/lib/s3-empty-bucket.mjs +++ b/src/lib/s3-empty-bucket.mjs @@ -1,9 +1,10 @@ import { DeleteObjectsCommand, ListObjectsCommand } from '@aws-sdk/client-s3' -const emptyBucket = async ({ bucketName, s3Client }) => { +const emptyBucket = async ({ bucketName, s3Client, verbose }) => { const objects = [] let marker, isTruncated - process.stdout.write('Cataloging files...\n') + + maybeSay('Cataloging files...\n', verbose) do { const listObjectsCommand = new ListObjectsCommand({ Bucket : bucketName, Marker : marker }) const listObjectsResult = await s3Client.send(listObjectsCommand) @@ -15,11 +16,11 @@ const emptyBucket = async ({ bucketName, s3Client }) => { } while (isTruncated === true) if (objects.length === 0) { - process.stdout.write('Bucket already empty.\n') + maybeSay('Bucket already empty.\n', verbose) return } - process.stdout.write(`Deleting ${objects.length} files...\n`) + maybeSay(`Deleting ${objects.length} files...\n`, verbose) const input = { Bucket : bucketName, @@ -32,7 +33,13 @@ const emptyBucket = async ({ bucketName, s3Client }) => { const deleteObjectsCommand = new DeleteObjectsCommand(input) await s3Client.send(deleteObjectsCommand) - process.stdout.write('Done!\n') + maybeSay('Done!\n', verbose) +} + +const maybeSay = (message, verbose) => { + if (verbose === true) { + process.stdout.write(message) + } } export { emptyBucket }