From 18deca484b7a8c2c1566bab18d1e07096419dba6 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Thu, 9 Jul 2020 16:47:49 +0200 Subject: [PATCH 01/29] chore: add prettier style as usual for graasp apps --- .eslintrc | 6 ++---- .prettierrc | 6 ++++++ package.json | 1 + yarn.lock | 12 ++++++++++++ 4 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 .prettierrc diff --git a/.eslintrc b/.eslintrc index 0e91e04..c112a09 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,5 @@ { - "extends": "airbnb-base", + "extends": ["airbnb-base", "prettier"], "env": { "node": true, "mocha": true @@ -9,9 +9,7 @@ "no-underscore-dangle": [ "error", { - "allow": [ - "_id" - ] + "allow": ["_id"] } ], "import/no-named-as-default": 0 diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..dc75c8a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "all", + "tabWidth": 2, + "semi": true, + "singleQuote": true +} diff --git a/package.json b/package.json index 402473f..0f6ac18 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "babel-eslint": "10.1.0", "eslint": "6.8.0", "eslint-config-airbnb-base": "14.1.0", + "eslint-config-prettier": "6.11.0", "eslint-plugin-import": "2.20.1", "husky": "4.2.3", "npm-run-all": "4.1.5", diff --git a/yarn.lock b/yarn.lock index 95daabb..b10e6bb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2027,6 +2027,13 @@ eslint-config-airbnb-base@14.1.0: object.assign "^4.1.0" object.entries "^1.1.1" +eslint-config-prettier@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" + integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== + dependencies: + get-stdin "^6.0.0" + eslint-import-resolver-node@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" @@ -2432,6 +2439,11 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" From d248ef062087c54624ab552a698adb5f33bd665d Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Thu, 9 Jul 2020 16:49:43 +0200 Subject: [PATCH 02/29] feat: add basic new command --- src/createCli.js | 53 +++++++++++++++++++++++++++++++----------------- src/deploy.js | 16 +++++++++++++++ src/test.sh | 3 +++ 3 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 src/deploy.js create mode 100755 src/test.sh diff --git a/src/createCli.js b/src/createCli.js index ffbd4b8..fbaf519 100644 --- a/src/createCli.js +++ b/src/createCli.js @@ -1,5 +1,6 @@ import yargs from 'yargs'; import prompt from './prompt'; +import deploy from './deploy'; import { DEFAULT_STARTER } from './config'; const promisify = (fn) => (...args) => { @@ -33,26 +34,41 @@ const createCli = (argv) => { .command({ command: 'new', desc: 'Create new Graasp app.', - builder: (_) => _.option('s', { - alias: 'starter', - type: 'string', - default: DEFAULT_STARTER, - describe: `Set starter. Defaults to ${DEFAULT_STARTER}`, - }).option('f', { - alias: 'framework', - type: 'string', - describe: 'Set development framework (e.g. React, Angular)', - }).option('t', { - alias: 'type', - choices: ['app', 'lab'], - describe: 'Type of application (app or lab)', - }).option('p', { - alias: 'path', - type: 'string', - describe: 'Path where project directory will be set up.', - }), + builder: (_) => + _.option('s', { + alias: 'starter', + type: 'string', + default: DEFAULT_STARTER, + describe: `Set starter. Defaults to ${DEFAULT_STARTER}`, + }) + .option('f', { + alias: 'framework', + type: 'string', + describe: 'Set development framework (e.g. React, Angular)', + }) + .option('t', { + alias: 'type', + choices: ['app', 'lab'], + describe: 'Type of application (app or lab)', + }) + .option('p', { + alias: 'path', + type: 'string', + describe: 'Path where project directory will be set up.', + }), handler: promisify(prompt), }) + .command({ + command: 'deploy', + desc: 'Deploy the Graasp app', + builder: (_) => + _.option('p', { + alias: 'path', + type: 'string', + describe: 'Path to the Graasp app that shall be deployed', + }), + handler: promisify(deploy), + }) .wrap(cli.terminalWidth()) .demandCommand(1, 'Pass --help to see all available commands and options.') .strict() @@ -61,5 +77,4 @@ const createCli = (argv) => { .parse(argv.slice(2)); }; - export default createCli; diff --git a/src/deploy.js b/src/deploy.js new file mode 100644 index 0000000..7669c52 --- /dev/null +++ b/src/deploy.js @@ -0,0 +1,16 @@ +import { spawn } from './utils'; + +const deploy = async (opts) => { + const { path } = opts; + + console.error(`path passed '${path}'`); + console.log(process.cwd()); + try { + await spawn('./src/test.sh'); + } catch (e) { + console.error(e); + } + return true; +}; + +export default deploy; diff --git a/src/test.sh b/src/test.sh new file mode 100755 index 0000000..97199d4 --- /dev/null +++ b/src/test.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +echo 'anay' From 70c76dc69e0c19a39e9b529e46e4a4186c16b8cd Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Fri, 10 Jul 2020 14:40:03 +0200 Subject: [PATCH 03/29] feat: load dev env and deploy to aws --- package.json | 3 + src/deploy.js | 90 +++++++++++++++++++--- src/deploy.sh | 125 +++++++++++++++++++++++++++++++ yarn.lock | 204 +++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 411 insertions(+), 11 deletions(-) create mode 100755 src/deploy.sh diff --git a/package.json b/package.json index 0f6ac18..9fa8aa4 100644 --- a/package.json +++ b/package.json @@ -56,14 +56,17 @@ }, "dependencies": { "@babel/polyfill": "7.8.7", + "aws-sdk": "2.713.0", "bson-objectid": "1.3.0", "del": "4.1.1", + "dotenv": "8.2.0", "execa": "1.0.0", "fs-exists-cached": "1.0.0", "fs-extra": "7.0.1", "hosted-git-info": "2.7.1", "inquirer": "6.2.2", "lodash": "4.17.15", + "s3-node-client": "4.4.4", "yargs": "12.0.5" } } diff --git a/src/deploy.js b/src/deploy.js index 7669c52..81ba281 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -1,15 +1,85 @@ -import { spawn } from './utils'; +import AWS from 'aws-sdk'; +import s3 from 's3-node-client'; +import dotenv from 'dotenv'; + +const path = require('path'); + +// default build directory +const BUILD = 'build/'; + +/** + * Returns an object with all variables loaded from a environment + * @param {string} environmentName is the suffix after .env.* + */ const deploy = async (opts) => { - const { path } = opts; - - console.error(`path passed '${path}'`); - console.log(process.cwd()); - try { - await spawn('./src/test.sh'); - } catch (e) { - console.error(e); - } + // const { path } = opts; + + const usageMessage = console.log( + 'usage: $0 [-e ] [-v ] [-b ]', + ); + + console.log(`Exectued with path: ${opts.path}`); + console.log(usageMessage); + + // fetch environment variables + dotenv.config({ path: path.resolve(process.cwd(), '.env.dev') }); + /* eslint-disable no-unused-vars */ + const { + REACT_APP_GRAASP_DEVELOPER_ID, + REACT_APP_GRAASP_APP_ID, + REACT_APP_GRAASP_DOMAIN, + REACT_APP_HOST, + REACT_APP_VERSION, + REACT_APP_BASE, + NODE_ENV, + BUCKET, + AWS_DEFAULT_REGION, + AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY, + DISTRIBUTION, + } = process.env; + /* eslint-enable no-unused-vars */ + + AWS.config.getCredentials(function (err) { + if (err) console.error(err.stack); + // credentials not loaded + else { + console.log('Access key:', AWS.config.credentials.accessKeyId); + } + }); + + const APP_PATH = `${REACT_APP_GRAASP_DEVELOPER_ID}/${REACT_APP_GRAASP_APP_ID}/${REACT_APP_VERSION}`; + + const client = s3.createClient({ s3Client: new AWS.S3() }); + + const params = { + localDir: BUILD, + deleteRemoved: true, // default false, whether to remove s3 objects + // that have no corresponding local file. + + s3Params: { + Bucket: BUCKET, + Prefix: APP_PATH, + // other options supported by putObject, except Body and ContentLength. + // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property + }, + }; + const uploader = client.uploadDir(params); + uploader.on('error', function (err) { + console.error('unable to sync:', err.stack); + }); + uploader.on('progress', function () { + console.log('progress', uploader.progressAmount, uploader.progressTotal); + }); + uploader.on('end', function () { + console.log('done uploading'); + }); + + console.log( + `published app to https://${REACT_APP_HOST}/${APP_PATH}/index.html`, + ); + return true; }; diff --git a/src/deploy.sh b/src/deploy.sh new file mode 100755 index 0000000..fe3ef1e --- /dev/null +++ b/src/deploy.sh @@ -0,0 +1,125 @@ +#!/bin/bash + +# fail the build on any failed command +set -e + +usage() { + echo "usage: $0 [-e ] [-v ] [-b ]" 1>&2 + exit 1 +} + +# default build directory +BUILD="build/" + +# default version +REACT_APP_VERSION="latest" + +# validates versioning e.g. v0.1.0 +validate_version() { + # regex matching version numbers + rx='^v([0-9]+\.){0,2}(\*|[0-9]+)$' + if [[ $1 =~ $rx ]]; then + echo "info: validated version $1" + REACT_APP_VERSION=$1 + else + echo "error: unable to validate version '$1'" 1>&2 + echo "format is '${rx}'" + exit 1 + fi +} + + +# validates that build directory exists +validate_build() { + if [ -d $1 ]; then + echo "info: validated build directory $1" + BUILD=$1 + else + echo "error: build directory '$1' does not exist" 1>&2 + exit 1 + fi +} + +# validates that environment file exists +validate_env() { + if [ -f $1 ]; then + echo "info: validated environment file $1" + source ${1} + else + echo "error: environment file '$1' does not exist" 1>&2 + exit 1 + fi +} + +# parse command line arguments +while getopts "e:v:b:" opt; do + case ${opt} in + e) + e=${OPTARG} + validate_env ${e} + ;; + b) + b=${OPTARG} + validate_build ${b} + ;; + v) + v=${OPTARG} + validate_version ${v} + ;; + \?) + echo "error: invalid option '-$OPTARG'" 1>&2 + exit 1 + ;; + esac +done + +# ensure the correct variables are defined +if \ + [ -z "${REACT_APP_HOST}" ] || \ + [ -z "${REACT_APP_GRAASP_DEVELOPER_ID}" ] || \ + [ -z "${REACT_APP_GRAASP_APP_ID}" ]; then + echo "error: environment variables REACT_APP_GRAASP_APP_ID, REACT_APP_GRAASP_DEVELOPER_ID and/or REACT_APP_HOST are not defined" 1>&2 + echo "error: you can specify them through a .env file in the app root folder" 1>&2 + echo "error: or through another file specified with the -e flag" 1>&2 + exit 1 +fi + +# ensure the correct aws credentials are defined +if \ + [ -z "${BUCKET}" ] || \ + [ -z "${AWS_ACCESS_KEY_ID}" ] || \ + [ -z "${AWS_SECRET_ACCESS_KEY}" ]; then + echo "error: environment variables BUCKET, AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY are not defined" 1>&2 + echo "error: make sure you setup your credentials file correctly using the scripts/setup.sh script" 1>&2 + echo "error: and contact your favourite Graasp engineer if you keep running into trouble" 1>&2 + exit 1 +fi + +export AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} +export AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} + +echo "info: publishing app ${REACT_APP_GRAASP_APP_ID} version ${REACT_APP_VERSION}" + +APP_DIR=${BUCKET}/${REACT_APP_GRAASP_DEVELOPER_ID}/${REACT_APP_GRAASP_APP_ID}/${REACT_APP_VERSION}/ + +# make sure you do not use the word PATH as a variable because it overrides the PATH environment variable +APP_PATH=${REACT_APP_GRAASP_DEVELOPER_ID}/${REACT_APP_GRAASP_APP_ID}/${REACT_APP_VERSION} + +# sync s3 bucket +aws s3 sync ${BUILD} s3://${APP_DIR} --delete + +# todo: allow cache invalidations per app once it is supported by cloudfront +# see: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html + +# ensure the correct distribution variables are defined +if \ + [ -z "${DISTRIBUTION}" ]; then + echo "error: environment variable DISTRIBUTION is not defined" 1>&2 + echo "error: contact your favourite Graasp engineer if you keep running into trouble" 1>&2 + exit 1 +fi + +# invalidate cloudfront distribution +aws cloudfront create-invalidation --distribution-id ${DISTRIBUTION} --paths /${APP_PATH}/* + +echo "published app to https://${REACT_APP_HOST}/${APP_PATH}/index.html" diff --git a/yarn.lock b/yarn.lock index b10e6bb..b4cc88f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1169,6 +1169,36 @@ atob@^2.1.1: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +aws-sdk@2.67.0: + version "2.67.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.67.0.tgz#c0fc3a4343cfc631265d9dd6bc50ba709401f857" + integrity sha1-wPw6Q0PPxjEmXZ3WvFC6cJQB+Fc= + dependencies: + buffer "5.0.6" + crypto-browserify "1.0.9" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.0.1" + xml2js "0.4.17" + xmlbuilder "4.2.1" + +aws-sdk@2.713.0: + version "2.713.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.713.0.tgz#e87404ddcba093d36afafb48f119ec66f654a83f" + integrity sha512-axR1eOVn134KXJc1IT+Au2TXcK6oswY+4nvGe5GfU3pXeehhe0xNeP9Bw9yF36TRBxuvu4IJ2hRHDKma05smgA== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.3.2" + xml2js "0.4.19" + babel-eslint@10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" @@ -1210,6 +1240,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-js@^1.0.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" + integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -1279,6 +1314,23 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== +buffer@4.9.2: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +buffer@5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.6.tgz#2ea669f7eec0b6eda05b08f8b5ff661b28573588" + integrity sha1-LqZp9+7Atu2gWwj4tf9mGyhXNYg= + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -1799,6 +1851,11 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" +crypto-browserify@1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-1.0.9.tgz#cc5449685dfb85eb11c9828acc7cb87ab5bbfcc0" + integrity sha1-zFRJaF37hesRyYKKzHy4erW7/MA= + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -1929,6 +1986,11 @@ dot-prop@^3.0.0: dependencies: is-obj "^1.0.0" +dotenv@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + dotgitignore@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dotgitignore/-/dotgitignore-2.1.0.tgz#a4b15a4e4ef3cf383598aaf1dfa4a04bcc089b7b" @@ -2169,6 +2231,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= +events@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= + execa@1.0.0, execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -2248,6 +2315,13 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fd-slicer@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" + integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= + dependencies: + pend "~1.2.0" + figures@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec" @@ -2328,6 +2402,11 @@ find-versions@^3.2.0: dependencies: semver-regex "^2.0.0" +findit2@~2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/findit2/-/findit2-2.2.3.tgz#58a466697df8a6205cdfdbf395536b8bd777a5f6" + integrity sha1-WKRmaX34piBc39vzlVNri9d3pfY= + flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -2570,7 +2649,7 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@~4.1.4: version "4.1.15" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== @@ -2672,6 +2751,11 @@ iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +ieee754@1.1.13, ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -3056,6 +3140,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +jmespath@0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" + integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc= + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -3263,6 +3352,11 @@ lodash@4.17.15, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.0.0: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== + lodash@^4.17.11, lodash@^4.2.1: version "4.17.13" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.13.tgz#0bdc3a6adc873d2f4e0c4bac285df91b64fc7b93" @@ -3399,6 +3493,11 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" +mime@~2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.3.1.tgz#b1621c54d63b97c47d3cfe7f7215f7d64517c369" + integrity sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg== + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -3459,6 +3558,13 @@ mkdirp@^0.5.1: dependencies: minimist "0.0.8" +mkdirp@~0.5.0: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + modify-values@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" @@ -3899,6 +4005,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + pidtree@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.0.tgz#f6fada10fccc9f99bf50e90d0b23d72c9ebc2e6b" @@ -3992,6 +4103,11 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -4002,6 +4118,11 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + quick-lru@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" @@ -4283,6 +4404,11 @@ rimraf@2.6.3, rimraf@^2.5.2, rimraf@^2.6.3: dependencies: glob "^7.1.3" +rimraf@~2.2.8: + version "2.2.8" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" + integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI= + run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -4311,6 +4437,21 @@ rxjs@^6.5.3: dependencies: tslib "^1.9.0" +s3-node-client@4.4.4: + version "4.4.4" + resolved "https://registry.yarnpkg.com/s3-node-client/-/s3-node-client-4.4.4.tgz#705f1cea2a7d2330773352c649008475fb767b6c" + integrity sha1-cF8c6ip9IzB3M1LGSQCEdft2e2w= + dependencies: + aws-sdk "2.67.0" + fd-slicer "~1.0.0" + findit2 "~2.2.3" + graceful-fs "~4.1.4" + mime "~2.3.1" + mkdirp "~0.5.0" + pend "~1.2.0" + rimraf "~2.2.8" + streamsink "~1.2.0" + safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -4333,6 +4474,16 @@ safe-regex@^1.1.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sax@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" + integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o= + +sax@>=0.6.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" @@ -4571,6 +4722,11 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +streamsink@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/streamsink/-/streamsink-1.2.0.tgz#efafee9f1e22d3591ed7de3dcaa95c3f5e79f73c" + integrity sha1-76/unx4i01ke1949yqlcP1559zw= + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -4923,6 +5079,14 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= +url@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + integrity sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -4933,6 +5097,16 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +uuid@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + integrity sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE= + +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + v8-compile-cache@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" @@ -5002,6 +5176,34 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" +xml2js@0.4.17: + version "0.4.17" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.17.tgz#17be93eaae3f3b779359c795b419705a8817e868" + integrity sha1-F76T6q4/O3eTWceVtBlwWogX6Gg= + dependencies: + sax ">=0.6.0" + xmlbuilder "^4.1.0" + +xml2js@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" + integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== + dependencies: + sax ">=0.6.0" + xmlbuilder "~9.0.1" + +xmlbuilder@4.2.1, xmlbuilder@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-4.2.1.tgz#aa58a3041a066f90eaa16c2f5389ff19f3f461a5" + integrity sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU= + dependencies: + lodash "^4.0.0" + +xmlbuilder@~9.0.1: + version "9.0.7" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= + xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" From 305efa0e4dc05cf5776a0facb2f8bd3d57d9511d Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Fri, 10 Jul 2020 14:40:56 +0200 Subject: [PATCH 04/29] refactor: rm test file and rm obsolet promisify() --- src/createCli.js | 12 +++--------- src/test.sh | 3 --- src/utils.js | 2 ++ 3 files changed, 5 insertions(+), 12 deletions(-) delete mode 100755 src/test.sh diff --git a/src/createCli.js b/src/createCli.js index fbaf519..db1a910 100644 --- a/src/createCli.js +++ b/src/createCli.js @@ -3,13 +3,6 @@ import prompt from './prompt'; import deploy from './deploy'; import { DEFAULT_STARTER } from './config'; -const promisify = (fn) => (...args) => { - Promise.resolve(fn(...args)).then( - () => process.exit(0), - // err => report.panic(err) - ); -}; - const createCli = (argv) => { const cli = yargs(); @@ -56,7 +49,7 @@ const createCli = (argv) => { type: 'string', describe: 'Path where project directory will be set up.', }), - handler: promisify(prompt), + handler: prompt, }) .command({ command: 'deploy', @@ -65,9 +58,10 @@ const createCli = (argv) => { _.option('p', { alias: 'path', type: 'string', + default: '.', describe: 'Path to the Graasp app that shall be deployed', }), - handler: promisify(deploy), + handler: deploy, }) .wrap(cli.terminalWidth()) .demandCommand(1, 'Pass --help to see all available commands and options.') diff --git a/src/test.sh b/src/test.sh deleted file mode 100755 index 97199d4..0000000 --- a/src/test.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -echo 'anay' diff --git a/src/utils.js b/src/utils.js index 657d8b9..e1c751b 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,7 @@ import execa from 'execa'; +// TODO: this is not optimal naming since it may cause confusion with 'spawn()' from 'child_process' module; + // use execa to spawn a better child process /* eslint-disable-next-line import/prefer-default-export */ export const spawn = (cmd, opts = { stdio: 'inherit' }) => { From 53925c37ebedd81c98664048033c67822d97f6b8 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Fri, 10 Jul 2020 15:33:24 +0200 Subject: [PATCH 05/29] refactor: remove accidentally added bash script --- src/deploy.sh | 125 -------------------------------------------------- 1 file changed, 125 deletions(-) delete mode 100755 src/deploy.sh diff --git a/src/deploy.sh b/src/deploy.sh deleted file mode 100755 index fe3ef1e..0000000 --- a/src/deploy.sh +++ /dev/null @@ -1,125 +0,0 @@ -#!/bin/bash - -# fail the build on any failed command -set -e - -usage() { - echo "usage: $0 [-e ] [-v ] [-b ]" 1>&2 - exit 1 -} - -# default build directory -BUILD="build/" - -# default version -REACT_APP_VERSION="latest" - -# validates versioning e.g. v0.1.0 -validate_version() { - # regex matching version numbers - rx='^v([0-9]+\.){0,2}(\*|[0-9]+)$' - if [[ $1 =~ $rx ]]; then - echo "info: validated version $1" - REACT_APP_VERSION=$1 - else - echo "error: unable to validate version '$1'" 1>&2 - echo "format is '${rx}'" - exit 1 - fi -} - - -# validates that build directory exists -validate_build() { - if [ -d $1 ]; then - echo "info: validated build directory $1" - BUILD=$1 - else - echo "error: build directory '$1' does not exist" 1>&2 - exit 1 - fi -} - -# validates that environment file exists -validate_env() { - if [ -f $1 ]; then - echo "info: validated environment file $1" - source ${1} - else - echo "error: environment file '$1' does not exist" 1>&2 - exit 1 - fi -} - -# parse command line arguments -while getopts "e:v:b:" opt; do - case ${opt} in - e) - e=${OPTARG} - validate_env ${e} - ;; - b) - b=${OPTARG} - validate_build ${b} - ;; - v) - v=${OPTARG} - validate_version ${v} - ;; - \?) - echo "error: invalid option '-$OPTARG'" 1>&2 - exit 1 - ;; - esac -done - -# ensure the correct variables are defined -if \ - [ -z "${REACT_APP_HOST}" ] || \ - [ -z "${REACT_APP_GRAASP_DEVELOPER_ID}" ] || \ - [ -z "${REACT_APP_GRAASP_APP_ID}" ]; then - echo "error: environment variables REACT_APP_GRAASP_APP_ID, REACT_APP_GRAASP_DEVELOPER_ID and/or REACT_APP_HOST are not defined" 1>&2 - echo "error: you can specify them through a .env file in the app root folder" 1>&2 - echo "error: or through another file specified with the -e flag" 1>&2 - exit 1 -fi - -# ensure the correct aws credentials are defined -if \ - [ -z "${BUCKET}" ] || \ - [ -z "${AWS_ACCESS_KEY_ID}" ] || \ - [ -z "${AWS_SECRET_ACCESS_KEY}" ]; then - echo "error: environment variables BUCKET, AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY are not defined" 1>&2 - echo "error: make sure you setup your credentials file correctly using the scripts/setup.sh script" 1>&2 - echo "error: and contact your favourite Graasp engineer if you keep running into trouble" 1>&2 - exit 1 -fi - -export AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} -export AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} - -echo "info: publishing app ${REACT_APP_GRAASP_APP_ID} version ${REACT_APP_VERSION}" - -APP_DIR=${BUCKET}/${REACT_APP_GRAASP_DEVELOPER_ID}/${REACT_APP_GRAASP_APP_ID}/${REACT_APP_VERSION}/ - -# make sure you do not use the word PATH as a variable because it overrides the PATH environment variable -APP_PATH=${REACT_APP_GRAASP_DEVELOPER_ID}/${REACT_APP_GRAASP_APP_ID}/${REACT_APP_VERSION} - -# sync s3 bucket -aws s3 sync ${BUILD} s3://${APP_DIR} --delete - -# todo: allow cache invalidations per app once it is supported by cloudfront -# see: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_aws-services-that-work-with-iam.html - -# ensure the correct distribution variables are defined -if \ - [ -z "${DISTRIBUTION}" ]; then - echo "error: environment variable DISTRIBUTION is not defined" 1>&2 - echo "error: contact your favourite Graasp engineer if you keep running into trouble" 1>&2 - exit 1 -fi - -# invalidate cloudfront distribution -aws cloudfront create-invalidation --distribution-id ${DISTRIBUTION} --paths /${APP_PATH}/* - -echo "published app to https://${REACT_APP_HOST}/${APP_PATH}/index.html" From 68fb33b1e06ce604ccbef441d40177e0a1850bc0 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Fri, 10 Jul 2020 17:00:06 +0200 Subject: [PATCH 06/29] feat: add options and validation --- src/config.js | 5 +++ src/createCli.js | 29 +++++++++++--- src/deploy.js | 98 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 110 insertions(+), 22 deletions(-) diff --git a/src/config.js b/src/config.js index bfd1fd7..9a51841 100644 --- a/src/config.js +++ b/src/config.js @@ -14,3 +14,8 @@ export const AWS_SECRET_ACCESS_KEY_LENGTH = 40; // file names export const GRAASP_IGNORE_FILE = '.graaspignore'; + +// deploy settings +export const DEFAULT_BUILD_DIR = 'build/'; +export const DEFAULT_APP_VERSION = 'latest'; +export const DEFAULT_ENV = '.env.dev'; diff --git a/src/createCli.js b/src/createCli.js index db1a910..9b07e41 100644 --- a/src/createCli.js +++ b/src/createCli.js @@ -1,7 +1,12 @@ import yargs from 'yargs'; import prompt from './prompt'; import deploy from './deploy'; -import { DEFAULT_STARTER } from './config'; +import { + DEFAULT_STARTER, + DEFAULT_BUILD_DIR, + DEFAULT_APP_VERSION, + DEFAULT_ENV, +} from './config'; const createCli = (argv) => { const cli = yargs(); @@ -55,12 +60,24 @@ const createCli = (argv) => { command: 'deploy', desc: 'Deploy the Graasp app', builder: (_) => - _.option('p', { - alias: 'path', + _.option('t', { + alias: 'tag', type: 'string', - default: '.', - describe: 'Path to the Graasp app that shall be deployed', - }), + default: DEFAULT_APP_VERSION, + describe: 'Tag the deployment with a version', + }) + .option('e', { + alias: 'env', + type: 'string', + default: DEFAULT_ENV, + describe: 'Environment used to load variables from', + }) + .option('b', { + alias: 'build', + type: 'string', + default: DEFAULT_BUILD_DIR, + describe: 'Path to the build directory that is deployed', + }), handler: deploy, }) .wrap(cli.terminalWidth()) diff --git a/src/deploy.js b/src/deploy.js index 81ba281..22712ca 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -1,29 +1,57 @@ import AWS from 'aws-sdk'; import s3 from 's3-node-client'; import dotenv from 'dotenv'; +import fs from 'fs'; -const path = require('path'); +const validateTag = (tag) => { + console.log(`warning: tag ${tag} is not validated. Needs to be implemented`); + return true; + /* Does not work yet ====================> Find out why... + // prettier-ignore + const pattern = new RegExp('^v([0-9]+\.){0,2}(\*|[0-9]+)$'); + if (tag === 'latest' || tag.test(pattern)) { + console.log(`info: validated tag ${tag}`); + return true; + } + console.error(`error: unable to validate version '${tag}'`); + return false; + */ +}; -// default build directory -const BUILD = 'build/'; +const validateEnv = async (env) => { + if (fs.existsSync(env)) { + console.log(`info: validated environment file ${env}`); + return true; + } + console.log(`error: environment file '${env}' does not exist`); + return false; +}; -/** - * Returns an object with all variables loaded from a environment - * @param {string} environmentName is the suffix after .env.* - */ +const validateBuild = async (build) => { + if (fs.existsSync(build)) { + console.log(`info: validated build directory ${build}`); + return true; + } + console.log(`error: build directory '${build}' does not exist`); + return false; +}; -const deploy = async (opts) => { - // const { path } = opts; +const varIsDefined = (variable) => { + return typeof variable !== 'undefined'; +}; - const usageMessage = console.log( - 'usage: $0 [-e ] [-v ] [-b ]', - ); +const deploy = async (opts) => { + const { tag, env, build } = opts; - console.log(`Exectued with path: ${opts.path}`); - console.log(usageMessage); + // Validate command options + if (!validateTag(tag) || !validateEnv(env) || !validateBuild(build)) { + console.error('Abort...'); + return false; + } // fetch environment variables - dotenv.config({ path: path.resolve(process.cwd(), '.env.dev') }); + // dotenv.config({ path: path.resolve(process.cwd(), '.env.dev') }); + dotenv.config({ path: env }); /* eslint-disable no-unused-vars */ const { REACT_APP_GRAASP_DEVELOPER_ID, @@ -41,6 +69,44 @@ const deploy = async (opts) => { } = process.env; /* eslint-enable no-unused-vars */ + // validate environment variables concerning the app + if ( + !varIsDefined(REACT_APP_HOST) || + !varIsDefined(REACT_APP_GRAASP_DEVELOPER_ID) || + !varIsDefined(REACT_APP_GRAASP_APP_ID) + ) { + console.error( + 'error: environment variables REACT_APP_GRAASP_APP_ID, REACT_APP_GRAASP_DEVELOPER_ID and/or REACT_APP_HOST are not defined', + ); + console.error( + 'error: you can specify them through a .env file in the app root folder', + ); + console.error('error: or through another file specified with the -e flag'); + return false; + } + + // validate environment variables concerning aws credentials + if ( + !varIsDefined(BUCKET) || + !varIsDefined(AWS_ACCESS_KEY_ID) || + !varIsDefined(AWS_SECRET_ACCESS_KEY) + ) { + console.error( + 'error: environment variables BUCKET, AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY are not defined', + ); + console.error( + 'error: make sure you setup your credentials file correctly using the scripts/setup.sh script', + ); + console.error( + 'error: and contact your favourite Graasp engineer if you keep running into trouble', + ); + } + + console.log( + `info: publishing app ${REACT_APP_GRAASP_APP_ID} version ${REACT_APP_VERSION}`, + ); + + // configure the deployment AWS.config.getCredentials(function (err) { if (err) console.error(err.stack); // credentials not loaded @@ -54,7 +120,7 @@ const deploy = async (opts) => { const client = s3.createClient({ s3Client: new AWS.S3() }); const params = { - localDir: BUILD, + localDir: build, deleteRemoved: true, // default false, whether to remove s3 objects // that have no corresponding local file. From 00f48a7c407f7cb26febe5056746df7aed19e3ce Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Fri, 10 Jul 2020 17:38:09 +0200 Subject: [PATCH 07/29] feat: invalidate cache after deployment --- src/deploy.js | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index 22712ca..754d642 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -69,7 +69,7 @@ const deploy = async (opts) => { } = process.env; /* eslint-enable no-unused-vars */ - // validate environment variables concerning the app + // ensure the correct app variables are defined if ( !varIsDefined(REACT_APP_HOST) || !varIsDefined(REACT_APP_GRAASP_DEVELOPER_ID) || @@ -85,7 +85,7 @@ const deploy = async (opts) => { return false; } - // validate environment variables concerning aws credentials + // ensure the correct aws credentials are defined if ( !varIsDefined(BUCKET) || !varIsDefined(AWS_ACCESS_KEY_ID) || @@ -146,6 +146,34 @@ const deploy = async (opts) => { `published app to https://${REACT_APP_HOST}/${APP_PATH}/index.html`, ); + // ensure the correct distribution variables are defined + if (!varIsDefined(DISTRIBUTION)) { + console.error('error: environment variable DISTRIBUTION is not defined'); + console.error( + 'error: contact your favourite Graasp engineer if you keep running into trouble', + ); + return false; + } + + // invalidate cloudfront distribution + const pathsToInvalidate = [`/${APP_PATH}/*`]; + const invalidationParams = { + DistributionId: DISTRIBUTION, + InvalidationBatch: { + CallerReference: new Date().toString(), + Paths: { + Quantity: pathsToInvalidate.length, + Items: pathsToInvalidate, + }, + }, + }; + const cloudfront = new AWS.CloudFront(); + cloudfront.createInvalidation(invalidationParams, function (err, data) { + if (err) console.log(err, err.stack); + // an error occurred + else console.log(data); // successful response + }); + return true; }; From 8ba826224b9f35c2d1e0313da84e8dceaea1abbd Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sat, 11 Jul 2020 16:38:52 +0200 Subject: [PATCH 08/29] fix: allow console output in eslint --- .eslintrc | 3 ++- src/createCli.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.eslintrc b/.eslintrc index c112a09..0c89313 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,6 +12,7 @@ "allow": ["_id"] } ], - "import/no-named-as-default": 0 + "no-console": "off", + "import/no-named-as-default": "off" } } diff --git a/src/createCli.js b/src/createCli.js index 9b07e41..ca73cf4 100644 --- a/src/createCli.js +++ b/src/createCli.js @@ -58,7 +58,7 @@ const createCli = (argv) => { }) .command({ command: 'deploy', - desc: 'Deploy the Graasp app', + desc: 'Deploy a Graasp app to AWS', builder: (_) => _.option('t', { alias: 'tag', From 96d3531e0f54111c9a8dfd3002945b2405239061 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sat, 11 Jul 2020 17:15:37 +0200 Subject: [PATCH 09/29] feat: add a fancy progress bar --- package.json | 1 + src/deploy.js | 21 ++++++++++++++------- yarn.lock | 13 +++++++++++++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 9fa8aa4..21f98aa 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "@babel/polyfill": "7.8.7", "aws-sdk": "2.713.0", "bson-objectid": "1.3.0", + "cli-progress": "3.8.2", "del": "4.1.1", "dotenv": "8.2.0", "execa": "1.0.0", diff --git a/src/deploy.js b/src/deploy.js index 754d642..f528a32 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -2,6 +2,7 @@ import AWS from 'aws-sdk'; import s3 from 's3-node-client'; import dotenv from 'dotenv'; import fs from 'fs'; +import cliProgress from 'cli-progress'; const validateTag = (tag) => { console.log(`warning: tag ${tag} is not validated. Needs to be implemented`); @@ -108,10 +109,9 @@ const deploy = async (opts) => { // configure the deployment AWS.config.getCredentials(function (err) { - if (err) console.error(err.stack); - // credentials not loaded - else { - console.log('Access key:', AWS.config.credentials.accessKeyId); + if (err) { + // credentials not loaded + console.error(err.stack); } }); @@ -131,19 +131,26 @@ const deploy = async (opts) => { // See: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property }, }; + const progressBar = new cliProgress.SingleBar( + {}, + cliProgress.Presets.shades_classic, + ); const uploader = client.uploadDir(params); uploader.on('error', function (err) { console.error('unable to sync:', err.stack); }); uploader.on('progress', function () { - console.log('progress', uploader.progressAmount, uploader.progressTotal); + progressBar.start(uploader.progressTotal, 0); + progressBar.update(uploader.progressAmount); }); uploader.on('end', function () { - console.log('done uploading'); + progressBar.stop(); + // TODO: insert here code that should be executed once the upload is done + // e.g. invalidate cache }); console.log( - `published app to https://${REACT_APP_HOST}/${APP_PATH}/index.html`, + `info: published app to https://${REACT_APP_HOST}/${APP_PATH}/index.html`, ); // ensure the correct distribution variables are defined diff --git a/yarn.lock b/yarn.lock index b4cc88f..38e408a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1482,6 +1482,14 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" +cli-progress@3.8.2: + version "3.8.2" + resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.8.2.tgz#abaf1fc6d6401351f16f068117a410554a0eb8c7" + integrity sha512-qRwBxLldMSfxB+YGFgNRaj5vyyHe1yMpVeDL79c+7puGujdKJHQHydgqXDcrkvQgJ5U/d3lpf6vffSoVVUftVQ== + dependencies: + colors "^1.1.2" + string-width "^4.2.0" + cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" @@ -1542,6 +1550,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colors@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + commander@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" From 7ac7faabd1a305a7a8ea36142e452a3110a63879 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sat, 11 Jul 2020 17:40:40 +0200 Subject: [PATCH 10/29] fix: tag validation and accidental async --- src/deploy.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index f528a32..54f73bf 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -5,21 +5,18 @@ import fs from 'fs'; import cliProgress from 'cli-progress'; const validateTag = (tag) => { - console.log(`warning: tag ${tag} is not validated. Needs to be implemented`); - return true; - /* Does not work yet ====================> Find out why... // prettier-ignore - const pattern = new RegExp('^v([0-9]+\.){0,2}(\*|[0-9]+)$'); - if (tag === 'latest' || tag.test(pattern)) { + // eslint-disable-next-line no-useless-escape + const pattern = new RegExp('v\\d+(\.\\d+){0,2}$'); + if (tag === 'latest' || pattern.test(tag)) { console.log(`info: validated tag ${tag}`); return true; } console.error(`error: unable to validate version '${tag}'`); return false; - */ }; -const validateEnv = async (env) => { +const validateEnv = (env) => { if (fs.existsSync(env)) { console.log(`info: validated environment file ${env}`); return true; @@ -28,7 +25,7 @@ const validateEnv = async (env) => { return false; }; -const validateBuild = async (build) => { +const validateBuild = (build) => { if (fs.existsSync(build)) { console.log(`info: validated build directory ${build}`); return true; From dc383ad271dd2863f27632abed13146e5b0d6eb2 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sat, 11 Jul 2020 17:41:55 +0200 Subject: [PATCH 11/29] chore: add comment to RegExp and backshlashes --- src/deploy.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/deploy.js b/src/deploy.js index 54f73bf..dd73ff3 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -5,6 +5,7 @@ import fs from 'fs'; import cliProgress from 'cli-progress'; const validateTag = (tag) => { + // Both compilation hints because of backslashes used in RegExp but unecessary by conception in JS Strings // prettier-ignore // eslint-disable-next-line no-useless-escape const pattern = new RegExp('v\\d+(\.\\d+){0,2}$'); From c37afb8f93f17d33ee56b8b04e3a5b3fdf0fc1db Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sat, 11 Jul 2020 17:43:20 +0200 Subject: [PATCH 12/29] refactor: rename varIsDefined() to isDefined() --- src/deploy.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index dd73ff3..020b1a2 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -35,7 +35,7 @@ const validateBuild = (build) => { return false; }; -const varIsDefined = (variable) => { +const isDefined = (variable) => { return typeof variable !== 'undefined'; }; @@ -70,9 +70,9 @@ const deploy = async (opts) => { // ensure the correct app variables are defined if ( - !varIsDefined(REACT_APP_HOST) || - !varIsDefined(REACT_APP_GRAASP_DEVELOPER_ID) || - !varIsDefined(REACT_APP_GRAASP_APP_ID) + !isDefined(REACT_APP_HOST) || + !isDefined(REACT_APP_GRAASP_DEVELOPER_ID) || + !isDefined(REACT_APP_GRAASP_APP_ID) ) { console.error( 'error: environment variables REACT_APP_GRAASP_APP_ID, REACT_APP_GRAASP_DEVELOPER_ID and/or REACT_APP_HOST are not defined', @@ -86,9 +86,9 @@ const deploy = async (opts) => { // ensure the correct aws credentials are defined if ( - !varIsDefined(BUCKET) || - !varIsDefined(AWS_ACCESS_KEY_ID) || - !varIsDefined(AWS_SECRET_ACCESS_KEY) + !isDefined(BUCKET) || + !isDefined(AWS_ACCESS_KEY_ID) || + !isDefined(AWS_SECRET_ACCESS_KEY) ) { console.error( 'error: environment variables BUCKET, AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY are not defined', @@ -152,7 +152,7 @@ const deploy = async (opts) => { ); // ensure the correct distribution variables are defined - if (!varIsDefined(DISTRIBUTION)) { + if (!isDefined(DISTRIBUTION)) { console.error('error: environment variable DISTRIBUTION is not defined'); console.error( 'error: contact your favourite Graasp engineer if you keep running into trouble', From 305cb3bdeba879e05a34dcf2576d95903bb29ee6 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sat, 11 Jul 2020 17:43:57 +0200 Subject: [PATCH 13/29] refactor: remove unused env varas from import --- src/deploy.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index 020b1a2..7c60b40 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -51,22 +51,16 @@ const deploy = async (opts) => { // fetch environment variables // dotenv.config({ path: path.resolve(process.cwd(), '.env.dev') }); dotenv.config({ path: env }); - /* eslint-disable no-unused-vars */ const { REACT_APP_GRAASP_DEVELOPER_ID, REACT_APP_GRAASP_APP_ID, - REACT_APP_GRAASP_DOMAIN, REACT_APP_HOST, REACT_APP_VERSION, - REACT_APP_BASE, - NODE_ENV, BUCKET, - AWS_DEFAULT_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, DISTRIBUTION, } = process.env; - /* eslint-enable no-unused-vars */ // ensure the correct app variables are defined if ( From 19eb2238ac5daba48d8e5412a37c04a5e5c87bba Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sat, 11 Jul 2020 17:45:58 +0200 Subject: [PATCH 14/29] refactor: replace unnamed func with arrow func --- src/deploy.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index 7c60b40..5417f87 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -100,7 +100,7 @@ const deploy = async (opts) => { ); // configure the deployment - AWS.config.getCredentials(function (err) { + AWS.config.getCredentials((err) => { if (err) { // credentials not loaded console.error(err.stack); @@ -128,14 +128,14 @@ const deploy = async (opts) => { cliProgress.Presets.shades_classic, ); const uploader = client.uploadDir(params); - uploader.on('error', function (err) { + uploader.on('error', (err) => { console.error('unable to sync:', err.stack); }); - uploader.on('progress', function () { + uploader.on('progress', () => { progressBar.start(uploader.progressTotal, 0); progressBar.update(uploader.progressAmount); }); - uploader.on('end', function () { + uploader.on('end', () => { progressBar.stop(); // TODO: insert here code that should be executed once the upload is done // e.g. invalidate cache @@ -167,7 +167,7 @@ const deploy = async (opts) => { }, }; const cloudfront = new AWS.CloudFront(); - cloudfront.createInvalidation(invalidationParams, function (err, data) { + cloudfront.createInvalidation(invalidationParams, (err, data) => { if (err) console.log(err, err.stack); // an error occurred else console.log(data); // successful response From 820b5f534c14898c3aeee335baa8caf75020c610 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sat, 11 Jul 2020 17:59:21 +0200 Subject: [PATCH 15/29] fix: minor changes based on feedback on pr --- src/config.js | 2 +- src/deploy.js | 8 ++++---- src/initStarter.js | 10 ++++++---- src/utils.js | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/config.js b/src/config.js index 9a51841..89bb429 100644 --- a/src/config.js +++ b/src/config.js @@ -16,6 +16,6 @@ export const AWS_SECRET_ACCESS_KEY_LENGTH = 40; export const GRAASP_IGNORE_FILE = '.graaspignore'; // deploy settings -export const DEFAULT_BUILD_DIR = 'build/'; +export const DEFAULT_BUILD_DIR = './build'; export const DEFAULT_APP_VERSION = 'latest'; export const DEFAULT_ENV = '.env.dev'; diff --git a/src/deploy.js b/src/deploy.js index 5417f87..f4ae379 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -1,4 +1,4 @@ -import AWS from 'aws-sdk'; +import aws from 'aws-sdk'; import s3 from 's3-node-client'; import dotenv from 'dotenv'; import fs from 'fs'; @@ -100,7 +100,7 @@ const deploy = async (opts) => { ); // configure the deployment - AWS.config.getCredentials((err) => { + aws.config.getCredentials((err) => { if (err) { // credentials not loaded console.error(err.stack); @@ -109,7 +109,7 @@ const deploy = async (opts) => { const APP_PATH = `${REACT_APP_GRAASP_DEVELOPER_ID}/${REACT_APP_GRAASP_APP_ID}/${REACT_APP_VERSION}`; - const client = s3.createClient({ s3Client: new AWS.S3() }); + const client = s3.createClient({ s3Client: new aws.S3() }); const params = { localDir: build, @@ -166,7 +166,7 @@ const deploy = async (opts) => { }, }, }; - const cloudfront = new AWS.CloudFront(); + const cloudfront = new aws.CloudFront(); cloudfront.createInvalidation(invalidationParams, (err, data) => { if (err) console.log(err, err.stack); // an error occurred diff --git a/src/initStarter.js b/src/initStarter.js index 17bf81b..e8dc099 100644 --- a/src/initStarter.js +++ b/src/initStarter.js @@ -8,7 +8,7 @@ import del from 'del'; import { sync as existsSync } from 'fs-exists-cached'; import { DEFAULT_PATH, DEFAULT_STARTER, GRAASP_IGNORE_FILE } from './config'; import writeEnvFiles from './writeEnvFiles'; -import { spawn } from './utils'; +import { spawnProcess } from './utils'; const readFile = util.promisify(fs.readFile); @@ -35,7 +35,9 @@ const install = async (rootPath) => { process.chdir(rootPath); try { - const cmd = shouldUseYarn() ? spawn('yarnpkg') : spawn('npm install'); + const cmd = shouldUseYarn() + ? spawnProcess('yarnpkg') + : spawnProcess('npm install'); await cmd; } finally { process.chdir(prevDir); @@ -87,7 +89,7 @@ const commit = async (rootPath) => { process.chdir(rootPath); try { - await spawn('git add -A', { stdio: 'ignore' }); + await spawnProcess('git add -A', { stdio: 'ignore' }); // cannot spawn this because of the way we are splitting the command execSync('git commit -m "chore: initial commit from graasp cli"', { @@ -130,7 +132,7 @@ const clone = async (hostInfo, rootPath) => { console.log(`creating new site from git: ${url}`); - await spawn(`git clone ${branch} ${url} ${rootPath} --single-branch`); + await spawnProcess(`git clone ${branch} ${url} ${rootPath} --single-branch`); console.log('created starter directory layout'); diff --git a/src/utils.js b/src/utils.js index e1c751b..e4cdbba 100644 --- a/src/utils.js +++ b/src/utils.js @@ -4,7 +4,7 @@ import execa from 'execa'; // use execa to spawn a better child process /* eslint-disable-next-line import/prefer-default-export */ -export const spawn = (cmd, opts = { stdio: 'inherit' }) => { +export const spawnProcess = (cmd, opts = { stdio: 'inherit' }) => { const [file, ...args] = cmd.split(/\s+/); return execa(file, args, opts); }; From ad0f6f65941564988ca55ee0654eb1f969b0099e Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sat, 11 Jul 2020 18:00:33 +0200 Subject: [PATCH 16/29] chore: remove obsolete comment --- src/utils.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/utils.js b/src/utils.js index e4cdbba..a5484ee 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,9 +1,7 @@ import execa from 'execa'; -// TODO: this is not optimal naming since it may cause confusion with 'spawn()' from 'child_process' module; - // use execa to spawn a better child process -/* eslint-disable-next-line import/prefer-default-export */ +// eslint-disable-next-line import/prefer-default-export export const spawnProcess = (cmd, opts = { stdio: 'inherit' }) => { const [file, ...args] = cmd.split(/\s+/); return execa(file, args, opts); From 5687b3b990efa55794492e5f57525da60d2028e7 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sat, 11 Jul 2020 18:19:21 +0200 Subject: [PATCH 17/29] refactor: factor validation out --- src/deploy.js | 74 +++++++++++++++++++++++++++++++-------------------- src/utils.js | 12 ++++++++- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index f4ae379..9d399ac 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -3,6 +3,7 @@ import s3 from 's3-node-client'; import dotenv from 'dotenv'; import fs from 'fs'; import cliProgress from 'cli-progress'; +import { isDefined } from './utils'; const validateTag = (tag) => { // Both compilation hints because of backslashes used in RegExp but unecessary by conception in JS Strings @@ -35,34 +36,11 @@ const validateBuild = (build) => { return false; }; -const isDefined = (variable) => { - return typeof variable !== 'undefined'; -}; - -const deploy = async (opts) => { - const { tag, env, build } = opts; - - // Validate command options - if (!validateTag(tag) || !validateEnv(env) || !validateBuild(build)) { - console.error('Abort...'); - return false; - } - - // fetch environment variables - // dotenv.config({ path: path.resolve(process.cwd(), '.env.dev') }); - dotenv.config({ path: env }); - const { - REACT_APP_GRAASP_DEVELOPER_ID, - REACT_APP_GRAASP_APP_ID, - REACT_APP_HOST, - REACT_APP_VERSION, - BUCKET, - AWS_ACCESS_KEY_ID, - AWS_SECRET_ACCESS_KEY, - DISTRIBUTION, - } = process.env; - - // ensure the correct app variables are defined +const validateAppVariables = ({ + REACT_APP_HOST, + REACT_APP_GRAASP_DEVELOPER_ID, + REACT_APP_GRAASP_APP_ID, +}) => { if ( !isDefined(REACT_APP_HOST) || !isDefined(REACT_APP_GRAASP_DEVELOPER_ID) || @@ -77,8 +55,14 @@ const deploy = async (opts) => { console.error('error: or through another file specified with the -e flag'); return false; } + return true; +}; - // ensure the correct aws credentials are defined +const validateAwsCredentialsVariables = ({ + BUCKET, + AWS_ACCESS_KEY_ID, + AWS_SECRET_ACCESS_KEY, +}) => { if ( !isDefined(BUCKET) || !isDefined(AWS_ACCESS_KEY_ID) || @@ -93,7 +77,39 @@ const deploy = async (opts) => { console.error( 'error: and contact your favourite Graasp engineer if you keep running into trouble', ); + return false; } + return true; +}; + +const deploy = async (opts) => { + const { tag, env, build } = opts; + + // validate command options + if (!validateTag(tag) || !validateEnv(env) || !validateBuild(build)) { + console.error('Abort...'); + return false; + } + + // set environment + dotenv.config({ path: env }); + + // validate environment variables + if ( + !validateAppVariables(process.env) || + !validateAwsCredentialsVariables(process.env) + ) { + return false; + } + + const { + REACT_APP_GRAASP_DEVELOPER_ID, + REACT_APP_GRAASP_APP_ID, + REACT_APP_HOST, + REACT_APP_VERSION, + BUCKET, + DISTRIBUTION, + } = process.env; console.log( `info: publishing app ${REACT_APP_GRAASP_APP_ID} version ${REACT_APP_VERSION}`, diff --git a/src/utils.js b/src/utils.js index a5484ee..2cc3433 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,8 +1,18 @@ import execa from 'execa'; // use execa to spawn a better child process -// eslint-disable-next-line import/prefer-default-export export const spawnProcess = (cmd, opts = { stdio: 'inherit' }) => { const [file, ...args] = cmd.split(/\s+/); return execa(file, args, opts); }; + +export const isDefined = (variable) => { + return typeof variable !== 'undefined'; +}; + +export const promisify = (fn) => (...args) => { + Promise.resolve(fn(...args)).then( + () => process.exit(0), + // err => report.panic(err) + ); +}; From cb5650adf0aec920c1deb9164d98b11a99907cdb Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Fri, 17 Jul 2020 09:09:03 +0200 Subject: [PATCH 18/29] fix: use proper tag validation --- src/deploy.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index 9d399ac..a787be0 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -9,7 +9,8 @@ const validateTag = (tag) => { // Both compilation hints because of backslashes used in RegExp but unecessary by conception in JS Strings // prettier-ignore // eslint-disable-next-line no-useless-escape - const pattern = new RegExp('v\\d+(\.\\d+){0,2}$'); + // const pattern = new RegExp('v\\d+(\.\\d+){0,2}$'); + const pattern = new RegExp('^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z]*)?$') if (tag === 'latest' || pattern.test(tag)) { console.log(`info: validated tag ${tag}`); return true; @@ -91,7 +92,7 @@ const deploy = async (opts) => { return false; } - // set environment + // load environment dotenv.config({ path: env }); // validate environment variables From 6b38e41c282b6d21f4821a17cebbd58154c031e9 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Fri, 17 Jul 2020 09:13:27 +0200 Subject: [PATCH 19/29] chore: add explanation to regexp --- src/deploy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deploy.js b/src/deploy.js index a787be0..e2cf0a0 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -7,9 +7,9 @@ import { isDefined } from './utils'; const validateTag = (tag) => { // Both compilation hints because of backslashes used in RegExp but unecessary by conception in JS Strings + // Furthermore, the escaption is needed so RegExp will interpret the String correctly. // prettier-ignore // eslint-disable-next-line no-useless-escape - // const pattern = new RegExp('v\\d+(\.\\d+){0,2}$'); const pattern = new RegExp('^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z]*)?$') if (tag === 'latest' || pattern.test(tag)) { console.log(`info: validated tag ${tag}`); From f4b79efc121e8986ae84d1528529cfdd36faec52 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Fri, 17 Jul 2020 09:20:37 +0200 Subject: [PATCH 20/29] fix: remove default env --- src/createCli.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/createCli.js b/src/createCli.js index ca73cf4..2a8389d 100644 --- a/src/createCli.js +++ b/src/createCli.js @@ -5,7 +5,6 @@ import { DEFAULT_STARTER, DEFAULT_BUILD_DIR, DEFAULT_APP_VERSION, - DEFAULT_ENV, } from './config'; const createCli = (argv) => { @@ -69,7 +68,6 @@ const createCli = (argv) => { .option('e', { alias: 'env', type: 'string', - default: DEFAULT_ENV, describe: 'Environment used to load variables from', }) .option('b', { From 3cf03668ec22d50b77ca30e026602f06bf3184f2 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Fri, 17 Jul 2020 09:32:33 +0200 Subject: [PATCH 21/29] refactor: replace isdefined() with lodash --- package.json | 2 +- src/deploy.js | 30 +++++++++++++++++------------- src/utils.js | 4 ---- yarn.lock | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 21f98aa..a314583 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "fs-extra": "7.0.1", "hosted-git-info": "2.7.1", "inquirer": "6.2.2", - "lodash": "4.17.15", + "lodash": "4.17.19", "s3-node-client": "4.4.4", "yargs": "12.0.5" } diff --git a/src/deploy.js b/src/deploy.js index e2cf0a0..d10e366 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -3,7 +3,7 @@ import s3 from 's3-node-client'; import dotenv from 'dotenv'; import fs from 'fs'; import cliProgress from 'cli-progress'; -import { isDefined } from './utils'; +import _ from 'lodash'; const validateTag = (tag) => { // Both compilation hints because of backslashes used in RegExp but unecessary by conception in JS Strings @@ -43,9 +43,9 @@ const validateAppVariables = ({ REACT_APP_GRAASP_APP_ID, }) => { if ( - !isDefined(REACT_APP_HOST) || - !isDefined(REACT_APP_GRAASP_DEVELOPER_ID) || - !isDefined(REACT_APP_GRAASP_APP_ID) + _.isUndefined(REACT_APP_HOST) || + _.isUndefined(REACT_APP_GRAASP_DEVELOPER_ID) || + _.isUndefined(REACT_APP_GRAASP_APP_ID) ) { console.error( 'error: environment variables REACT_APP_GRAASP_APP_ID, REACT_APP_GRAASP_DEVELOPER_ID and/or REACT_APP_HOST are not defined', @@ -65,9 +65,9 @@ const validateAwsCredentialsVariables = ({ AWS_SECRET_ACCESS_KEY, }) => { if ( - !isDefined(BUCKET) || - !isDefined(AWS_ACCESS_KEY_ID) || - !isDefined(AWS_SECRET_ACCESS_KEY) + _.isUndefined(BUCKET) || + _.isUndefined(AWS_ACCESS_KEY_ID) || + _.isUndefined(AWS_SECRET_ACCESS_KEY) ) { console.error( 'error: environment variables BUCKET, AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY are not defined', @@ -88,7 +88,7 @@ const deploy = async (opts) => { // validate command options if (!validateTag(tag) || !validateEnv(env) || !validateBuild(build)) { - console.error('Abort...'); + console.error('aborting deployment...'); return false; } @@ -113,7 +113,7 @@ const deploy = async (opts) => { } = process.env; console.log( - `info: publishing app ${REACT_APP_GRAASP_APP_ID} version ${REACT_APP_VERSION}`, + `publishing app ${REACT_APP_GRAASP_APP_ID} version ${REACT_APP_VERSION}`, ); // configure the deployment @@ -163,7 +163,7 @@ const deploy = async (opts) => { ); // ensure the correct distribution variables are defined - if (!isDefined(DISTRIBUTION)) { + if (_.isUndefined(DISTRIBUTION)) { console.error('error: environment variable DISTRIBUTION is not defined'); console.error( 'error: contact your favourite Graasp engineer if you keep running into trouble', @@ -185,9 +185,13 @@ const deploy = async (opts) => { }; const cloudfront = new aws.CloudFront(); cloudfront.createInvalidation(invalidationParams, (err, data) => { - if (err) console.log(err, err.stack); - // an error occurred - else console.log(data); // successful response + if (err) { + // an error occurred + console.error(err, err.stack); + } else { + // successful response + console.log(data); + } }); return true; diff --git a/src/utils.js b/src/utils.js index 2cc3433..0735d58 100644 --- a/src/utils.js +++ b/src/utils.js @@ -6,10 +6,6 @@ export const spawnProcess = (cmd, opts = { stdio: 'inherit' }) => { return execa(file, args, opts); }; -export const isDefined = (variable) => { - return typeof variable !== 'undefined'; -}; - export const promisify = (fn) => (...args) => { Promise.resolve(fn(...args)).then( () => process.exit(0), diff --git a/yarn.lock b/yarn.lock index 38e408a..b381618 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3365,7 +3365,7 @@ lodash@4.17.15, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@^4.0.0: +lodash@4.17.19, lodash@^4.0.0: version "4.17.19" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== From e85ac364209ec57c325ab7839fcb484ab28cef83 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sat, 18 Jul 2020 15:50:44 +0200 Subject: [PATCH 22/29] chore: enable linting warning for console log --- .eslintrc | 1 - 1 file changed, 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 0c89313..91b2eaa 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,7 +12,6 @@ "allow": ["_id"] } ], - "no-console": "off", "import/no-named-as-default": "off" } } From 2338ead532f028319a64d87de9cbb73cd9a5c96d Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sun, 26 Jul 2020 13:35:53 +0200 Subject: [PATCH 23/29] chore: try out promisify --- src/deploy.js | 33 +++++++++++++++++++++++++++++++-- yarn.lock | 2 +- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index d10e366..b112432 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -1,9 +1,10 @@ import aws from 'aws-sdk'; -import s3 from 's3-node-client'; +// import s3 from 's3-node-client'; import dotenv from 'dotenv'; import fs from 'fs'; -import cliProgress from 'cli-progress'; +// import cliProgress from 'cli-progress'; import _ from 'lodash'; +import { promisify } from './utils'; const validateTag = (tag) => { // Both compilation hints because of backslashes used in RegExp but unecessary by conception in JS Strings @@ -103,6 +104,10 @@ const deploy = async (opts) => { return false; } + const { REACT_APP_GRAASP_APP_ID, REACT_APP_VERSION } = process.env; + + /* + const { REACT_APP_GRAASP_DEVELOPER_ID, REACT_APP_GRAASP_APP_ID, @@ -112,10 +117,33 @@ const deploy = async (opts) => { DISTRIBUTION, } = process.env; +*/ + console.log( `publishing app ${REACT_APP_GRAASP_APP_ID} version ${REACT_APP_VERSION}`, ); + const getAwsCredentialsPromise = promisify(aws.config.getCredentials); + console.log('Before promise'); + getAwsCredentialsPromise() + .then((err) => { + console.log('In then promises'); + if (err) { + // credentials not loaded + console.error(err.stack); + } + }) + .catch((err) => { + console.log('In catch promises'); + if (err) { + // credentials not loaded + console.error(err.stack); + } + }); + console.log('After promise'); + + return false; + /* // configure the deployment aws.config.getCredentials((err) => { if (err) { @@ -195,6 +223,7 @@ const deploy = async (opts) => { }); return true; + */ }; export default deploy; diff --git a/yarn.lock b/yarn.lock index e3ce6e9..6b50296 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3365,7 +3365,7 @@ lodash@4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@4.17.19, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.2.1: +lodash@4.17.19, lodash@^4.0.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.2.1: version "4.17.19" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== From 449109249e87ab0c28aa3c26802d93519b705292 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sun, 26 Jul 2020 13:38:02 +0200 Subject: [PATCH 24/29] chore: force commit with eslint errors --- src/deploy.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index b112432..5141379 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -1,8 +1,8 @@ import aws from 'aws-sdk'; -// import s3 from 's3-node-client'; +import s3 from 's3-node-client'; import dotenv from 'dotenv'; import fs from 'fs'; -// import cliProgress from 'cli-progress'; +import cliProgress from 'cli-progress'; import _ from 'lodash'; import { promisify } from './utils'; @@ -104,10 +104,6 @@ const deploy = async (opts) => { return false; } - const { REACT_APP_GRAASP_APP_ID, REACT_APP_VERSION } = process.env; - - /* - const { REACT_APP_GRAASP_DEVELOPER_ID, REACT_APP_GRAASP_APP_ID, @@ -117,8 +113,6 @@ const deploy = async (opts) => { DISTRIBUTION, } = process.env; -*/ - console.log( `publishing app ${REACT_APP_GRAASP_APP_ID} version ${REACT_APP_VERSION}`, ); @@ -143,7 +137,7 @@ const deploy = async (opts) => { console.log('After promise'); return false; - /* + // configure the deployment aws.config.getCredentials((err) => { if (err) { @@ -223,7 +217,6 @@ const deploy = async (opts) => { }); return true; - */ }; export default deploy; From 62ea0f8f538c7a31f7d86e220917085687046330 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sun, 26 Jul 2020 14:53:24 +0200 Subject: [PATCH 25/29] feat: load sync aws credentials --- src/deploy.js | 82 +++++++++++++++++++++------------------------------ 1 file changed, 34 insertions(+), 48 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index 5141379..4f1f351 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -4,7 +4,7 @@ import dotenv from 'dotenv'; import fs from 'fs'; import cliProgress from 'cli-progress'; import _ from 'lodash'; -import { promisify } from './utils'; +// import { promisify } from './utils'; const validateTag = (tag) => { // Both compilation hints because of backslashes used in RegExp but unecessary by conception in JS Strings @@ -13,28 +13,28 @@ const validateTag = (tag) => { // eslint-disable-next-line no-useless-escape const pattern = new RegExp('^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\-[0-9A-Za-z]*)?$') if (tag === 'latest' || pattern.test(tag)) { - console.log(`info: validated tag ${tag}`); + console.info(`validated tag ${tag}`); return true; } - console.error(`error: unable to validate version '${tag}'`); + console.error(`unable to validate version '${tag}'`); return false; }; const validateEnv = (env) => { if (fs.existsSync(env)) { - console.log(`info: validated environment file ${env}`); + console.info(`validated environment file ${env}`); return true; } - console.log(`error: environment file '${env}' does not exist`); + console.error(`environment file '${env}' does not exist`); return false; }; const validateBuild = (build) => { if (fs.existsSync(build)) { - console.log(`info: validated build directory ${build}`); + console.info(`validated build directory ${build}`); return true; } - console.log(`error: build directory '${build}' does not exist`); + console.error(`build directory '${build}' does not exist`); return false; }; @@ -49,12 +49,12 @@ const validateAppVariables = ({ _.isUndefined(REACT_APP_GRAASP_APP_ID) ) { console.error( - 'error: environment variables REACT_APP_GRAASP_APP_ID, REACT_APP_GRAASP_DEVELOPER_ID and/or REACT_APP_HOST are not defined', + 'environment variables REACT_APP_GRAASP_APP_ID, REACT_APP_GRAASP_DEVELOPER_ID and/or REACT_APP_HOST are not defined', ); console.error( - 'error: you can specify them through a .env file in the app root folder', + 'you can specify them through a .env file in the app root folder', ); - console.error('error: or through another file specified with the -e flag'); + console.error('or through another file specified with the -e flag'); return false; } return true; @@ -71,19 +71,32 @@ const validateAwsCredentialsVariables = ({ _.isUndefined(AWS_SECRET_ACCESS_KEY) ) { console.error( - 'error: environment variables BUCKET, AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY are not defined', + 'environment variables BUCKET, AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY are not defined', ); console.error( - 'error: make sure you setup your credentials file correctly using the scripts/setup.sh script', + 'make sure you setup your credentials file correctly using the scripts/setup.sh script', ); console.error( - 'error: and contact your favourite Graasp engineer if you keep running into trouble', + 'and contact your favourite Graasp engineer if you keep running into trouble', ); return false; } return true; }; +const loadAwsCredentials = async () => { + const awsCredentials = new aws.Credentials(); + await awsCredentials.getPromise().then( + async () => true, + (err) => { + console.error(err.stack); + return false; + }, + ); + // set the AWS credentials into the global object + aws.config.credentials = awsCredentials; +}; + const deploy = async (opts) => { const { tag, env, build } = opts; @@ -93,7 +106,7 @@ const deploy = async (opts) => { return false; } - // load environment + // load environment variables dotenv.config({ path: env }); // validate environment variables @@ -113,38 +126,11 @@ const deploy = async (opts) => { DISTRIBUTION, } = process.env; - console.log( + console.info( `publishing app ${REACT_APP_GRAASP_APP_ID} version ${REACT_APP_VERSION}`, ); - const getAwsCredentialsPromise = promisify(aws.config.getCredentials); - console.log('Before promise'); - getAwsCredentialsPromise() - .then((err) => { - console.log('In then promises'); - if (err) { - // credentials not loaded - console.error(err.stack); - } - }) - .catch((err) => { - console.log('In catch promises'); - if (err) { - // credentials not loaded - console.error(err.stack); - } - }); - console.log('After promise'); - - return false; - - // configure the deployment - aws.config.getCredentials((err) => { - if (err) { - // credentials not loaded - console.error(err.stack); - } - }); + loadAwsCredentials(); const APP_PATH = `${REACT_APP_GRAASP_DEVELOPER_ID}/${REACT_APP_GRAASP_APP_ID}/${REACT_APP_VERSION}`; @@ -180,15 +166,15 @@ const deploy = async (opts) => { // e.g. invalidate cache }); - console.log( - `info: published app to https://${REACT_APP_HOST}/${APP_PATH}/index.html`, + console.info( + `published app to https://${REACT_APP_HOST}/${APP_PATH}/index.html`, ); // ensure the correct distribution variables are defined if (_.isUndefined(DISTRIBUTION)) { - console.error('error: environment variable DISTRIBUTION is not defined'); + console.error('environment variable DISTRIBUTION is not defined'); console.error( - 'error: contact your favourite Graasp engineer if you keep running into trouble', + 'contact your favourite Graasp engineer if you keep running into trouble', ); return false; } @@ -212,7 +198,7 @@ const deploy = async (opts) => { console.error(err, err.stack); } else { // successful response - console.log(data); + console.info(data); } }); From 89e88212f644c5b9a783259500d239ea32732bbe Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sun, 26 Jul 2020 14:56:30 +0200 Subject: [PATCH 26/29] chore: update flag descriptions --- src/createCli.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/createCli.js b/src/createCli.js index 2a8389d..fcca0d4 100644 --- a/src/createCli.js +++ b/src/createCli.js @@ -68,13 +68,13 @@ const createCli = (argv) => { .option('e', { alias: 'env', type: 'string', - describe: 'Environment used to load variables from', + describe: 'Environment file used to load variables from', }) .option('b', { alias: 'build', type: 'string', default: DEFAULT_BUILD_DIR, - describe: 'Path to the build directory that is deployed', + describe: 'Path to the build directory that will be deployed', }), handler: deploy, }) From 7a288e8b7be432b995a731bf6800b664382eb68a Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sun, 26 Jul 2020 14:59:56 +0200 Subject: [PATCH 27/29] chore: assemble error outputs in together --- src/deploy.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/deploy.js b/src/deploy.js index 4f1f351..b5b3dc6 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -49,12 +49,9 @@ const validateAppVariables = ({ _.isUndefined(REACT_APP_GRAASP_APP_ID) ) { console.error( - 'environment variables REACT_APP_GRAASP_APP_ID, REACT_APP_GRAASP_DEVELOPER_ID and/or REACT_APP_HOST are not defined', + `environment variables REACT_APP_GRAASP_APP_ID, REACT_APP_GRAASP_DEVELOPER_ID and/or REACT_APP_HOST are not defined \n + you can specify them through a .env file in the app root folder or through another file specified with the -e flag`, ); - console.error( - 'you can specify them through a .env file in the app root folder', - ); - console.error('or through another file specified with the -e flag'); return false; } return true; @@ -71,13 +68,8 @@ const validateAwsCredentialsVariables = ({ _.isUndefined(AWS_SECRET_ACCESS_KEY) ) { console.error( - 'environment variables BUCKET, AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY are not defined', - ); - console.error( - 'make sure you setup your credentials file correctly using the scripts/setup.sh script', - ); - console.error( - 'and contact your favourite Graasp engineer if you keep running into trouble', + `environment variables BUCKET, AWS_ACCESS_KEY_ID and/or AWS_SECRET_ACCESS_KEY are not defined. \n + make sure you setup your credentials file correctly using the scripts/setup.sh script and contact your favourite Graasp engineer if you keep running into trouble`, ); return false; } From 28a0dc57aca9c7c6be40d20ee51bf38865597d99 Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sun, 26 Jul 2020 15:05:07 +0200 Subject: [PATCH 28/29] chore: remove obsolete default env variable --- src/config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/config.js b/src/config.js index 89bb429..9e2d689 100644 --- a/src/config.js +++ b/src/config.js @@ -18,4 +18,3 @@ export const GRAASP_IGNORE_FILE = '.graaspignore'; // deploy settings export const DEFAULT_BUILD_DIR = './build'; export const DEFAULT_APP_VERSION = 'latest'; -export const DEFAULT_ENV = '.env.dev'; From 3e19e195aca7caffd8e46b0972ac85801e3640bf Mon Sep 17 00:00:00 2001 From: Uchendu Nwachukwu Date: Sun, 9 Aug 2020 21:56:44 +0200 Subject: [PATCH 29/29] chore: first version of promise for upload --- package.json | 2 ++ src/deploy.js | 79 +++++++++++++++++++++++++++++++++++++++++++++++++-- yarn.lock | 14 ++++++++- 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a314583..9e83b03 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ }, "dependencies": { "@babel/polyfill": "7.8.7", + "async": "3.2.0", "aws-sdk": "2.713.0", "bson-objectid": "1.3.0", "cli-progress": "3.8.2", @@ -67,6 +68,7 @@ "hosted-git-info": "2.7.1", "inquirer": "6.2.2", "lodash": "4.17.19", + "recursive-readdir": "2.2.2", "s3-node-client": "4.4.4", "yargs": "12.0.5" } diff --git a/src/deploy.js b/src/deploy.js index b5b3dc6..2c9c229 100644 --- a/src/deploy.js +++ b/src/deploy.js @@ -1,11 +1,15 @@ import aws from 'aws-sdk'; -import s3 from 's3-node-client'; +// import s3 from 's3-node-client'; import dotenv from 'dotenv'; import fs from 'fs'; import cliProgress from 'cli-progress'; import _ from 'lodash'; // import { promisify } from './utils'; +const path = require('path'); +const async = require('async'); +const readdir = require('recursive-readdir'); + const validateTag = (tag) => { // Both compilation hints because of backslashes used in RegExp but unecessary by conception in JS Strings // Furthermore, the escaption is needed so RegExp will interpret the String correctly. @@ -89,6 +93,51 @@ const loadAwsCredentials = async () => { aws.config.credentials = awsCredentials; }; +function getFiles(dirPath) { + return fs.existsSync(dirPath) ? readdir(dirPath) : []; +} + +async function uploadToS3(s3, uploadPath, bucket) { + const rootFolder = process.cwd(); + const filesToUpload = await getFiles(path.resolve(rootFolder, uploadPath)); + + console.log(filesToUpload); + + return new Promise((resolve, reject) => { + async.eachOfLimit( + filesToUpload, + 10, + async.asyncify(async (file) => { + const Key = file.replace(`${rootFolder}/`, ''); + console.log(`uploading: [${Key}]`); + return new Promise((res, rej) => { + s3.upload( + { + Key, + Bucket: bucket, + Body: fs.readFileSync(file), + }, + // eslint-disable-next-line + (err) => { + if (err) { + return rej(new Error(err)); + } + res({ result: true }); + }, + ); + }); + }), + // eslint-disable-next-line + (err) => { + if (err) { + return reject(new Error(err)); + } + resolve({ result: true }); + }, + ); + }); +} + const deploy = async (opts) => { const { tag, env, build } = opts; @@ -124,8 +173,34 @@ const deploy = async (opts) => { loadAwsCredentials(); + // eslint-disable-next-line const APP_PATH = `${REACT_APP_GRAASP_DEVELOPER_ID}/${REACT_APP_GRAASP_APP_ID}/${REACT_APP_VERSION}`; + const uploadFolder = './build'; + + // The name of the bucket that you have created + + const s3 = new aws.S3(); + + await uploadToS3(s3, uploadFolder, BUCKET) + .then(() => { + console.log('upload complete!'); + }) + .catch((err) => { + console.error(err.message); + process.exit(1); + }); + + // ensure the correct distribution variables are defined + if (_.isUndefined(DISTRIBUTION)) { + console.error('environment variable DISTRIBUTION is not defined'); + console.error( + 'contact your favourite Graasp engineer if you keep running into trouble', + ); + return false; + } + + /* const client = s3.createClient({ s3Client: new aws.S3() }); const params = { @@ -157,7 +232,7 @@ const deploy = async (opts) => { // TODO: insert here code that should be executed once the upload is done // e.g. invalidate cache }); - +*/ console.info( `published app to https://${REACT_APP_HOST}/${APP_PATH}/index.html`, ); diff --git a/yarn.lock b/yarn.lock index 6b50296..e59334c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1164,6 +1164,11 @@ async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== +async@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" + integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw== + atob@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" @@ -3516,7 +3521,7 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -4218,6 +4223,13 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +recursive-readdir@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" + integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + dependencies: + minimatch "3.0.4" + redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"