@capawesome/cli 4.6.0-dev.80c962a.1774597304 → 4.6.0-dev.8ae803e.1775035527
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/dist/commands/apps/builds/create.js +123 -128
- package/dist/commands/apps/bundles/create.js +4 -2
- package/dist/commands/apps/bundles/delete.js +2 -3
- package/dist/commands/apps/bundles/update.js +2 -3
- package/dist/commands/apps/certificates/delete.js +28 -5
- package/dist/commands/apps/certificates/get.js +28 -5
- package/dist/commands/apps/deployments/create.js +5 -77
- package/dist/commands/apps/liveupdates/bundle.js +7 -2
- package/dist/commands/apps/liveupdates/create.js +285 -0
- package/dist/commands/apps/liveupdates/create.test.js +262 -0
- package/dist/commands/apps/liveupdates/generate-manifest.js +12 -1
- package/dist/commands/apps/liveupdates/generate-manifest.test.js +21 -1
- package/dist/commands/apps/liveupdates/register.js +10 -15
- package/dist/commands/apps/liveupdates/upload.js +18 -16
- package/dist/commands/manifests/generate.js +1 -1
- package/dist/index.js +1 -0
- package/dist/services/app-build-sources.js +120 -0
- package/dist/services/authorization-service.js +5 -1
- package/dist/services/jobs.js +13 -0
- package/dist/types/app-build-source.js +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/custom-properties.js +22 -0
- package/dist/utils/file.js +8 -1
- package/dist/utils/job.js +77 -0
- package/dist/utils/zip.js +19 -2
- package/package.json +2 -1
|
@@ -2,6 +2,7 @@ import { DEFAULT_CONSOLE_BASE_URL } from '../../../config/consts.js';
|
|
|
2
2
|
import appBundlesService from '../../../services/app-bundles.js';
|
|
3
3
|
import appsService from '../../../services/apps.js';
|
|
4
4
|
import { withAuth } from '../../../utils/auth.js';
|
|
5
|
+
import { parseCustomProperties } from '../../../utils/custom-properties.js';
|
|
5
6
|
import { createBufferFromPath, createBufferFromString, isPrivateKeyContent } from '../../../utils/buffer.js';
|
|
6
7
|
import { isInteractive } from '../../../utils/environment.js';
|
|
7
8
|
import { fileExistsAtPath } from '../../../utils/file.js';
|
|
@@ -14,7 +15,7 @@ import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
|
14
15
|
import consola from 'consola';
|
|
15
16
|
import { z } from 'zod';
|
|
16
17
|
export default defineCommand({
|
|
17
|
-
description: 'Register a self-hosted bundle URL.',
|
|
18
|
+
description: 'Register a self-hosted bundle URL for serving artifacts from your own infrastructure.',
|
|
18
19
|
options: defineOptions(z.object({
|
|
19
20
|
androidMax: z.coerce
|
|
20
21
|
.string()
|
|
@@ -52,6 +53,7 @@ export default defineCommand({
|
|
|
52
53
|
.describe('The commit sha related to the bundle. Deprecated, use `--git-ref` instead.'),
|
|
53
54
|
customProperty: z
|
|
54
55
|
.array(z.string().min(1).max(100))
|
|
56
|
+
.max(10)
|
|
55
57
|
.optional()
|
|
56
58
|
.describe('A custom property to assign to the bundle. Must be in the format `key=value`. Can be specified multiple times.'),
|
|
57
59
|
expiresInDays: z.coerce
|
|
@@ -192,7 +194,13 @@ export default defineCommand({
|
|
|
192
194
|
process.exit(1);
|
|
193
195
|
}
|
|
194
196
|
// Sign the bundle
|
|
195
|
-
|
|
197
|
+
try {
|
|
198
|
+
signature = await createSignature(privateKeyBuffer, fileBuffer);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
consola.error('Failed to parse the private key. Make sure the private key is a valid PEM-formatted key and is not encrypted.');
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
196
204
|
}
|
|
197
205
|
}
|
|
198
206
|
// Get app details for confirmation
|
|
@@ -237,16 +245,3 @@ export default defineCommand({
|
|
|
237
245
|
consola.success('Live Update successfully registered.');
|
|
238
246
|
}),
|
|
239
247
|
});
|
|
240
|
-
const parseCustomProperties = (customProperty) => {
|
|
241
|
-
let customProperties;
|
|
242
|
-
if (customProperty) {
|
|
243
|
-
customProperties = {};
|
|
244
|
-
for (const property of customProperty) {
|
|
245
|
-
const [key, value] = property.split('=');
|
|
246
|
-
if (key && value) {
|
|
247
|
-
customProperties[key] = value;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
return customProperties;
|
|
252
|
-
};
|
|
@@ -3,9 +3,10 @@ import appBundleFilesService from '../../../services/app-bundle-files.js';
|
|
|
3
3
|
import appBundlesService from '../../../services/app-bundles.js';
|
|
4
4
|
import appsService from '../../../services/apps.js';
|
|
5
5
|
import { withAuth } from '../../../utils/auth.js';
|
|
6
|
+
import { parseCustomProperties } from '../../../utils/custom-properties.js';
|
|
6
7
|
import { createBufferFromPath, createBufferFromReadStream, createBufferFromString, isPrivateKeyContent, } from '../../../utils/buffer.js';
|
|
7
8
|
import { isInteractive } from '../../../utils/environment.js';
|
|
8
|
-
import { directoryContainsSourceMaps, fileExistsAtPath, getFilesInDirectoryAndSubdirectories, isDirectory, } from '../../../utils/file.js';
|
|
9
|
+
import { directoryContainsSourceMaps, directoryContainsSymlinks, fileExistsAtPath, getFilesInDirectoryAndSubdirectories, isDirectory, } from '../../../utils/file.js';
|
|
9
10
|
import { createHash } from '../../../utils/hash.js';
|
|
10
11
|
import { generateManifestJson } from '../../../utils/manifest.js';
|
|
11
12
|
import { formatPrivateKey } from '../../../utils/private-key.js';
|
|
@@ -18,7 +19,7 @@ import { createReadStream } from 'fs';
|
|
|
18
19
|
import pathModule from 'path';
|
|
19
20
|
import { z } from 'zod';
|
|
20
21
|
export default defineCommand({
|
|
21
|
-
description: 'Upload a bundle to Capawesome Cloud.',
|
|
22
|
+
description: 'Upload a locally built bundle to Capawesome Cloud.',
|
|
22
23
|
options: defineOptions(z.object({
|
|
23
24
|
androidMax: z.coerce
|
|
24
25
|
.string()
|
|
@@ -63,6 +64,7 @@ export default defineCommand({
|
|
|
63
64
|
.describe('The commit sha related to the bundle. Deprecated, use `--git-ref` instead.'),
|
|
64
65
|
customProperty: z
|
|
65
66
|
.array(z.string().min(1).max(100))
|
|
67
|
+
.max(10)
|
|
66
68
|
.optional()
|
|
67
69
|
.describe('A custom property to assign to the bundle. Must be in the format `key=value`. Can be specified multiple times.'),
|
|
68
70
|
expiresInDays: z.coerce
|
|
@@ -156,6 +158,13 @@ export default defineCommand({
|
|
|
156
158
|
consola.error('The path must be either a folder or a zip file.');
|
|
157
159
|
process.exit(1);
|
|
158
160
|
}
|
|
161
|
+
// Check for symlinks
|
|
162
|
+
if (pathIsDirectory) {
|
|
163
|
+
const containsSymlinks = await directoryContainsSymlinks(path);
|
|
164
|
+
if (containsSymlinks) {
|
|
165
|
+
consola.warn('Symbolic links were detected in the specified path. Symbolic links are skipped during upload.');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
159
168
|
// Check for source maps
|
|
160
169
|
if (pathIsDirectory) {
|
|
161
170
|
const containsSourceMaps = await directoryContainsSourceMaps(path);
|
|
@@ -290,7 +299,13 @@ const uploadFile = async (options) => {
|
|
|
290
299
|
// Sign the bundle
|
|
291
300
|
let signature;
|
|
292
301
|
if (privateKeyBuffer) {
|
|
293
|
-
|
|
302
|
+
try {
|
|
303
|
+
signature = await createSignature(privateKeyBuffer, buffer);
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
consola.error('Failed to parse the private key. Make sure the private key is a valid PEM-formatted key and is not encrypted.');
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
294
309
|
}
|
|
295
310
|
// Create the multipart upload
|
|
296
311
|
return await appBundleFilesService.create({
|
|
@@ -377,16 +392,3 @@ const uploadZip = async (options) => {
|
|
|
377
392
|
appBundleFileId: result.id,
|
|
378
393
|
};
|
|
379
394
|
};
|
|
380
|
-
const parseCustomProperties = (customProperty) => {
|
|
381
|
-
let customProperties;
|
|
382
|
-
if (customProperty) {
|
|
383
|
-
customProperties = {};
|
|
384
|
-
for (const property of customProperty) {
|
|
385
|
-
const [key, value] = property.split('=');
|
|
386
|
-
if (key && value) {
|
|
387
|
-
customProperties[key] = value;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
return customProperties;
|
|
392
|
-
};
|
|
@@ -2,7 +2,7 @@ import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
|
2
2
|
import consola from 'consola';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
export default defineCommand({
|
|
5
|
-
description: 'Generate a manifest file.',
|
|
5
|
+
description: 'Generate a manifest file. Deprecated, use `apps:liveupdates:generate-manifest` instead.',
|
|
6
6
|
options: defineOptions(z.object({
|
|
7
7
|
path: z.string().optional().describe('Path to the web assets folder (e.g. `www` or `dist`).'),
|
|
8
8
|
})),
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,7 @@ const config = defineConfig({
|
|
|
60
60
|
'apps:environments:set': await import('./commands/apps/environments/set.js').then((mod) => mod.default),
|
|
61
61
|
'apps:environments:unset': await import('./commands/apps/environments/unset.js').then((mod) => mod.default),
|
|
62
62
|
'apps:liveupdates:bundle': await import('./commands/apps/liveupdates/bundle.js').then((mod) => mod.default),
|
|
63
|
+
'apps:liveupdates:create': await import('./commands/apps/liveupdates/create.js').then((mod) => mod.default),
|
|
63
64
|
'apps:liveupdates:generatesigningkey': await import('./commands/apps/liveupdates/generate-signing-key.js').then((mod) => mod.default),
|
|
64
65
|
'apps:liveupdates:rollback': await import('./commands/apps/liveupdates/rollback.js').then((mod) => mod.default),
|
|
65
66
|
'apps:liveupdates:rollout': await import('./commands/apps/liveupdates/rollout.js').then((mod) => mod.default),
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { MAX_CONCURRENT_PART_UPLOADS } from '../config/index.js';
|
|
2
|
+
import authorizationService from '../services/authorization-service.js';
|
|
3
|
+
import httpClient from '../utils/http-client.js';
|
|
4
|
+
import FormData from 'form-data';
|
|
5
|
+
class AppBuildSourcesServiceImpl {
|
|
6
|
+
httpClient;
|
|
7
|
+
constructor(httpClient) {
|
|
8
|
+
this.httpClient = httpClient;
|
|
9
|
+
}
|
|
10
|
+
async createFromFile(dto, onProgress) {
|
|
11
|
+
const response = await this.httpClient.post(`/v1/apps/${dto.appId}/build-sources`, { fileSizeInBytes: dto.fileSizeInBytes }, {
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
await this.upload({
|
|
17
|
+
appBuildSourceId: response.data.id,
|
|
18
|
+
appId: dto.appId,
|
|
19
|
+
buffer: dto.buffer,
|
|
20
|
+
name: dto.name,
|
|
21
|
+
}, onProgress);
|
|
22
|
+
return response.data;
|
|
23
|
+
}
|
|
24
|
+
async createFromUrl(dto) {
|
|
25
|
+
const response = await this.httpClient.post(`/v1/apps/${dto.appId}/build-sources`, { fileUrl: dto.fileUrl }, {
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
return response.data;
|
|
31
|
+
}
|
|
32
|
+
async completeUpload(dto) {
|
|
33
|
+
return this.httpClient
|
|
34
|
+
.post(`/v1/apps/${dto.appId}/build-sources/${dto.appBuildSourceId}/upload?action=mpu-complete&uploadId=${dto.uploadId}`, {
|
|
35
|
+
parts: dto.parts,
|
|
36
|
+
}, {
|
|
37
|
+
headers: {
|
|
38
|
+
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
.then((response) => response.data);
|
|
42
|
+
}
|
|
43
|
+
async createUpload(dto) {
|
|
44
|
+
const response = await this.httpClient.post(`/v1/apps/${dto.appId}/build-sources/${dto.appBuildSourceId}/upload?action=mpu-create`, {}, {
|
|
45
|
+
headers: {
|
|
46
|
+
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
return response.data;
|
|
50
|
+
}
|
|
51
|
+
async createUploadPart(dto) {
|
|
52
|
+
const formData = new FormData();
|
|
53
|
+
formData.append('blob', dto.buffer, { filename: dto.name });
|
|
54
|
+
formData.append('partNumber', dto.partNumber.toString());
|
|
55
|
+
return this.httpClient
|
|
56
|
+
.put(`/v1/apps/${dto.appId}/build-sources/${dto.appBuildSourceId}/upload?action=mpu-uploadpart&uploadId=${dto.uploadId}`, formData, {
|
|
57
|
+
headers: {
|
|
58
|
+
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
|
|
59
|
+
...formData.getHeaders(),
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
.then((response) => response.data);
|
|
63
|
+
}
|
|
64
|
+
async createUploadParts(dto, onProgress) {
|
|
65
|
+
const uploadedParts = [];
|
|
66
|
+
const partSize = 10 * 1024 * 1024; // 10 MB
|
|
67
|
+
const totalParts = Math.ceil(dto.buffer.byteLength / partSize);
|
|
68
|
+
let partNumber = 0;
|
|
69
|
+
const uploadNextPart = async () => {
|
|
70
|
+
if (partNumber >= totalParts) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
partNumber++;
|
|
74
|
+
onProgress?.(partNumber, totalParts);
|
|
75
|
+
const start = (partNumber - 1) * partSize;
|
|
76
|
+
const end = Math.min(start + partSize, dto.buffer.byteLength);
|
|
77
|
+
const partBuffer = dto.buffer.subarray(start, end);
|
|
78
|
+
const uploadedPart = await this.createUploadPart({
|
|
79
|
+
appBuildSourceId: dto.appBuildSourceId,
|
|
80
|
+
appId: dto.appId,
|
|
81
|
+
buffer: partBuffer,
|
|
82
|
+
name: dto.name,
|
|
83
|
+
partNumber,
|
|
84
|
+
uploadId: dto.uploadId,
|
|
85
|
+
});
|
|
86
|
+
uploadedParts.push(uploadedPart);
|
|
87
|
+
await uploadNextPart();
|
|
88
|
+
};
|
|
89
|
+
const uploadPartPromises = Array.from({ length: MAX_CONCURRENT_PART_UPLOADS });
|
|
90
|
+
for (let i = 0; i < MAX_CONCURRENT_PART_UPLOADS; i++) {
|
|
91
|
+
uploadPartPromises[i] = uploadNextPart();
|
|
92
|
+
}
|
|
93
|
+
await Promise.all(uploadPartPromises);
|
|
94
|
+
return uploadedParts.sort((a, b) => a.partNumber - b.partNumber);
|
|
95
|
+
}
|
|
96
|
+
async upload(dto, onProgress) {
|
|
97
|
+
// 1. Create a multipart upload
|
|
98
|
+
const { uploadId } = await this.createUpload({
|
|
99
|
+
appBuildSourceId: dto.appBuildSourceId,
|
|
100
|
+
appId: dto.appId,
|
|
101
|
+
});
|
|
102
|
+
// 2. Upload the file in parts
|
|
103
|
+
const parts = await this.createUploadParts({
|
|
104
|
+
appBuildSourceId: dto.appBuildSourceId,
|
|
105
|
+
appId: dto.appId,
|
|
106
|
+
buffer: dto.buffer,
|
|
107
|
+
name: dto.name,
|
|
108
|
+
uploadId,
|
|
109
|
+
}, onProgress);
|
|
110
|
+
// 3. Complete the upload
|
|
111
|
+
await this.completeUpload({
|
|
112
|
+
appBuildSourceId: dto.appBuildSourceId,
|
|
113
|
+
appId: dto.appId,
|
|
114
|
+
parts,
|
|
115
|
+
uploadId,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const appBuildSourcesService = new AppBuildSourcesServiceImpl(httpClient);
|
|
120
|
+
export default appBuildSourcesService;
|
|
@@ -5,7 +5,11 @@ class AuthorizationServiceImpl {
|
|
|
5
5
|
this.userConfig = userConfig;
|
|
6
6
|
}
|
|
7
7
|
getCurrentAuthorizationToken() {
|
|
8
|
-
|
|
8
|
+
const token = this.userConfig.read().token || process.env.CAPAWESOME_CLOUD_TOKEN || process.env.CAPAWESOME_TOKEN || null;
|
|
9
|
+
// Trim to remove newline characters that may be included when pasting a token,
|
|
10
|
+
// which would cause an invalid character error in the Authorization header.
|
|
11
|
+
const trimmedToken = token?.trim();
|
|
12
|
+
return trimmedToken || null;
|
|
9
13
|
}
|
|
10
14
|
hasAuthorizationToken() {
|
|
11
15
|
return !!this.getCurrentAuthorizationToken();
|
package/dist/services/jobs.js
CHANGED
|
@@ -5,6 +5,19 @@ class JobsServiceImpl {
|
|
|
5
5
|
constructor(httpClient) {
|
|
6
6
|
this.httpClient = httpClient;
|
|
7
7
|
}
|
|
8
|
+
async findOne(dto) {
|
|
9
|
+
const params = {};
|
|
10
|
+
if (dto.relations) {
|
|
11
|
+
params.relations = dto.relations;
|
|
12
|
+
}
|
|
13
|
+
const response = await this.httpClient.get(`/v1/jobs/${dto.jobId}`, {
|
|
14
|
+
headers: {
|
|
15
|
+
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
|
|
16
|
+
},
|
|
17
|
+
params,
|
|
18
|
+
});
|
|
19
|
+
return response.data;
|
|
20
|
+
}
|
|
8
21
|
async update(options) {
|
|
9
22
|
const { jobId, dto } = options;
|
|
10
23
|
const response = await this.httpClient.patch(`/v1/jobs/${jobId}`, dto, {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/index.js
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const parseCustomProperties = (customProperty) => {
|
|
2
|
+
if (!customProperty) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
const customProperties = {};
|
|
6
|
+
for (const property of customProperty) {
|
|
7
|
+
if (!property) {
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
const separatorIndex = property.indexOf('=');
|
|
11
|
+
if (separatorIndex === -1) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
const key = property.slice(0, separatorIndex).trim();
|
|
15
|
+
const value = property.slice(separatorIndex + 1).trim();
|
|
16
|
+
if (!key || !value) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
customProperties[key] = value;
|
|
20
|
+
}
|
|
21
|
+
return Object.keys(customProperties).length > 0 ? customProperties : undefined;
|
|
22
|
+
};
|
package/dist/utils/file.js
CHANGED
|
@@ -7,7 +7,10 @@ export const getFilesInDirectoryAndSubdirectories = async (path) => {
|
|
|
7
7
|
const dirEntries = await fs.promises.readdir(directory, { withFileTypes: true }).catch(() => []);
|
|
8
8
|
for (const dirEntry of dirEntries) {
|
|
9
9
|
const fullPath = pathModule.join(directory, dirEntry.name);
|
|
10
|
-
if (dirEntry.
|
|
10
|
+
if (dirEntry.isSymbolicLink()) {
|
|
11
|
+
// Skip symlinks
|
|
12
|
+
}
|
|
13
|
+
else if (dirEntry.isDirectory()) {
|
|
11
14
|
await walk(fullPath);
|
|
12
15
|
}
|
|
13
16
|
else {
|
|
@@ -35,6 +38,10 @@ export const getFilesInDirectoryAndSubdirectories = async (path) => {
|
|
|
35
38
|
await walk(path);
|
|
36
39
|
return files;
|
|
37
40
|
};
|
|
41
|
+
export const directoryContainsSymlinks = async (path) => {
|
|
42
|
+
const dirEntries = await fs.promises.readdir(path, { withFileTypes: true, recursive: true }).catch(() => []);
|
|
43
|
+
return dirEntries.some((dirEntry) => dirEntry.isSymbolicLink());
|
|
44
|
+
};
|
|
38
45
|
export const directoryContainsSourceMaps = async (path) => {
|
|
39
46
|
const files = await getFilesInDirectoryAndSubdirectories(path);
|
|
40
47
|
return files.some((file) => file.name.endsWith('.js.map') || file.name.endsWith('.css.map'));
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import jobsService from '../services/jobs.js';
|
|
2
|
+
import { unescapeAnsi } from '../utils/ansi.js';
|
|
3
|
+
import { wait } from '../utils/wait.js';
|
|
4
|
+
import consola from 'consola';
|
|
5
|
+
const getLabel = (job) => {
|
|
6
|
+
if (job.appBuildId) {
|
|
7
|
+
return 'build';
|
|
8
|
+
}
|
|
9
|
+
if (job.appDeploymentId) {
|
|
10
|
+
return 'deployment';
|
|
11
|
+
}
|
|
12
|
+
return 'job';
|
|
13
|
+
};
|
|
14
|
+
const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
15
|
+
export const waitForJobCompletion = async (options) => {
|
|
16
|
+
const { jobId } = options;
|
|
17
|
+
let lastPrintedLogNumber = 0;
|
|
18
|
+
let isWaitingForStart = true;
|
|
19
|
+
while (true) {
|
|
20
|
+
try {
|
|
21
|
+
const job = await jobsService.findOne({ jobId, relations: 'jobLogs' });
|
|
22
|
+
const label = getLabel(job);
|
|
23
|
+
const jobStatus = job.status;
|
|
24
|
+
if (jobStatus === 'queued' || jobStatus === 'pending') {
|
|
25
|
+
if (isWaitingForStart) {
|
|
26
|
+
consola.start(`Waiting for ${label} to start (status: ${jobStatus})...`);
|
|
27
|
+
}
|
|
28
|
+
await wait(3000);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (isWaitingForStart && jobStatus === 'in_progress') {
|
|
32
|
+
isWaitingForStart = false;
|
|
33
|
+
consola.success(`${capitalize(label)} started...`);
|
|
34
|
+
}
|
|
35
|
+
if (job.jobLogs && job.jobLogs.length > 0) {
|
|
36
|
+
const newLogs = job.jobLogs
|
|
37
|
+
.filter((log) => log.number > lastPrintedLogNumber)
|
|
38
|
+
.sort((a, b) => a.number - b.number);
|
|
39
|
+
for (const log of newLogs) {
|
|
40
|
+
console.log(unescapeAnsi(log.payload));
|
|
41
|
+
lastPrintedLogNumber = log.number;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (jobStatus === 'succeeded' ||
|
|
45
|
+
jobStatus === 'failed' ||
|
|
46
|
+
jobStatus === 'canceled' ||
|
|
47
|
+
jobStatus === 'rejected' ||
|
|
48
|
+
jobStatus === 'timed_out') {
|
|
49
|
+
console.log();
|
|
50
|
+
if (jobStatus === 'succeeded') {
|
|
51
|
+
return job;
|
|
52
|
+
}
|
|
53
|
+
else if (jobStatus === 'failed') {
|
|
54
|
+
consola.error(`${capitalize(label)} failed.`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
else if (jobStatus === 'canceled') {
|
|
58
|
+
consola.warn(`${capitalize(label)} was canceled.`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
else if (jobStatus === 'rejected') {
|
|
62
|
+
consola.error(`${capitalize(label)} was rejected.`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
else if (jobStatus === 'timed_out') {
|
|
66
|
+
consola.error(`${capitalize(label)} timed out.`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
await wait(3000);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
consola.error('Error polling job status:', error);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
package/dist/utils/zip.js
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
import AdmZip from 'adm-zip';
|
|
2
|
+
import { globby } from 'globby';
|
|
3
|
+
import path from 'path';
|
|
2
4
|
class ZipImpl {
|
|
3
5
|
async zipFolder(sourceFolder) {
|
|
4
6
|
const zip = new AdmZip();
|
|
5
7
|
zip.addLocalFolder(sourceFolder);
|
|
6
8
|
return zip.toBuffer();
|
|
7
9
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
async zipFolderWithGitignore(sourceFolder) {
|
|
11
|
+
const files = await globby(['**/*'], {
|
|
12
|
+
cwd: sourceFolder,
|
|
13
|
+
gitignore: true,
|
|
14
|
+
ignore: ['.git/**'],
|
|
15
|
+
dot: true,
|
|
16
|
+
});
|
|
17
|
+
const zip = new AdmZip();
|
|
18
|
+
for (const file of files) {
|
|
19
|
+
const filePath = path.join(sourceFolder, file);
|
|
20
|
+
const dirName = path.dirname(file);
|
|
21
|
+
zip.addLocalFile(filePath, dirName === '.' ? '' : dirName);
|
|
22
|
+
}
|
|
23
|
+
return zip.toBuffer();
|
|
24
|
+
}
|
|
25
|
+
isZipped(filePath) {
|
|
26
|
+
return filePath.endsWith('.zip');
|
|
10
27
|
}
|
|
11
28
|
}
|
|
12
29
|
const zip = new ZipImpl();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capawesome/cli",
|
|
3
|
-
"version": "4.6.0-dev.
|
|
3
|
+
"version": "4.6.0-dev.8ae803e.1775035527",
|
|
4
4
|
"description": "The Capawesome Cloud Command Line Interface (CLI) to manage Live Updates and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"c12": "3.3.3",
|
|
62
62
|
"consola": "3.3.0",
|
|
63
63
|
"form-data": "4.0.4",
|
|
64
|
+
"globby": "16.1.1",
|
|
64
65
|
"http-proxy-agent": "7.0.2",
|
|
65
66
|
"https-proxy-agent": "7.0.6",
|
|
66
67
|
"mime": "4.0.7",
|