├── .eslintrc ├── .gitignore ├── .npmignore ├── CONTRIBUTING.md ├── LICENCE ├── README.md ├── package-lock.json ├── package.json ├── script └── s3.js └── test.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", // https://github.com/babel/babel-eslint 3 | "parserOptions": { 4 | "ecmaFeatures": { 5 | "experimentalObjectRestSpread": true 6 | }, 7 | "sourceType": "module" 8 | }, 9 | "env": { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments 10 | "node": true, // node global variables 11 | "mocha": true, // mocha keywords 12 | "es6": true 13 | }, 14 | "plugins": [ 15 | "prettier" 16 | ], 17 | "extends": [ 18 | "eslint:recommended", 19 | "airbnb-base", 20 | "prettier" 21 | ], 22 | "rules": { 23 | "accessor-pairs": 0, // http://eslint.org/docs/rules/accessor-pairs 24 | "arrow-body-style": 0, // http://eslint.org/docs/rules/arrow-body-style 25 | "callback-return": 0, // http://eslint.org/docs/rules/callback-return 26 | "consistent-return": 0, // http://eslint.org/docs/rules/consistent-return 27 | "default-case": 0, // http://eslint.org/docs/rules/default-case 28 | "func-names": 0, // http://eslint.org/docs/rules/func-names 29 | "global-require": 0, // http://eslint.org/docs/rules/global-require 30 | "guard-for-in": 0, // http://eslint.org/docs/rules/guard-for-in 31 | "handle-callback-err": 0, // http://eslint.org/docs/rules/handle-callback-err 32 | "id-length": 0, // http://eslint.org/docs/rules/id-length 33 | "id-match": 0, // http://eslint.org/docs/rules/id-match 34 | "import/no-dynamic-require": 0, // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-dynamic-require.md 35 | "import/no-extraneous-dependencies": 0, // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-extraneous-dependencies.md 36 | "init-declarations": 0, // http://eslint.org/docs/rules/init-declarations 37 | "jsx-quotes": 0, // http://eslint.org/docs/rules/jsx-quotes 38 | "key-spacing": 0, // http://eslint.org/docs/rules/key-spacing 39 | "linebreak-style": 0, // http://eslint.org/docs/rules/linebreak-style 40 | "lines-around-comment": 0, // http://eslint.org/docs/rules/lines-around-comment 41 | "max-depth": 0, // http://eslint.org/docs/rules/max-depth 42 | "max-nested-callbacks": 0, // http://eslint.org/docs/rules/max-nested-callbacks 43 | "max-params": 0, // http://eslint.org/docs/rules/max-params 44 | "max-statements": 0, // http://eslint.org/docs/rules/max-statements 45 | "newline-after-var": 0, // http://eslint.org/docs/rules/newline-after-var 46 | "no-array-constructor": 0, // http://eslint.org/docs/rules/no-array-constructor 47 | "no-arrow-condition": 0, // http://eslint.org/docs/rules/no-arrow-condition 48 | "no-caller": 0, // http://eslint.org/docs/rules/no-caller 49 | "no-case-declarations": 0, // http://eslint.org/docs/rules/no-case-declarations 50 | "no-control-regex": 0, // http://eslint.org/docs/rules/no-control-regex 51 | "no-else-return": 0, // http://eslint.org/docs/rules/no-else-return 52 | "no-empty-character-class": 0, // http://eslint.org/docs/rules/no-empty-character-class 53 | "no-extend-native": 0, // http://eslint.org/docs/rules/no-extend-native 54 | "no-implicit-coercion": 0, // http://eslint.org/docs/rules/no-implicit-coercion 55 | "no-inline-comments": 0, // http://eslint.org/docs/rules/no-inline-comments 56 | "no-inner-declarations": 0, // http://eslint.org/docs/rules/no-inner-declarations 57 | "no-invalid-this": 0, // http://eslint.org/docs/rules/no-invalid-this 58 | "no-iterator": 0, // http://eslint.org/docs/rules/no-iterator 59 | "no-lonely-if": 0, // http://eslint.org/docs/rules/no-lonely-if 60 | "no-loop-func": 0, // http://eslint.org/docs/rules/no-loop-func 61 | "no-magic-numbers": 0, // http://eslint.org/docs/rules/no-magic-numbers 62 | "no-mixed-requires": 0, // http://eslint.org/docs/rules/no-mixed-requires 63 | "no-multi-str": 0, // http://eslint.org/docs/rules/no-multi-str 64 | "no-nested-ternary": 0, // http://eslint.org/docs/rules/no-nested-ternary 65 | "no-param-reassign": 0, // https://eslint.org/docs/rules/no-param-reassign 66 | "no-plusplus": 0, // http://eslint.org/docs/rules/no-plusplus 67 | "no-process-env": 0, // http://eslint.org/docs/rules/no-process-env 68 | "no-process-exit": 0, // http://eslint.org/docs/rules/no-process-exit 69 | "no-proto": 0, // http://eslint.org/docs/rules/no-proto 70 | "no-regex-spaces": 0, // http://eslint.org/docs/rules/no-regex-spaces 71 | "no-restricted-imports": 0, // http://eslint.org/docs/rules/no-restricted-imports 72 | "no-restricted-modules": 0, // http://eslint.org/docs/rules/no-restricted-modules 73 | "no-restricted-syntax": 0, // http://eslint.org/docs/rules/no-restricted-syntax 74 | "no-sequences": 0, // http://eslint.org/docs/rules/no-sequences 75 | "no-spaced-func": 0, // http://eslint.org/docs/rules/no-spaced-func 76 | "no-sync": 0, // http://eslint.org/docs/rules/no-sync 77 | "no-ternary": 0, // http://eslint.org/docs/rules/no-ternary 78 | "no-undefined": 0, // http://eslint.org/docs/rules/no-undefined 79 | "no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle 80 | "no-unexpected-multiline": 0, // http://eslint.org/docs/rules/no-unexpected-multiline 81 | "no-unused-expressions": 0, // http://eslint.org/docs/rules/no-unused-expressions 82 | "no-unused-vars": 0, // http://eslint.org/docs/rules/no-unused-vars 83 | "no-useless-escape": 0, // http://eslint.org/docs/rules/no-useless-escape 84 | "one-var": 0, // http://eslint.org/docs/rules/one-var 85 | "operator-linebreak": 0, // http://eslint.org/docs/rules/operator-linebreak 86 | "padded-blocks": 0, // http://eslint.org/docs/rules/padded-blocks 87 | "prefer-arrow-callback": 0, // http://eslint.org/docs/rules/prefer-arrow-callback 88 | "prefer-destructuring": 0, // https://eslint.org/docs/rules/prefer-destructuring 89 | "prefer-promise-reject-errors": 0, // https://eslint.org/docs/rules/prefer-promise-reject-errors 90 | "prefer-reflect": 0, // http://eslint.org/docs/rules/prefer-reflect 91 | "prefer-rest-params": 0, // http://eslint.org/docs/rules/prefer-rest-params 92 | "radix": 0, // http://eslint.org/docs/rules/radix 93 | "require-jsdoc": 0, // http://eslint.org/docs/rules/require-jsdoc 94 | "require-yield": 0, // http://eslint.org/docs/rules/require-yield 95 | "sort-vars": 0, // http://eslint.org/docs/rules/sort-vars 96 | "strict": 0, // http://eslint.org/docs/rules/strict 97 | "valid-jsdoc": 0, // http://eslint.org/docs/rules/valid-jsdoc 98 | "vars-on-top": 0, // http://eslint.org/docs/rules/vars-on-top 99 | "wrap-regex": 0, // http://eslint.org/docs/rules/wrap-regex 100 | 101 | 102 | 103 | "prettier/prettier": ["error", { "printWidth": 110, "singleQuote": true }] // https://github.com/prettier/prettier 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | aws.js 3 | build/* 4 | coverage/* 5 | .vscode 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | 4 | # testing 5 | /coverage 6 | 7 | # production 8 | /build 9 | 10 | # misc 11 | .gitignore 12 | .DS_Store 13 | .env 14 | .mock.js 15 | .test.js 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | package-lock.json -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First off, thank you for considering contributing to this project! Now, to start contributing: 4 | 5 | ## Issues and suggestions 6 | 7 | If you either find a bug or have any suggestion or opinion you want to discuss and share, please open an issue and add the proper label to it so we can get in contact with you. 8 | There are no wrong opinions! All feedback is welcome to make this the most suitable tool for you to use and for us to grow. 9 | 10 | ## How to contribute 11 | 12 | If you have a new feature you want to add or a bug you think you can fix, follow this steps: 13 | 14 | 1. Fork the repo 15 | 2. Create your feature branch (`git checkout -b my-new-feature`) 16 | 3. Commit your changes (`git commit -am 'Add some feature'`) 17 | 4. Push to the branch (`git push origin my-new-feature`) 18 | 5. Create new Pull Request with **clear title and description** 19 | 20 | ## Installation 21 | 22 | To dev this project and see changes on the fly, simply use the script 23 | 24 | ```bash 25 | npm start 26 | ``` 27 | 28 | ## Testing 29 | 30 | Your new feature **must** be tested with the proper tools. In this project, we use Jest and Enzyme. Once your tests are written, run: 31 | 32 | ```bash 33 | npm run test 34 | ``` 35 | All new tests needs to be pass in order to approve the PR. 36 | 37 | If there any view changes, you need to add screenshots for what's been changed for us to see any improvement or your new feature. 38 | 39 | ## Documentation 40 | 41 | If you are adding a new feature, you **must** add the documentation for it, showing how to use it. -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Francisco Iglesias 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Deploy script 2 | 3 | [![FEArmy](https://github.com/Wolox/react-chat-widget/raw/master/assets/FEA_open_source_sm.png)](https://github.com/orgs/Wolox/teams/front-end-army/members) 4 | 5 | AWS script for deploying your frontend applications. 6 | 7 | ## Introduction 8 | 9 | The most common configuration used for our projects is using an S3 Bucket to contain our static files. Optionally you can have a CloudFront playing as a CDN. 10 | 11 | The aim of this script is to read all your files and sync directly to the bucket without having to install AWS CLI or any kind of credentials configuration. 12 | 13 | 14 | This script is cross project which means you only need to have a `build/` folder to sync. 15 | 16 | ## Folder structure 17 | ``` 18 | ROOT 19 | build/ 20 | index.html 21 | ... 22 | aws.js 23 | ... 24 | ``` 25 | 26 | - The `aws.js` file should be at the root of your project, it will contain credentials and will export them as an object like this: 27 | 28 | - The `aws.js` file also contains a specific object called metrics to persist the measured `deploy-time` in a db. If you do not wish to persist the metric the object can be removed. 29 | 30 | ```js 31 | module.exports = { 32 | development: { 33 | accessKeyId: "XXXXXXXXXXXXXXXXXXXX", 34 | secretAccessKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 35 | region: "us-east-1", 36 | bucket: "", 37 | distributionId: "XXXXXXXXXXXXXX", 38 | options: { // Optional 39 | preserveFiles: [/^sitemap/, "bar.js"] 40 | } 41 | }, 42 | stage: { 43 | accessKeyId: "XXXXXXXXXXXXXXXXXXXX", 44 | secretAccessKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 45 | region: "us-east-1", 46 | bucket: "", 47 | distributionId: "XXXXXXXXXXXXXX", 48 | options: { // Optional 49 | preserveFiles: ["static/baz.js"] 50 | } 51 | }, 52 | metrics: { 53 | tech: "react/vue/angular", 54 | repoName: "", 55 | baseUrl: "" 56 | } 57 | }; 58 | ``` 59 | 60 | - The `build/` folder will contain the file you want to sync 61 | 62 | - The `distributionId` is optional, in case you've got it. The script will create an invalidation for all your files. 63 | 64 | ## Options 65 | 66 | - `preserveFiles`. It allows to specify an array of S3 file paths that won't be removed when the script cleans the bucket before uploading the new build. It's useful if you want to upload files manually to the S3 bucket. Supports regex and strings 67 | 68 | - `environment` or `e`. It specifies the environment in which you are going to deploy your app. Also the key of your `aws.js` file that has the corresponding credentials 69 | 70 | - `path` or `p`. The path of you generated static build files. 71 | 72 | - `outputPath` or `o`. The S3 path to upload your files (e.g. in case you don't want to upload all files in the root of the bucket) 73 | 74 | ## Usage 75 | 76 | You can install this package globally and run `aws-deploy` 77 | 78 | ``` 79 | npm install -g aws-deploy-script-fe 80 | ``` 81 | 82 | The command line to execute can be: 83 | 84 | ``` 85 | aws-deploy --env --path 86 | ``` 87 | or 88 | ``` 89 | aws-deploy -e -p 90 | ``` 91 | 92 | * This **enviroment-name** is the key of the main object exported in `aws.js`. 93 | * If no **enviroment-name** is declared then it will use `development` as default. 94 | * The **build-path** is optional and defaults to `build`. 95 | 96 | ## Required dependencies 97 | 98 | The dependency you need to install is [aws-sdk](https://www.npmjs.com/package/aws-sdk) 99 | 100 | 101 | ## Required policies 102 | 103 | To run this script you must add these action policies to the user configured in the `aws.js` file 104 | 105 | ```js 106 | "Action": [ 107 | "s3:GetObject", 108 | "s3:GetObjectAcl", 109 | "s3:ListMultipartUploadParts", 110 | "s3:PutObject", 111 | "s3:PutObjectAcl", 112 | "s3:DeleteObject", 113 | "cloudfront:CreateInvalidation" 114 | ] 115 | ``` 116 | 117 | ## About 118 | 119 | This project is maintained by [Damián Finkelstein](https://github.com/damfinkel), [Pablo Ferro](https://github.com/pabloferro), [Francisco Iglesias](https://github.com/FrankIglesias) and [Lucas Zibell](https://github.com/LucasZibell) and it was written by [Wolox](http://www.wolox.com.ar). 120 | 121 | ![Wolox](https://raw.githubusercontent.com/Wolox/press-kit/master/logos/logo_banner.png) 122 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-deploy-script-fe", 3 | "version": "1.0.9", 4 | "description": "AWS script for deploying your frontend applications.", 5 | "main": "./script/s3.js", 6 | "scripts": { 7 | "test": "jest" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Wolox/aws-deploy-script-frontend.git" 12 | }, 13 | "author": "Wolox", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/Wolox/aws-deploy-script-frontend/issues" 17 | }, 18 | "homepage": "https://github.com/Wolox/aws-deploy-script-frontend#readme", 19 | "dependencies": { 20 | "aws-sdk": "^2.344.0", 21 | "axios": "^0.19.2", 22 | "mime-types": "^2.1.21", 23 | "minimist": "^1.2.0" 24 | }, 25 | "bin": { 26 | "aws-deploy": "./script/s3.js" 27 | }, 28 | "devDependencies": { 29 | "jest": "^23.6.0", 30 | "shelljs": "^0.8.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /script/s3.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const S3 = require("aws-sdk/clients/s3"), 3 | Cloudfront = require("aws-sdk/clients/cloudfront"), 4 | fs = require("fs"), 5 | awsCredentials = require(process.cwd() + "/aws.js"), 6 | path = require("path"), 7 | parseArgs = require("minimist"), 8 | mime = require('mime-types'), 9 | axios = require('axios'); 10 | 11 | const args = parseArgs(process.argv); 12 | const colors = { 13 | red: '\x1b[31m%s\x1b[0m', 14 | green: '\x1b[32m%s\x1b[0m' 15 | }; 16 | 17 | let relativePath, env = "development"; 18 | let outputPath = ''; 19 | const gzip = args.gzip || args.g; 20 | 21 | if (args.path) relativePath = args.path; 22 | else if (args.p) relativePath = args.p; 23 | 24 | if (args.env) env = args.env; 25 | else if (args.e) env = args.e; 26 | 27 | if (args.outputPath) outputPath = args.outputPath; 28 | else if (args.o) outputPath = args.o; 29 | 30 | const buildDirectoryName = relativePath || "build"; 31 | const buildPath = path.join(process.cwd(), buildDirectoryName); 32 | 33 | let credentials = { ...awsCredentials[env] }; 34 | const options = credentials.options || {}; 35 | delete credentials.options; 36 | if (!credentials) throw "There are no credentials for environment: " + env; 37 | 38 | const uploader = new S3({ 39 | region: credentials.region, 40 | apiVersion: "2006-03-01", 41 | credentials: credentials, 42 | httpOptions: { 43 | timeout: 0 44 | } 45 | }); 46 | 47 | const findRegexOrString = (array, value) => 48 | array.some(string => 49 | // if it is a regex string.test exist as a func, else is undefined 50 | string.test ? string.test(value) : value === string 51 | ); 52 | 53 | const emptyBucket = (bucketName, callback) => { 54 | let params = { 55 | Bucket: bucketName 56 | }; 57 | 58 | uploader.listObjectsV2(params, (err, data) => { 59 | if (err) return callback(err); 60 | 61 | if (data.Contents.length == 0) callback(); 62 | 63 | params = { Bucket: bucketName }; 64 | params.Delete = { Objects: [] }; 65 | data.Contents.forEach(content => { 66 | if (!options.preserveFiles || !findRegexOrString(options.preserveFiles, content.Key)) { 67 | params.Delete.Objects.push({ Key: content.Key }); 68 | console.log(colors.red, `[Deleting]: ${content.Key}`); 69 | } 70 | }); 71 | 72 | uploader.deleteObjects(params, (err, _) => { 73 | if (err) return callback(err); 74 | if (data.isTruncated) emptyBucket(bucketName, callback); 75 | else callback(); 76 | }); 77 | }); 78 | } 79 | 80 | const isMainFile = file => file === "index.html"; 81 | 82 | const getMetadataObject = metaData => { 83 | if (gzip) { 84 | return { ...metaData, ContentEncoding: 'gzip' }; 85 | } else { 86 | return metaData; 87 | } 88 | }; 89 | 90 | const read = file => { 91 | return new Promise((resolve, _) => { 92 | fs.readFile(path.join(buildPath, file), (_, data) => { 93 | var base64data = Buffer.from(data, "binary"); 94 | resolve(base64data); 95 | }); 96 | }).then(base64data => { 97 | return new Promise((resolve, reject) => { 98 | const fileIsMainFile = isMainFile(file); 99 | let CacheControl = fileIsMainFile ? "max-age=0" : "max-age=6048000"; 100 | let Expires = fileIsMainFile ? 0 : 6048000; 101 | const fileKey = outputPath ? `${outputPath}/${file}` : file; 102 | const metaData = { 103 | Bucket: credentials.bucket, 104 | Key: fileKey, 105 | Body: base64data, 106 | ACL: "public-read", 107 | CacheControl: CacheControl, 108 | Expires: Expires, 109 | ContentType: mime.lookup(file) 110 | }; 111 | uploader.putObject(getMetadataObject(metaData), 112 | (error) => { 113 | if (error) return reject(error); 114 | console.log(colors.green, `Successfully uploaded ${file}`); 115 | return resolve(file); 116 | } 117 | ); 118 | }); 119 | }); 120 | } 121 | 122 | const recursiveRead = (dir, done) => { 123 | var results = []; 124 | fs.readdir(dir, (err, list) => { 125 | if (err) return done(err); 126 | var pending = list.length; 127 | if (!pending) return done(null, results); 128 | list.forEach(file => { 129 | file = path.resolve(dir, file); 130 | fs.stat(file, (_, stat) => { 131 | if (stat && stat.isDirectory()) { 132 | recursiveRead(file, (_, res) => { 133 | results = results.concat(res); 134 | if (!--pending) done(null, results); 135 | }); 136 | } else { 137 | results.push(file); 138 | if (!--pending) done(null, results); 139 | } 140 | }); 141 | }); 142 | }); 143 | }; 144 | 145 | const persistDeployTime = (deployTime, metricsInfo) => { 146 | if (metricsInfo.baseUrl) { 147 | const axiosApi = axios.create({ 148 | baseURL: metricsInfo.baseUrl, 149 | timeout: 10000 150 | }); 151 | body = { 152 | env, 153 | tech: metricsInfo.tech, 154 | repo_name: metricsInfo.repoName, 155 | metrics: [ 156 | { 157 | name: 'deploy-time', 158 | version: '1.0', 159 | value: `${deployTime}` 160 | } 161 | ] 162 | } 163 | axiosApi.post('/metrics', body).then(() => console.log('Deploy time saved succesfully'), error => console.log(`Deploy time couldn't be saved error: ${error}`)); 164 | } else { 165 | console.log("Deploy time couldn't be saved due to the missing api base url") 166 | } 167 | } 168 | 169 | const uploadFiles = () => recursiveRead(buildPath, (err, results) => { 170 | if (err) throw err; 171 | const start = new Date(); 172 | Promise.all( 173 | results 174 | .map(result => 175 | read(result.slice( 176 | result.indexOf(buildDirectoryName) + buildDirectoryName.length + 1 177 | )) 178 | ) 179 | ).then(() => { 180 | var params = { 181 | DistributionId: credentials.distributionId, 182 | InvalidationBatch: { 183 | CallerReference: new Date().getTime().toString(), 184 | Paths: { 185 | Quantity: 1, 186 | Items: ['/*'] 187 | } 188 | } 189 | }; 190 | 191 | if (credentials.distributionId) { 192 | new Cloudfront({ credentials }).createInvalidation( 193 | params, 194 | (err, data) => { 195 | if (err) console.log(err, err.stack); 196 | else console.log(data); 197 | } 198 | ); 199 | } 200 | deployTime = (new Date().getTime() - start.getTime()) / 1000; 201 | if (awsCredentials.metrics) { 202 | persistDeployTime(deployTime, awsCredentials.metrics) 203 | } 204 | }).catch(err => { 205 | console.log(err); 206 | }); 207 | }); 208 | 209 | emptyBucket(credentials.bucket, err => { 210 | console.log(colors.green, "Cleaning the bucket..."); 211 | if (err) { 212 | console.error(err, err.stack); 213 | } else { 214 | console.log(colors.green, "Uploading new build..."); 215 | uploadFiles(); 216 | } 217 | }); 218 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const shell = require('shelljs'); 2 | 3 | shell.config.silent = true; 4 | 5 | test('TEST FAILURE: Build not found -p', () => expect(shell.exec('node ./script/s3.js -p ../sakjdh/./..asd').stderr).toContain('Error: ENOENT: no such file or directory')); 6 | 7 | test('TEST FAILURE: Build not found --path', () => expect(shell.exec('node ./script/s3.js --path ../sakjdh/./..asd').stderr).toContain('Error: ENOENT: no such file or directory')); 8 | 9 | test('TEST FAILURE: Env not found -e', () => expect(shell.exec('node ./script/s3.js -e sakjdh').stderr).toContain('There are no credentials for environment:')); 10 | 11 | test('TEST FAILURE: Env not found --env', () => expect(shell.exec('node ./script/s3.js --env sakjdh').stderr).toContain('There are no credentials for environment:')); 12 | --------------------------------------------------------------------------------