-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deploy cmd #42
base: main
Are you sure you want to change the base?
Deploy cmd #42
Changes from 10 commits
18deca4
d248ef0
70c76dc
305efa0
53925c3
68fb33b
00f48a7
8ba8262
96d3531
7ac7faa
dc383ad
c37afb8
305cb3b
19eb223
820b5f5
ad0f6f6
5687b3b
cb5650a
6b38e41
f4b79ef
3cf0366
e85ac36
baaa792
2338ead
4491092
62ea0f8
89e8821
7a288e8
28a0dc5
cfa7135
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ugGit, please consider this comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @juancarlosfarah Good question. As this was not the case in the previous There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just saw that this can't be enforced. So, I suggest to simply let the validation error to pop up. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
|
@@ -53,14 +58,26 @@ const createCli = (argv) => { | |
}) | ||
.command({ | ||
command: 'deploy', | ||
desc: 'Deploy the Graasp app', | ||
desc: 'Deploy a Graasp app to AWS', | ||
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', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Environment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ugGit, please consider this comment. |
||
}) | ||
.option('b', { | ||
alias: 'build', | ||
type: 'string', | ||
default: DEFAULT_BUILD_DIR, | ||
describe: 'Path to the build directory that is deployed', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ugGit, please consider this comment. |
||
}), | ||
handler: deploy, | ||
}) | ||
.wrap(cli.terminalWidth()) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,109 @@ | ||
import AWS from 'aws-sdk'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's stick to either There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ugGit, I don't see the change. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just pushed the latest changes. Please check again |
||
import s3 from 's3-node-client'; | ||
import dotenv from 'dotenv'; | ||
import fs from 'fs'; | ||
import cliProgress from 'cli-progress'; | ||
|
||
const path = require('path'); | ||
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}$'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure this patter is the same as the one in the bash file. Consider this one:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that I haven't checked if it's JS compliant. Might be good to add some basic tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ugGit, please consider this comment. |
||
if (tag === 'latest' || pattern.test(tag)) { | ||
console.log(`info: validated tag ${tag}`); | ||
return true; | ||
} | ||
console.error(`error: unable to validate version '${tag}'`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For these, no need to include There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ugGit, please consider this comment. |
||
return false; | ||
}; | ||
|
||
// default build directory | ||
const BUILD = 'build/'; | ||
const validateEnv = (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 = (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 isDefined = (variable) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
return typeof variable !== 'undefined'; | ||
}; | ||
|
||
const usageMessage = console.log( | ||
'usage: $0 [-e <path/to/file>] [-v <version string>] [-b <path/to/build>]', | ||
); | ||
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...'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return false; | ||
} | ||
|
||
// fetch environment variables | ||
dotenv.config({ path: path.resolve(process.cwd(), '.env.dev') }); | ||
/* eslint-disable no-unused-vars */ | ||
// 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_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); | ||
// ensure the correct app variables are defined | ||
if ( | ||
!isDefined(REACT_APP_HOST) || | ||
juancarlosfarah marked this conversation as resolved.
Show resolved
Hide resolved
|
||
!isDefined(REACT_APP_GRAASP_DEVELOPER_ID) || | ||
!isDefined(REACT_APP_GRAASP_APP_ID) | ||
) { | ||
console.error( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to separate into three messages. Just one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ugGit, you forgot to remove the |
||
'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; | ||
} | ||
|
||
// ensure the correct aws credentials are defined | ||
if ( | ||
!isDefined(BUCKET) || | ||
juancarlosfarah marked this conversation as resolved.
Show resolved
Hide resolved
|
||
!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', | ||
); | ||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ugGit, I think that you can use this promise version of getting the credentials. https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Credentials.html#getPromise-property |
||
AWS.config.getCredentials((err) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you need to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @juancarlosfarah Absolutely correct. But currently not enough time to include it in this PR. Consequently, the commands risk not to be executed in the correct chronological order. |
||
if (err) { | ||
// credentials not loaded | ||
console.error(err.stack); | ||
} | ||
}); | ||
|
||
|
@@ -54,7 +112,7 @@ const deploy = async (opts) => { | |
const client = s3.createClient({ s3Client: new AWS.S3() }); | ||
|
||
const params = { | ||
localDir: BUILD, | ||
localDir: build, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did not see comment on left side for the delete removed comments:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @juancarlosfarah Please let me know if here is still something pending |
||
deleteRemoved: true, // default false, whether to remove s3 objects | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put both lines of comment above |
||
// that have no corresponding local file. | ||
|
||
|
@@ -65,21 +123,56 @@ 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) { | ||
uploader.on('error', (err) => { | ||
console.error('unable to sync:', err.stack); | ||
}); | ||
uploader.on('progress', function () { | ||
console.log('progress', uploader.progressAmount, uploader.progressTotal); | ||
uploader.on('progress', () => { | ||
progressBar.start(uploader.progressTotal, 0); | ||
progressBar.update(uploader.progressAmount); | ||
}); | ||
uploader.on('end', function () { | ||
console.log('done uploading'); | ||
uploader.on('end', () => { | ||
progressBar.stop(); | ||
// TODO: insert here code that should be executed once the upload is done | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be done. You can always create helper functions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @juancarlosfarah Yes, you are right. But I would require more time to do this. Might consider implementing this when finalizing the |
||
// 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`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need for |
||
); | ||
|
||
// ensure the correct distribution variables are defined | ||
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', | ||
); | ||
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, (err, data) => { | ||
if (err) console.log(err, err.stack); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Always use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, put comments above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ugGit, please consider this comment. |
||
// an error occurred | ||
else console.log(data); // successful response | ||
}); | ||
|
||
return true; | ||
}; | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to turn this off, as it is a warning. I like to keep this on b/c it reminds me that we should use a
logger
, but for now it's fine. There are very nice loggers for CLIs, but you can leave it off for now if you want.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Answered here #42 (comment)