Commit c88404cb authored by Vladimir Kiryakov's avatar Vladimir Kiryakov
Browse files

sync ui with stack output data

parent 0e223737
......@@ -39,6 +39,7 @@ Options
Sync Web to s3.
Options
- *stackName* - AWS CloudFormation Stack Name default deviceops-hello-world-frontend
- *updateEnv* - Updates .env file by stack data **default** *true*
`yarn describe-stacks`
Returns the stack description.
......
......@@ -4,7 +4,7 @@ const fs = require('fs');
const prog = require('caporal');
const {
describeStacks, createOrUpdateStack, changeRegion, syncUI, deleteStack,
describeStacks, createOrUpdateStack, changeRegion, syncUIWithStackData, build, updateEnvVariables, deleteStack,
DEFAULT_INSTANCE_TYPE, DEFAULT_STACK_NAME, DEFAULT_REGION
} = require('./s3-service');
......@@ -37,10 +37,9 @@ prog
prog
.command('publish', 'Sync Web to s3')
.option('--stackName <stackName>', 'Stack Name', NO_VALIDATE, DEFAULT_STACK_NAME)
.option('--updateEnv <updateEnv>', 'Updates .env file by stack data', NO_VALIDATE, true)
.action((args, options) => {
syncUI(options.stackName).then(({stdout, stderr, websiteURL}) => {
console.log(`${stdout}`);
console.error(`${stderr}`);
syncUIWithStackData(options.stackName, options.updateEnv).then(({websiteURL}) => {
console.log(`build has been synced to s3 bucket`);
console.log(`website URL: ${websiteURL}`);
}).catch((err) => {
......
/* global process */
/* global Promise */
const fs = require('fs');
const {exec} = require('child_process');
const AWS = require('aws-sdk');
const _ = require('lodash');
const execa = require('execa');
const DEFAULT_STACK_NAME = 'deviceops-hello-world-frontend';
const DEFAULT_INSTANCE_TYPE = 't2.micro';
const DEFAULT_REGION = 'us-east-1';
const REACT_APP_MQTT_TOPIC = '/gateway/localhost.localdomain/sensors';
const REACT_APP_MQTT_URL = 'ws://broker.mqttdashboard.com:8000/mqtt';
let cloudformation = new AWS.CloudFormation({region: DEFAULT_REGION});
......@@ -16,7 +20,7 @@ const changeRegion = (region) => {
cloudformation = new AWS.CloudFormation({region})
};
const describeStacks = (stackName) => {
const describeStacks = (stackName = DEFAULT_STACK_NAME) => {
return new Promise((resolve, reject) => {
cloudformation.describeStacks({
StackName: stackName
......@@ -29,32 +33,58 @@ const describeStacks = (stackName) => {
});
};
const syncUI = (stackName) => {
const syncUIWithStackData = (stackName, updateEnv) => {
return describeStacks(stackName)
.then((data) => {
const stackOutputs = _.get(data, 'Stacks[0].Outputs', []);
const s3BucketNameOutput = _.find(stackOutputs, {OutputKey: 'S3BucketName'});
const s3BucketName = _.get(s3BucketNameOutput, 'OutputValue');
const websiteURLOutpup = _.find(stackOutputs, {OutputKey: 'WebsiteURL'});
const websiteURL = _.get(websiteURLOutpup, 'OutputValue');
const S3BucketName = _.get(s3BucketNameOutput, 'OutputValue');
if (!S3BucketName) return Promise.reject('no S3BucketSecureURL in template output');
const instanceIPAddressOutpup = _.find(stackOutputs, {OutputKey: 'InstanceIPAddress'});
const instanceIPAddress = _.get(instanceIPAddressOutpup, 'OutputValue');
const mqttUrl = `ws://${instanceIPAddress}:8000/`;
if (!s3BucketName) return Promise.reject('no S3BucketSecureURL in template output');
return sync(`${__dirname}/../build`, S3BucketName).then(({stdout, stderr}) => {
return {stdout, stderr, websiteURL}
});
const updateThenSync = () => build()
.then(() => sync(`${__dirname}/../build`, s3BucketName).then(() => {
return {websiteURL}
}));
if (updateEnv) {
return updateEnvVariables(false, mqttUrl)
.then(() => updateThenSync())
} else {
return updateThenSync();
}
})
};
const build = () => {
const run = execa('react-scripts', ['build']);
run.stdout.pipe(process.stdout);
run.stderr.pipe(process.stderr);
return run;
};
const sync = (dirPath, bucket) => {
const run = execa(`aws`, ['s3', 'sync', dirPath, bucket, '--acl', 'public-read', '--delete']);
run.stdout.pipe(process.stdout);
run.stderr.pipe(process.stderr);
return run;
};
const updateEnvVariables = (debug = false, mqttUrl = REACT_APP_MQTT_URL, mqttTopic = REACT_APP_MQTT_TOPIC) => {
return new Promise((resolve, reject) => {
exec(`aws s3 sync ${dirPath} ${bucket} --acl public-read --delete`, (err, stdout, stderr) => {
if (err) {
return reject(err);
}
resolve({stdout, stderr})
});
fs.writeFile(`${__dirname}/../.env`,
`REACT_APP_DEBUG=${debug}\nREACT_APP_MQTT_TOPIC=${mqttTopic}\nREACT_APP_MQTT_URL=${mqttUrl}`,
(err) => err ? reject(err) : resolve())
});
};
......@@ -132,7 +162,9 @@ module.exports = {
DEFAULT_REGION,
changeRegion,
describeStacks,
syncUI,
build,
updateEnvVariables,
syncUIWithStackData,
sync,
createStack,
updateStack,
......
......@@ -7,6 +7,7 @@
"bootstrap": "^4.0.0",
"caporal": "^0.10.0",
"classnames": "^2.2.5",
"execa": "^0.10.0",
"history": "^4.7.2",
"lodash": "^4.17.5",
"moment": "^2.22.0",
......@@ -38,7 +39,7 @@
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"deploy": "node cli/app.js deploy",
"publish": "react-scripts build && node cli/app.js publish",
"publish": "node cli/app.js publish",
"describe-stacks": "node cli/app.js describe-stacks"
},
"devDependencies": {
......
......@@ -2038,6 +2038,16 @@ cross-spawn@5.1.0, cross-spawn@^5.0.1, cross-spawn@^5.1.0:
shebang-command "^1.2.0"
which "^1.2.9"
cross-spawn@^6.0.0:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
dependencies:
nice-try "^1.0.4"
path-key "^2.0.1"
semver "^5.5.0"
shebang-command "^1.2.0"
which "^1.2.9"
cryptiles@2.x.x:
version "2.0.5"
resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8"
......@@ -2896,6 +2906,18 @@ exec-sh@^0.2.0:
dependencies:
merge "^1.1.3"
execa@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50"
dependencies:
cross-spawn "^6.0.0"
get-stream "^3.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.0.0"
execa@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
......@@ -5448,6 +5470,10 @@ next-tick@1:
version "1.0.0"
resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
nice-try@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
no-case@^2.2.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
......@@ -5893,7 +5919,7 @@ path-is-inside@^1.0.1, path-is-inside@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
path-key@^2.0.0:
path-key@^2.0.0, path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
......@@ -7313,7 +7339,7 @@ semver-diff@^2.0.0:
dependencies:
semver "^5.0.3"
"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1:
"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment