Skip to content

Commit cc411cc

Browse files
committed
Merge branch 'develop', prepare 0.21.0
2 parents a525fe7 + f365a5c commit cc411cc

File tree

6 files changed

+235
-104
lines changed

6 files changed

+235
-104
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ All the configuration of your projects happens automatically. So after running t
1717

1818
Currently, Exoframe understands and can deploy the following project types:
1919

20-
1. static html based projects - will be deployed using [nginx](http://hub.docker.com/_/nginx) image
21-
2. node.js based projects - will be deployed using [node:alpine](https://hub.docker.com/_/node) image
22-
3. docker based project - will be deployed using your [Dockerfile](https://docs.docker.com/engine/reference/builder/)
23-
4. docker-compose based project - will be deployed using your [docker-compose](https://docs.docker.com/compose/compose-file/) file
20+
1. Static html based projects - will be deployed using [nginx](http://hub.docker.com/_/nginx) image
21+
2. Node.js based projects - will be deployed using [Node:latest](https://hub.docker.com/_/node) image
22+
3. Docker based project - will be deployed using your [Dockerfile](https://docs.docker.com/engine/reference/builder/)
23+
4. Docker-Compose based project - will be deployed using your [docker-compose](https://docs.docker.com/compose/compose-file/) file
2424

2525
To run Exoframe you need two parts - Exoframe CLI on your local machine and [Exoframe server](https://github.com/exoframejs/exoframe-server) on your server with Docker.
2626

docs/Basics.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
| token | Generate new deployment token |
1818
| login | Login into Exoframe server |
1919
| endpoint [url] | Selects or adds the endpoint of Exoframe server |
20+
| rm-endpoint [url] | Removes an existing endpoint of Exoframe server |
21+
| update [target] | Gets current versions or updates given target |
2022
| completion | Generates bash completion script |
2123

2224
## Project config file

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
},
3333
"devDependencies": {
3434
"coveralls": "^2.13.1",
35+
"highland": "^2.11.1",
3536
"nock": "^9.0.14",
3637
"pkg": "^4.2.4",
3738
"proxyquire": "^1.8.0",

src/commands/deploy.js

Lines changed: 61 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,53 @@ const formatServices = require('../util/formatServices');
1616

1717
const ignores = ['.git', 'node_modules'];
1818

19-
const streamToResponse = ({tarStream, remoteUrl, options}) =>
19+
const streamToResponse = ({tarStream, remoteUrl, options, verbose}) =>
2020
new Promise((resolve, reject) => {
2121
// store error and result
2222
let error;
23-
let result = '';
23+
let result = {};
2424
// pipe stream to remote
2525
const stream = tarStream.pipe(got.stream.post(remoteUrl, options));
2626
// store output
27-
stream.on('data', str => (result += str.toString()));
28-
// listen for read stream end
29-
stream.on('end', () => {
27+
stream.on('data', str => {
28+
const s = str.toString();
3029
try {
31-
const res = JSON.parse(result);
32-
// if stream had error - reject
33-
if (error) {
34-
// add response to allow access to body
35-
error.response = res;
36-
reject(error);
37-
return;
30+
const data = JSON.parse(s);
31+
// always log info
32+
if (data.level === 'info') {
33+
verbose && console.log(chalk.blue('[info]'), data.message);
34+
// if data has deployments info - assign it as result
35+
if (data.deployments) {
36+
result = data;
37+
}
38+
}
39+
// log verbose if needed
40+
data.level === 'verbose' && verbose > 1 && console.log(chalk.grey('[verbose]'), data.message);
41+
// if error - store as error and log
42+
if (data.level === 'error') {
43+
verbose && console.log(chalk.red('[error]'), data.message);
44+
verbose > 1 && console.log(JSON.stringify(data, null, 2));
45+
error = new Error(data.message);
46+
error.response = data;
3847
}
39-
// otherwise resolve
40-
resolve(res);
41-
} catch (parseErr) {
42-
// catch parsing error
43-
// add response to allow access to body
44-
parseErr.response = {result: {error: result, log: ['No log available']}};
45-
reject(parseErr);
48+
} catch (e) {
49+
error = new Error('Error parsing output!');
50+
error.response = {
51+
error: s,
52+
};
53+
verbose && console.log(chalk.red('[error]'), 'Error parsing line:', s);
54+
}
55+
});
56+
// listen for read stream end
57+
stream.on('end', () => {
58+
// if stream had error - reject
59+
if (error) {
60+
reject(error);
61+
return;
4662
}
63+
// otherwise resolve
64+
resolve(result);
4765
});
48-
// listen for stream errors
4966
stream.on('error', e => (error = e));
5067
});
5168

@@ -67,6 +84,7 @@ exports.builder = {
6784
verbose: {
6885
alias: 'v',
6986
description: 'Verbose mode; will output more information',
87+
count: true,
7088
},
7189
};
7290
exports.handler = async (args = {}) => {
@@ -110,13 +128,16 @@ exports.handler = async (args = {}) => {
110128
}
111129

112130
// show loader
113-
const spinner = ora('Uploading project to server...').start();
131+
let spinner;
132+
if (!verbose) {
133+
spinner = ora('Deploying project to server...').start();
134+
}
114135

115136
// syntax-check config
116137
try {
117138
JSON.parse(fs.readFileSync(configPath));
118139
} catch (e) {
119-
spinner.fail('Your exoframe.json is not valid');
140+
spinner && spinner.fail('Your exoframe.json is not valid');
120141
console.log(chalk.red('Please, check your config and try again:'), e.toString());
121142
return;
122143
}
@@ -132,7 +153,7 @@ exports.handler = async (args = {}) => {
132153
let token = userConfig.token;
133154
if (deployToken) {
134155
token = deployToken;
135-
console.log('Deploying using given token..');
156+
console.log('\nDeploying using given token..');
136157
}
137158
const options = {
138159
headers: {
@@ -143,13 +164,18 @@ exports.handler = async (args = {}) => {
143164
// pipe stream to remote
144165
try {
145166
const res = await streamToResponse({tarStream, remoteUrl, options, verbose});
146-
// if in verbose mode - log response
147-
verbose && console.log('\nGot response from server:', res);
148167
// check deployments
149168
if (!res.deployments || !res.deployments.length) {
150-
throw new Error('Something went wrong!');
169+
const err = new Error('Something went wrong!');
170+
err.response = res;
171+
throw err;
151172
}
152-
spinner.succeed('Upload finsihed!');
173+
spinner && spinner.succeed('Upload finsihed!');
174+
175+
// log response in verbose-verbose mode
176+
verbose > 2 && console.log(chalk.gray('Server response:'), JSON.stringify(res, null, 2), '\n');
177+
178+
// log result
153179
console.log('Your project is now deployed as:\n');
154180
// create table
155181
const resultTable = new Table({
@@ -172,7 +198,7 @@ exports.handler = async (args = {}) => {
172198
opn(`http://${formattedServices[0].domain.split(',')[0].trim()}`);
173199
}
174200
} catch (e) {
175-
spinner.fail('Deployment failed!');
201+
spinner && spinner.fail('Deployment failed!');
176202
// if authorization is expired/broken/etc
177203
if (e.statusCode === 401) {
178204
logout(userConfig);
@@ -181,20 +207,18 @@ exports.handler = async (args = {}) => {
181207
}
182208

183209
const response = e.response || {};
184-
const reason = response.result ? response.result.error : e.toString();
210+
const reason = response.error || e.toString();
185211
console.log(chalk.red('Error deploying project:'), reason || 'Unknown reason');
186212
console.log('Build log:\n');
187-
response.result
188-
? (response.result.log || ['No log available'])
189-
.filter(l => l !== undefined)
190-
.map(l => l.trim())
191-
.filter(l => l && l.length > 0)
192-
.forEach(line => console.log(line))
193-
: console.log('No log available');
213+
(response.log || ['No log available'])
214+
.filter(l => l !== undefined)
215+
.map(l => l.trim())
216+
.filter(l => l && l.length > 0)
217+
.forEach(line => console.log(line));
194218

195219
// if in verbose mode - log original error and response
196220
verbose && console.log('');
197221
verbose && console.log('Original error:', e);
198-
verbose && console.log('Original response:', e.response);
222+
verbose > 1 && console.log('Original response:', e.response);
199223
}
200224
};

test/deploy.js

Lines changed: 33 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const path = require('path');
55
const nock = require('nock');
66
const sinon = require('sinon');
77
const proxyquire = require('proxyquire');
8+
const _ = require('highland');
9+
const {Readable} = require('stream');
810

911
// our packages
1012
const {userConfig, updateConfig} = require('../src/config');
@@ -15,6 +17,14 @@ const opnStub = sinon.spy();
1517
// require deploy with stub for opn
1618
const {handler: deploy} = proxyquire('../src/commands/deploy', {opn: opnStub});
1719

20+
// reply with stream helper
21+
const replyWithStream = dataArr => {
22+
const replyStream = _();
23+
dataArr.forEach(data => replyStream.write(JSON.stringify(data)));
24+
replyStream.end('');
25+
return new Readable().wrap(replyStream);
26+
};
27+
1828
module.exports = () => {
1929
const folder = 'test_html_project';
2030
const folderPath = path.join('test', 'fixtures', folder);
@@ -47,13 +57,13 @@ module.exports = () => {
4757
// handle correct request
4858
const deployServer = nock('http://localhost:8080')
4959
.post('/deploy')
50-
.reply((uri, requestBody, cb) => {
60+
.reply((uri, requestBody) => {
5161
const excgf = fs.readFileSync(path.join(testFolder, 'exoframe.json'));
5262
const index = fs.readFileSync(path.join(testFolder, 'index.html'));
5363
t.ok(requestBody.includes(excgf), 'Should send correct config');
5464
t.ok(requestBody.includes(index), 'Should send correct index file');
5565

56-
cb(null, [200, {status: 'success', deployments}]);
66+
return replyWithStream([{message: 'Deployment success!', deployments, level: 'info'}]);
5767
});
5868

5969
// execute login
@@ -87,9 +97,7 @@ module.exports = () => {
8797
// handle correct request
8898
const deployServer = nock('http://localhost:8080')
8999
.post('/deploy')
90-
.reply((uri, requestBody, cb) => {
91-
cb(null, [200, {status: 'success', deployments}]);
92-
});
100+
.reply(() => replyWithStream([{message: 'Deployment success!', deployments, level: 'info'}]));
93101

94102
// execute login
95103
deploy().then(() => {
@@ -124,9 +132,7 @@ module.exports = () => {
124132
// handle correct request
125133
const deployServer = nock('http://localhost:8080')
126134
.post('/deploy')
127-
.reply((uri, requestBody, cb) => {
128-
cb(null, [200, {status: 'success', deployments}]);
129-
});
135+
.reply(() => replyWithStream([{message: 'Deployment success!', deployments, level: 'info'}]));
130136

131137
// remove auth from config
132138
updateConfig({endpoint: 'http://localhost:8080'});
@@ -141,7 +147,7 @@ module.exports = () => {
141147
consoleSpy.args,
142148
[
143149
['Deploying current project to endpoint:', 'http://localhost:8080'],
144-
['Deploying using given token..'],
150+
['\nDeploying using given token..'],
145151
['Your project is now deployed as:\n'],
146152
[' ID URL Hostname \n test localhost test '],
147153
],
@@ -165,9 +171,7 @@ module.exports = () => {
165171
// handle correct request
166172
const updateServer = nock('http://localhost:8080')
167173
.post('/update')
168-
.reply((uri, requestBody, cb) => {
169-
cb(null, [200, {status: 'success', deployments}]);
170-
});
174+
.reply(() => replyWithStream([{message: 'Deployment success!', deployments, level: 'info'}]));
171175

172176
// execute login
173177
deploy({update: true}).then(() => {
@@ -200,9 +204,7 @@ module.exports = () => {
200204
// handle correct request
201205
const deployServer = nock('http://localhost:8080')
202206
.post('/deploy')
203-
.reply((uri, requestBody, cb) => {
204-
cb(null, [200, {status: 'success', deployments}]);
205-
});
207+
.reply(() => replyWithStream([{message: 'Deployment success!', deployments, level: 'info'}]));
206208

207209
// execute
208210
deploy({open: true}).then(() => {
@@ -237,19 +239,16 @@ module.exports = () => {
237239
// handle correct request
238240
const deployServer = nock('http://localhost:8080')
239241
.post('/deploy')
240-
.reply((uri, requestBody, cb) => {
241-
cb(null, [
242-
400,
242+
.reply(() =>
243+
replyWithStream([
243244
{
244-
status: 'error',
245-
result: {
246-
error: 'Build failed! See build log for details.',
247-
log: ['Error log', 'here'],
248-
image: 'test:latest',
249-
},
245+
message: 'Build failed! See build log for details.',
246+
error: 'Build failed! See build log for details.',
247+
log: ['Error log', 'here'],
248+
level: 'error',
250249
},
251-
]);
252-
});
250+
])
251+
);
253252

254253
// execute
255254
deploy().then(() => {
@@ -331,37 +330,24 @@ module.exports = () => {
331330
t.ok(deployServer.isDone());
332331
// first check console output
333332
t.deepEqual(
334-
consoleSpy.args,
333+
// check beginning of log
334+
consoleSpy.args.slice(0, consoleSpy.args.length - 1),
335335
[
336336
['Deploying current project to endpoint:', 'http://localhost:8080'],
337337
['\nIgnoring following paths:', ['.git', 'node_modules']],
338+
['[error]', 'Error parsing line:', 'Bad Gateway'],
338339
['Error deploying project:', 'Bad Gateway'],
339340
['Build log:\n'],
340341
['No log available'],
341342
[''],
342-
[
343-
'Original error:',
344-
{
345-
response: {
346-
result: {
347-
error: 'Bad Gateway',
348-
log: ['No log available'],
349-
},
350-
},
351-
},
352-
],
353-
[
354-
'Original response:',
355-
{
356-
result: {
357-
error: 'Bad Gateway',
358-
log: ['No log available'],
359-
},
360-
},
361-
],
362343
],
363344
'Correct log output'
364345
);
346+
// check error correctness
347+
const err = consoleSpy.args[consoleSpy.args.length - 1][1];
348+
t.equal(err.message, 'Error parsing output!', 'Correct error text');
349+
t.ok(err.response, 'Has response');
350+
t.equal(err.response.error, 'Bad Gateway', 'Correct error response');
365351
// restore console
366352
console.log.restore();
367353
// tear down nock

0 commit comments

Comments
 (0)