cloudron 5.11.3 → 5.11.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cloudron +1 -21
- package/bin/cloudron-build +56 -0
- package/package.json +1 -2
- package/src/build-actions.js +130 -111
package/bin/cloudron
CHANGED
|
@@ -22,12 +22,6 @@ if (!semver.satisfies(process.version, require('../package.json').engines.node))
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const program = new Command();
|
|
25
|
-
|
|
26
|
-
function collectArgs(value, collected) {
|
|
27
|
-
collected.push(value);
|
|
28
|
-
return collected;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
25
|
program.version(version);
|
|
32
26
|
|
|
33
27
|
// global options
|
|
@@ -39,6 +33,7 @@ program.option('--server <server>', 'Cloudron domain')
|
|
|
39
33
|
// these are separate binaries since global options are not applicable
|
|
40
34
|
program.command('appstore', 'Cloudron appstore commands');
|
|
41
35
|
program.command('repo', 'Cloudron repo commands');
|
|
36
|
+
program.command('build', 'Cloudron build commands');
|
|
42
37
|
|
|
43
38
|
const backupCommand = program.command('backup')
|
|
44
39
|
.description('App backup commands');
|
|
@@ -83,21 +78,6 @@ program.command('completion')
|
|
|
83
78
|
.description('Shows completion for your shell')
|
|
84
79
|
.action(completion);
|
|
85
80
|
|
|
86
|
-
// should probably move to separate binary since globals don't apply
|
|
87
|
-
program.command('build')
|
|
88
|
-
.description('Build an app')
|
|
89
|
-
.option('--build-arg <namevalue>', 'Build arg passed to docker. Can be used multiple times', collectArgs, [])
|
|
90
|
-
.option('--build-service-token <token>', 'Build service token')
|
|
91
|
-
.option('-f, --file <dockerfile>', 'Name of the Dockerfile')
|
|
92
|
-
.option('--set-repository [repository url]', 'Change the repository')
|
|
93
|
-
.option('--set-build-service [buildservice url]', 'Set build service app URL')
|
|
94
|
-
.option('--local', 'Build docker images locally')
|
|
95
|
-
.option('--no-cache', 'Do not use cache')
|
|
96
|
-
.option('--no-push', 'Do not push built image to registry')
|
|
97
|
-
.option('--raw', 'Raw output build log')
|
|
98
|
-
.option('--tag <docker image tag>', 'Docker image tag. Note that this does not include the repository name')
|
|
99
|
-
.action(buildActions.build);
|
|
100
|
-
|
|
101
81
|
program.command('cancel')
|
|
102
82
|
.description('Cancels any active or pending app task')
|
|
103
83
|
.option('--app <id/location>', 'App id or location')
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const { program } = require('commander'),
|
|
6
|
+
buildActions = require('../src/build-actions.js');
|
|
7
|
+
|
|
8
|
+
program.version(require('../package.json').version);
|
|
9
|
+
|
|
10
|
+
function collectArgs(value, collected) {
|
|
11
|
+
collected.push(value);
|
|
12
|
+
return collected;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// global options
|
|
16
|
+
program.option('--server <server>', 'Cloudron domain')
|
|
17
|
+
.option('--build-service-token <token>', 'Build service token')
|
|
18
|
+
.option('--build-service, --set-build-service [buildservice url]', 'Set build service app URL. This build service is automatically used for future calls from this project');
|
|
19
|
+
|
|
20
|
+
program.command('build', { isDefault: true })
|
|
21
|
+
.description('Build an app. This is the default subcommand')
|
|
22
|
+
.option('--build-arg <namevalue>', 'Build arg passed to docker. Can be used multiple times', collectArgs, [])
|
|
23
|
+
.option('-f, --file <dockerfile>', 'Name of the Dockerfile')
|
|
24
|
+
.option('--set-repository [repository url]', 'Change the repository. This url is stored for future builds for this project. e.g registry/username/projectname')
|
|
25
|
+
.option('--local', 'Build docker images locally')
|
|
26
|
+
.option('--no-cache', 'Do not use cache')
|
|
27
|
+
.option('--no-push', 'Do not push built image to registry')
|
|
28
|
+
.option('--raw', 'Raw output build log')
|
|
29
|
+
.option('--tag <docker image tag>', 'Docker image tag. Note that this does not include the repository name')
|
|
30
|
+
.action(buildActions.build);
|
|
31
|
+
|
|
32
|
+
program.command('login')
|
|
33
|
+
.description('Login to the build service')
|
|
34
|
+
.option('-u, --username <username>', 'Username')
|
|
35
|
+
.option('-p, --password <password>', 'Password (unsafe)')
|
|
36
|
+
.action(buildActions.login);
|
|
37
|
+
|
|
38
|
+
program.command('logs')
|
|
39
|
+
.description('Build logs. This works only when using the Build Service')
|
|
40
|
+
.option('--id <buildid>', 'Build ID')
|
|
41
|
+
.option('--raw', 'Raw output build log')
|
|
42
|
+
.action(buildActions.logs);
|
|
43
|
+
|
|
44
|
+
program.command('push')
|
|
45
|
+
.description('Push the build image')
|
|
46
|
+
.option('--id <buildid>', 'Build ID')
|
|
47
|
+
.option('--repository [repository url]', 'Set repository to push to. e.g registry/username/projectname')
|
|
48
|
+
.option('--tag <docker image tag>', 'Docker image tag. Note that this does not include the repository name')
|
|
49
|
+
.action(buildActions.push);
|
|
50
|
+
|
|
51
|
+
program.command('status')
|
|
52
|
+
.description('Build status. This works only when using the Build Service')
|
|
53
|
+
.option('--id <buildid>', 'Build ID')
|
|
54
|
+
.action(buildActions.status);
|
|
55
|
+
|
|
56
|
+
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudron",
|
|
3
|
-
"version": "5.11.
|
|
3
|
+
"version": "5.11.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Cloudron Commandline Tool",
|
|
6
6
|
"main": "main.js",
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
"ejs": "^3.1.10",
|
|
26
26
|
"eventsource": "^2.0.2",
|
|
27
27
|
"micromatch": "^4.0.7",
|
|
28
|
-
"once": "^1.4.0",
|
|
29
28
|
"open": "^8.4.0",
|
|
30
29
|
"progress": "^2.0.3",
|
|
31
30
|
"progress-stream": "^2.0.0",
|
package/src/build-actions.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
exports = module.exports = {
|
|
4
|
-
|
|
4
|
+
login,
|
|
5
|
+
build,
|
|
6
|
+
logs,
|
|
7
|
+
status,
|
|
8
|
+
push
|
|
5
9
|
};
|
|
6
10
|
|
|
7
11
|
const assert = require('assert'),
|
|
@@ -11,7 +15,6 @@ const assert = require('assert'),
|
|
|
11
15
|
execSync = require('child_process').execSync,
|
|
12
16
|
exit = require('./helper.js').exit,
|
|
13
17
|
fs = require('fs'),
|
|
14
|
-
once = require('once'),
|
|
15
18
|
helper = require('./helper.js'),
|
|
16
19
|
manifestFormat = require('cloudron-manifestformat'),
|
|
17
20
|
micromatch = require('micromatch'),
|
|
@@ -20,78 +23,55 @@ const assert = require('assert'),
|
|
|
20
23
|
os = require('os'),
|
|
21
24
|
path = require('path'),
|
|
22
25
|
safe = require('safetydance'),
|
|
26
|
+
stream = require('stream/promises'),
|
|
23
27
|
tar = require('tar-fs'),
|
|
24
|
-
url = require('url')
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
requestFactory().end(function (error, result) {
|
|
32
|
-
if (error && !error.response) return callback(error);
|
|
33
|
-
if (result.statusCode === 400) return authenticateBuildService({ error: true }, superagentEndBuildService.bind(null, requestFactory, callback));
|
|
34
|
-
if (result.statusCode === 401) return authenticateBuildService({ error: true }, superagentEndBuildService.bind(null, requestFactory, callback));
|
|
35
|
-
if (result.statusCode === 403) return callback(new Error(result.type === 'application/javascript' ? JSON.stringify(result.body) : result.text));
|
|
36
|
-
callback(error, result);
|
|
37
|
-
});
|
|
28
|
+
url = require('url');
|
|
29
|
+
|
|
30
|
+
function requestError(response) {
|
|
31
|
+
if (response.statusCode === 401 || response.statusCode === 403) return 'Invalid token. Use cloudron build login again.';
|
|
32
|
+
|
|
33
|
+
return `${response.statusCode} message: ${response.body?.message || null}`;
|
|
38
34
|
}
|
|
39
35
|
|
|
40
|
-
function
|
|
36
|
+
async function login(options) {
|
|
41
37
|
assert.strictEqual(typeof options, 'object');
|
|
42
|
-
assert.strictEqual(typeof callback, 'function');
|
|
43
38
|
|
|
44
|
-
|
|
39
|
+
const buildServiceConfig = config.getBuildServiceConfig();
|
|
45
40
|
|
|
46
41
|
if (!options.hideBanner) console.log('Build Service login' + ` (${buildServiceConfig.url}):`);
|
|
47
42
|
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
const username = options.username || readlineSync.question('Username: ', {});
|
|
44
|
+
const password = options.password || readlineSync.question('Password: ', { noEchoBack: true });
|
|
50
45
|
|
|
51
46
|
// reset token
|
|
52
47
|
buildServiceConfig.token = null;
|
|
53
48
|
config.setBuildServiceConfig(buildServiceConfig);
|
|
54
49
|
|
|
55
|
-
superagent.post(`${buildServiceConfig.url}/api/v1/login`).send({ username: username, password: password }).
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (result.statusCode !== 200 && result.statusCode !== 201) {
|
|
59
|
-
console.log('Login failed.');
|
|
60
|
-
|
|
61
|
-
options.hideBanner = true;
|
|
62
|
-
options.username = '';
|
|
63
|
-
options.password = '';
|
|
50
|
+
const response = await superagent.post(`${buildServiceConfig.url}/api/v1/login`).send({ username: username, password: password }).ok(() => true);
|
|
51
|
+
if (response.statusCode === 401 || response.statusCode === 403) return exit(`Authentication error: ${requestError(response)}}`);
|
|
52
|
+
if (response.statusCode !== 200 && response.statusCode !== 201) return exit(`Unexpected response: ${requestError(response)}`);
|
|
64
53
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
buildServiceConfig.token = result.body.accessToken;
|
|
69
|
-
config.setBuildServiceConfig(buildServiceConfig);
|
|
70
|
-
|
|
71
|
-
console.log('Login successful.');
|
|
54
|
+
buildServiceConfig.token = response.body.accessToken;
|
|
55
|
+
config.setBuildServiceConfig(buildServiceConfig);
|
|
72
56
|
|
|
73
|
-
|
|
74
|
-
});
|
|
57
|
+
console.log('Login successful.');
|
|
75
58
|
}
|
|
76
59
|
|
|
77
|
-
function followBuildLog(buildId, raw
|
|
60
|
+
async function followBuildLog(buildId, raw) {
|
|
78
61
|
assert.strictEqual(typeof buildId, 'string');
|
|
79
62
|
assert.strictEqual(typeof raw, 'boolean');
|
|
80
63
|
|
|
81
|
-
// ensure callback is only ever called once
|
|
82
|
-
callback = once(callback);
|
|
83
|
-
|
|
84
64
|
// EventSource always requires http
|
|
85
65
|
let tmp = url.parse(config.getBuildServiceConfig().url);
|
|
86
66
|
if (tmp.protocol !== 'https:' && tmp.protocol !== 'http:') tmp = url.parse('http://' + config.getBuildServiceConfig().url);
|
|
87
67
|
|
|
88
|
-
|
|
89
|
-
|
|
68
|
+
const es = new EventSource(`${tmp.href}api/v1/builds/${buildId}/logstream?accessToken=${config.getBuildServiceConfig().token}`);
|
|
69
|
+
let prevId = null, prevWasStatus = false;
|
|
90
70
|
|
|
91
71
|
es.on('message', function (e) {
|
|
92
72
|
if (raw) return console.dir(e);
|
|
93
73
|
|
|
94
|
-
|
|
74
|
+
const data = safe.JSON.parse(e.data);
|
|
95
75
|
if (!data) return; // this is a bug in docker or our build server
|
|
96
76
|
|
|
97
77
|
if (data.status) { // image push log
|
|
@@ -131,25 +111,39 @@ function followBuildLog(buildId, raw, callback) {
|
|
|
131
111
|
console.error(data.error);
|
|
132
112
|
}
|
|
133
113
|
});
|
|
134
|
-
es.on('error', function (error) {
|
|
135
|
-
if (raw) console.dir(error);
|
|
136
114
|
|
|
137
|
-
|
|
138
|
-
|
|
115
|
+
let didConnect = false;
|
|
116
|
+
es.once('open', () => didConnect = true);
|
|
117
|
+
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
es.once('error', function (error) { // server close or network error or some interruption
|
|
120
|
+
if (raw) console.dir(error);
|
|
139
121
|
|
|
140
|
-
|
|
122
|
+
es.close();
|
|
123
|
+
if (didConnect) resolve(); else reject(new Error('Failed to connect'));
|
|
124
|
+
});
|
|
141
125
|
});
|
|
142
126
|
}
|
|
143
127
|
|
|
128
|
+
async function getStatus(buildId) {
|
|
129
|
+
const buildServiceConfig = config.getBuildServiceConfig();
|
|
130
|
+
|
|
131
|
+
const response2 = await superagent.get(`${buildServiceConfig.url}/api/v1/builds/${buildId}`)
|
|
132
|
+
.query({ accessToken: buildServiceConfig.token })
|
|
133
|
+
.ok(() => true);
|
|
134
|
+
if (response2.statusCode !== 200) throw new Error(`Failed to get status: ${requestError(response2)}`);
|
|
135
|
+
return response2.body.status;
|
|
136
|
+
}
|
|
137
|
+
|
|
144
138
|
function dockerignoreMatcher(dockerignorePath) {
|
|
145
|
-
|
|
139
|
+
let patterns = [];
|
|
146
140
|
|
|
147
141
|
if (fs.existsSync(dockerignorePath)) {
|
|
148
142
|
patterns = fs.readFileSync(dockerignorePath, 'utf8').split('\n');
|
|
149
143
|
|
|
150
144
|
patterns = patterns.filter(function (line) { return line[0] !== '#'; });
|
|
151
145
|
patterns = patterns.map(function (line) {
|
|
152
|
-
|
|
146
|
+
let l = line.trim();
|
|
153
147
|
|
|
154
148
|
while (l[0] === '/') l = l.slice(1);
|
|
155
149
|
while (l[l.length-1] === '/') l = l.slice(0, -1);
|
|
@@ -164,7 +158,7 @@ function dockerignoreMatcher(dockerignorePath) {
|
|
|
164
158
|
};
|
|
165
159
|
}
|
|
166
160
|
|
|
167
|
-
function buildLocal(manifest, sourceDir, appConfig, options) {
|
|
161
|
+
async function buildLocal(manifest, sourceDir, appConfig, options) {
|
|
168
162
|
let tag;
|
|
169
163
|
if (options.tag) {
|
|
170
164
|
tag = options.tag;
|
|
@@ -180,7 +174,7 @@ function buildLocal(manifest, sourceDir, appConfig, options) {
|
|
|
180
174
|
console.log('Building locally as %s', dockerImage);
|
|
181
175
|
console.log();
|
|
182
176
|
|
|
183
|
-
|
|
177
|
+
const buildArgsCmdLine = options.buildArg.map(function (a) { return `--build-arg "${a}"`; }).join(' ');
|
|
184
178
|
|
|
185
179
|
let dockerfile = 'Dockerfile';
|
|
186
180
|
if (options.file) dockerfile = options.file;
|
|
@@ -196,9 +190,9 @@ function buildLocal(manifest, sourceDir, appConfig, options) {
|
|
|
196
190
|
if (safe.error) exit('Failed to push image (are you logged in? if not, use "docker login")');
|
|
197
191
|
}
|
|
198
192
|
|
|
199
|
-
|
|
193
|
+
const result = safe.child_process.execSync(`docker inspect --format="{{index .RepoDigests 0}}" ${dockerImage}`, { encoding: 'utf8' });
|
|
200
194
|
if (safe.error) exit('Failed to inspect image');
|
|
201
|
-
|
|
195
|
+
const match = /.*@sha256:(.*)/.exec(result.trim());
|
|
202
196
|
if (!match) exit('Failed to detect sha256');
|
|
203
197
|
|
|
204
198
|
appConfig.dockerImage = `${dockerImage}`;
|
|
@@ -206,7 +200,7 @@ function buildLocal(manifest, sourceDir, appConfig, options) {
|
|
|
206
200
|
config.setAppConfig(sourceDir, appConfig);
|
|
207
201
|
}
|
|
208
202
|
|
|
209
|
-
function buildRemote(manifest, sourceDir, appConfig, options) {
|
|
203
|
+
async function buildRemote(manifest, sourceDir, appConfig, options) {
|
|
210
204
|
console.log('Using build service', config.getBuildServiceConfig().url);
|
|
211
205
|
|
|
212
206
|
let tag;
|
|
@@ -223,21 +217,21 @@ function buildRemote(manifest, sourceDir, appConfig, options) {
|
|
|
223
217
|
|
|
224
218
|
console.log('Building %s', dockerImage);
|
|
225
219
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
220
|
+
const sourceArchiveFilePath = path.join(os.tmpdir(), path.basename(sourceDir) + '.tar.gz');
|
|
221
|
+
const dockerignoreFilePath = path.join(sourceDir, '.dockerignore');
|
|
222
|
+
const ignoreMatcher = dockerignoreMatcher(dockerignoreFilePath);
|
|
229
223
|
|
|
230
224
|
console.log('Uploading source tarball...');
|
|
231
225
|
|
|
232
|
-
|
|
226
|
+
const tarStream = tar.pack(sourceDir, {
|
|
233
227
|
ignore: function (name) {
|
|
234
228
|
return ignoreMatcher(name.slice(sourceDir.length + 1)); // make name as relative path
|
|
235
229
|
}
|
|
236
|
-
}).pipe(fs.createWriteStream(sourceArchiveFilePath));
|
|
237
|
-
|
|
238
|
-
stream.on('error', function (error) {
|
|
239
|
-
exit('Failed to create application source archive: ' + error);
|
|
240
230
|
});
|
|
231
|
+
const sourceArchiveStream = fs.createWriteStream(sourceArchiveFilePath);
|
|
232
|
+
|
|
233
|
+
const [tarError] = await safe(stream.pipeline(tarStream, sourceArchiveStream));
|
|
234
|
+
if (tarError) return exit(`Could not tar: ${tarError.message}`);
|
|
241
235
|
|
|
242
236
|
let dockerfile = 'Dockerfile';
|
|
243
237
|
if (options.file) dockerfile = options.file;
|
|
@@ -251,49 +245,38 @@ function buildRemote(manifest, sourceDir, appConfig, options) {
|
|
|
251
245
|
buildArgsObject[key] = value;
|
|
252
246
|
});
|
|
253
247
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
// appConfig.dockerImageSha256 = match[1]; // stash this separately for now
|
|
284
|
-
config.setAppConfig(sourceDir, appConfig);
|
|
285
|
-
|
|
286
|
-
console.log(dockerImage);
|
|
287
|
-
console.log('\nBuild successful');
|
|
288
|
-
|
|
289
|
-
exit();
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
});
|
|
248
|
+
const buildServiceConfig = config.getBuildServiceConfig();
|
|
249
|
+
const response = await superagent.post(`${buildServiceConfig.url}/api/v1/builds`)
|
|
250
|
+
.query({ accessToken: buildServiceConfig.token, noCache: !options.cache, dockerfile: dockerfile, noPush: !options.push })
|
|
251
|
+
.field('dockerImageRepo', appConfig.repository)
|
|
252
|
+
.field('dockerImageTag', tag)
|
|
253
|
+
.field('buildArgs', JSON.stringify(buildArgsObject))
|
|
254
|
+
.attach('sourceArchive', sourceArchiveFilePath)
|
|
255
|
+
.ok(() => true);
|
|
256
|
+
if (response.statusCode === 413) return exit('Failed to build app. The app source is too large.\nPlease adjust your .dockerignore file to only include neccessary files.');
|
|
257
|
+
if (response.statusCode !== 201) return exit(`Failed to upload app for building: ${requestError(response)}`);
|
|
258
|
+
|
|
259
|
+
const buildId = response.body.id;
|
|
260
|
+
console.log(`BuildId: ${buildId}`);
|
|
261
|
+
|
|
262
|
+
const [logsError] = await safe(followBuildLog(buildId, !!options.raw));
|
|
263
|
+
if (logsError) console.log(`Failed to get logs: ${logsError.message}`);
|
|
264
|
+
|
|
265
|
+
const [statusError, status] = await safe(getStatus(buildId));
|
|
266
|
+
if (statusError) return exit(`Failed to get status: ${statusError.message}`);
|
|
267
|
+
if (status !== 'success') return exit('Failed to build app. See log output above.');
|
|
268
|
+
|
|
269
|
+
appConfig.dockerImage = dockerImage;
|
|
270
|
+
// appConfig.dockerImageSha256 = match[1]; // stash this separately for now
|
|
271
|
+
config.setAppConfig(sourceDir, appConfig);
|
|
272
|
+
|
|
273
|
+
console.log(`Docker image: ${dockerImage}`);
|
|
274
|
+
console.log('\nBuild successful');
|
|
275
|
+
|
|
276
|
+
exit();
|
|
294
277
|
}
|
|
295
278
|
|
|
296
|
-
function build(options) {
|
|
279
|
+
async function build(options) {
|
|
297
280
|
// try to find the manifest of this project
|
|
298
281
|
const manifestFilePath = helper.locateManifest();
|
|
299
282
|
if (!manifestFilePath) return exit('No CloudronManifest.json found');
|
|
@@ -301,12 +284,12 @@ function build(options) {
|
|
|
301
284
|
const result = manifestFormat.parseFile(manifestFilePath);
|
|
302
285
|
if (result.error) return exit('Error in CloudronManifest.json: ' + result.error.message);
|
|
303
286
|
|
|
304
|
-
|
|
287
|
+
const manifest = result.manifest;
|
|
305
288
|
const sourceDir = path.dirname(manifestFilePath);
|
|
306
289
|
|
|
307
290
|
const appConfig = config.getAppConfig(sourceDir);
|
|
308
291
|
|
|
309
|
-
|
|
292
|
+
const buildService = config.getBuildServiceConfig();
|
|
310
293
|
if (!buildService.type) buildService.type = 'local'; // default
|
|
311
294
|
|
|
312
295
|
if (options.local) {
|
|
@@ -346,10 +329,46 @@ function build(options) {
|
|
|
346
329
|
}
|
|
347
330
|
|
|
348
331
|
if (buildService.type === 'local') {
|
|
349
|
-
buildLocal(manifest, sourceDir, appConfig, options);
|
|
332
|
+
await buildLocal(manifest, sourceDir, appConfig, options);
|
|
350
333
|
} else if (buildService.type === 'remote' && buildService.url) {
|
|
351
|
-
buildRemote(manifest, sourceDir, appConfig, options);
|
|
334
|
+
await buildRemote(manifest, sourceDir, appConfig, options);
|
|
352
335
|
} else {
|
|
353
336
|
exit('Unknown build service type or missing build service url. Rerun with --reset-build-service');
|
|
354
337
|
}
|
|
355
338
|
}
|
|
339
|
+
|
|
340
|
+
async function logs(options) {
|
|
341
|
+
if (!options.id) return exit('buildId is required');
|
|
342
|
+
|
|
343
|
+
const [logsError] = await safe(followBuildLog(options.id, !!options.raw));
|
|
344
|
+
if (logsError) console.log(`Failed to get logs: ${logsError.message}`);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function status(options) {
|
|
348
|
+
if (!options.id) return exit('buildId is required');
|
|
349
|
+
|
|
350
|
+
const [statusError, status] = await safe(getStatus(options.id));
|
|
351
|
+
if (statusError) return exit(`Failed to get status: ${statusError.message}`);
|
|
352
|
+
console.log(status);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async function push(options) {
|
|
356
|
+
if (!options.id) return exit('buildId is required');
|
|
357
|
+
if (!options.repository) return exit('repository is required');
|
|
358
|
+
if (!options.tag) return exit('tag is required');
|
|
359
|
+
|
|
360
|
+
const buildServiceConfig = config.getBuildServiceConfig();
|
|
361
|
+
|
|
362
|
+
const response = await superagent.post(`${buildServiceConfig.url}/api/v1/builds/${options.id}/push`)
|
|
363
|
+
.query({ accessToken: buildServiceConfig.token })
|
|
364
|
+
.send({ dockerImageRepo: options.repository, dockerImageTag: options.tag })
|
|
365
|
+
.ok(() => true);
|
|
366
|
+
if (response.statusCode !== 201) return exit(`Failed to push: ${requestError(response)}`);
|
|
367
|
+
|
|
368
|
+
const [logsError] = await safe(followBuildLog(options.id, !!options.raw));
|
|
369
|
+
if (logsError) console.log(`Failed to get logs: ${logsError.message}`);
|
|
370
|
+
|
|
371
|
+
const [statusError, status] = await safe(getStatus(options.id));
|
|
372
|
+
if (statusError) return exit(`Failed to get status: ${statusError.message}`);
|
|
373
|
+
if (status !== 'success') return exit('Failed to push app. See log output above.');
|
|
374
|
+
}
|