@capawesome/cli 1.14.0-dev.1f912c9.1755635879 → 1.14.0-dev.3b0fc7e.1755934102
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/README.md +4 -0
- package/dist/commands/apps/bundles/create.js +12 -10
- package/dist/commands/apps/bundles/create.test.js +20 -18
- package/dist/commands/apps/bundles/delete.js +13 -16
- package/dist/commands/apps/bundles/delete.test.js +4 -5
- package/dist/commands/apps/bundles/update.js +13 -21
- package/dist/commands/apps/bundles/update.test.js +7 -8
- package/dist/commands/apps/channels/create.js +16 -6
- package/dist/commands/apps/channels/create.test.js +12 -8
- package/dist/commands/apps/channels/delete.js +15 -18
- package/dist/commands/apps/channels/delete.test.js +7 -6
- package/dist/commands/apps/channels/get.js +34 -42
- package/dist/commands/apps/channels/get.test.js +20 -21
- package/dist/commands/apps/channels/list.js +20 -28
- package/dist/commands/apps/channels/list.test.js +9 -11
- package/dist/commands/apps/channels/update.js +10 -18
- package/dist/commands/apps/channels/update.test.js +7 -8
- package/dist/commands/apps/create.js +11 -14
- package/dist/commands/apps/create.test.js +8 -8
- package/dist/commands/apps/delete.js +10 -13
- package/dist/commands/apps/delete.test.js +7 -6
- package/dist/commands/apps/devices/delete.js +13 -16
- package/dist/commands/apps/devices/delete.test.js +4 -5
- package/dist/commands/login.js +11 -11
- package/dist/commands/login.test.js +8 -14
- package/dist/commands/logout.js +1 -1
- package/dist/commands/logout.test.js +2 -2
- package/dist/commands/manifests/generate.test.js +5 -5
- package/dist/commands/organizations/create.js +25 -0
- package/dist/commands/organizations/create.test.js +80 -0
- package/dist/commands/whoami.js +10 -4
- package/dist/commands/whoami.test.js +2 -2
- package/dist/index.js +4 -0
- package/dist/services/authorization-service.js +1 -1
- package/dist/services/organizations.js +8 -0
- package/dist/utils/private-key.js +23 -0
- package/package.json +2 -2
- /package/dist/utils/{userConfig.js → user-config.js} +0 -0
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# cli
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@capawesome/cli)
|
|
4
|
+
[](https://www.npmjs.com/package/@capawesome/cli)
|
|
5
|
+
[](https://github.com/capawesome-team/cli/blob/main/LICENSE)
|
|
6
|
+
|
|
3
7
|
💻 The Capawesome Cloud Command Line Interface (CLI) can be used to manage [Live Updates](https://capawesome.io/cloud/) from the command line.
|
|
4
8
|
|
|
5
9
|
## Installation
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
2
|
-
import consola from 'consola';
|
|
3
|
-
import { createReadStream } from 'fs';
|
|
4
|
-
import { z } from 'zod';
|
|
5
1
|
import { MAX_CONCURRENT_UPLOADS } from '../../../config/index.js';
|
|
6
2
|
import appBundleFilesService from '../../../services/app-bundle-files.js';
|
|
7
3
|
import appBundlesService from '../../../services/app-bundles.js';
|
|
@@ -9,13 +5,17 @@ import appsService from '../../../services/apps.js';
|
|
|
9
5
|
import authorizationService from '../../../services/authorization-service.js';
|
|
10
6
|
import organizationsService from '../../../services/organizations.js';
|
|
11
7
|
import { createBufferFromPath, createBufferFromReadStream, createBufferFromString, isPrivateKeyContent, } from '../../../utils/buffer.js';
|
|
12
|
-
import {
|
|
8
|
+
import { formatPrivateKey } from '../../../utils/private-key.js';
|
|
13
9
|
import { fileExistsAtPath, getFilesInDirectoryAndSubdirectories, isDirectory } from '../../../utils/file.js';
|
|
14
10
|
import { createHash } from '../../../utils/hash.js';
|
|
15
11
|
import { generateManifestJson } from '../../../utils/manifest.js';
|
|
16
12
|
import { prompt } from '../../../utils/prompt.js';
|
|
17
13
|
import { createSignature } from '../../../utils/signature.js';
|
|
18
14
|
import zip from '../../../utils/zip.js';
|
|
15
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
16
|
+
import consola from 'consola';
|
|
17
|
+
import { createReadStream } from 'fs';
|
|
18
|
+
import { z } from 'zod';
|
|
19
19
|
export default defineCommand({
|
|
20
20
|
description: 'Create a new app bundle.',
|
|
21
21
|
options: defineOptions(z.object({
|
|
@@ -194,13 +194,17 @@ export default defineCommand({
|
|
|
194
194
|
if (privateKey) {
|
|
195
195
|
if (isPrivateKeyContent(privateKey)) {
|
|
196
196
|
// Handle plain text private key content
|
|
197
|
-
|
|
197
|
+
const formattedPrivateKey = formatPrivateKey(privateKey);
|
|
198
|
+
privateKeyBuffer = createBufferFromString(formattedPrivateKey);
|
|
198
199
|
}
|
|
199
200
|
else if (privateKey.endsWith('.pem')) {
|
|
200
201
|
// Handle file path
|
|
201
202
|
const fileExists = await fileExistsAtPath(privateKey);
|
|
202
203
|
if (fileExists) {
|
|
203
|
-
|
|
204
|
+
const keyBuffer = await createBufferFromPath(privateKey);
|
|
205
|
+
const keyContent = keyBuffer.toString('utf8');
|
|
206
|
+
const formattedPrivateKey = formatPrivateKey(keyContent);
|
|
207
|
+
privateKeyBuffer = createBufferFromString(formattedPrivateKey);
|
|
204
208
|
}
|
|
205
209
|
else {
|
|
206
210
|
consola.error('Private key file not found.');
|
|
@@ -285,9 +289,7 @@ export default defineCommand({
|
|
|
285
289
|
// No-op
|
|
286
290
|
});
|
|
287
291
|
}
|
|
288
|
-
|
|
289
|
-
consola.error(message);
|
|
290
|
-
process.exit(1);
|
|
292
|
+
throw error;
|
|
291
293
|
}
|
|
292
294
|
},
|
|
293
295
|
});
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { DEFAULT_API_BASE_URL } from '../../../config/consts.js';
|
|
2
2
|
import authorizationService from '../../../services/authorization-service.js';
|
|
3
3
|
import { fileExistsAtPath, isDirectory } from '../../../utils/file.js';
|
|
4
|
-
import userConfig from '../../../utils/
|
|
4
|
+
import userConfig from '../../../utils/user-config.js';
|
|
5
5
|
import consola from 'consola';
|
|
6
6
|
import nock from 'nock';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
8
|
import createBundleCommand from './create.js';
|
|
9
9
|
// Mock dependencies
|
|
10
|
-
vi.mock('@/utils/
|
|
10
|
+
vi.mock('@/utils/user-config.js');
|
|
11
11
|
vi.mock('@/services/authorization-service.js');
|
|
12
12
|
vi.mock('@/utils/file.js');
|
|
13
13
|
vi.mock('@/utils/zip.js');
|
|
14
14
|
vi.mock('@/utils/buffer.js');
|
|
15
|
+
vi.mock('@/utils/private-key.js');
|
|
15
16
|
vi.mock('@/utils/hash.js');
|
|
16
17
|
vi.mock('@/utils/signature.js');
|
|
17
18
|
vi.mock('consola');
|
|
@@ -26,7 +27,9 @@ describe('apps-bundles-create', () => {
|
|
|
26
27
|
mockUserConfig.read.mockReturnValue({ token: 'test-token' });
|
|
27
28
|
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(true);
|
|
28
29
|
mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue('test-token');
|
|
29
|
-
vi.spyOn(process, 'exit').mockImplementation(() =>
|
|
30
|
+
vi.spyOn(process, 'exit').mockImplementation((code) => {
|
|
31
|
+
throw new Error(`Process exited with code ${code}`);
|
|
32
|
+
});
|
|
30
33
|
});
|
|
31
34
|
afterEach(() => {
|
|
32
35
|
nock.cleanAll();
|
|
@@ -36,9 +39,8 @@ describe('apps-bundles-create', () => {
|
|
|
36
39
|
const appId = 'app-123';
|
|
37
40
|
const options = { appId, path: './dist', artifactType: 'zip', rollout: 1 };
|
|
38
41
|
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(false);
|
|
39
|
-
await createBundleCommand.action(options, undefined);
|
|
42
|
+
await expect(createBundleCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
40
43
|
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command.');
|
|
41
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
42
44
|
});
|
|
43
45
|
it('should create bundle with self-hosted URL', async () => {
|
|
44
46
|
const appId = 'app-123';
|
|
@@ -84,9 +86,8 @@ describe('apps-bundles-create', () => {
|
|
|
84
86
|
const nonexistentPath = './nonexistent';
|
|
85
87
|
const options = { appId, path: nonexistentPath, artifactType: 'zip', rollout: 1 };
|
|
86
88
|
mockFileExistsAtPath.mockResolvedValue(false);
|
|
87
|
-
await createBundleCommand.action(options, undefined);
|
|
89
|
+
await expect(createBundleCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
88
90
|
expect(mockConsola.error).toHaveBeenCalledWith('The path does not exist.');
|
|
89
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
90
91
|
});
|
|
91
92
|
it('should validate manifest artifact type requires directory', async () => {
|
|
92
93
|
const appId = 'app-123';
|
|
@@ -99,9 +100,8 @@ describe('apps-bundles-create', () => {
|
|
|
99
100
|
};
|
|
100
101
|
mockFileExistsAtPath.mockResolvedValue(true);
|
|
101
102
|
mockIsDirectory.mockResolvedValue(false);
|
|
102
|
-
await createBundleCommand.action(options, undefined);
|
|
103
|
+
await expect(createBundleCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
103
104
|
expect(mockConsola.error).toHaveBeenCalledWith('The path must be a folder when creating a bundle with an artifact type of `manifest`.');
|
|
104
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
105
105
|
});
|
|
106
106
|
it('should validate manifest artifact type cannot use URL', async () => {
|
|
107
107
|
const appId = 'app-123';
|
|
@@ -112,9 +112,8 @@ describe('apps-bundles-create', () => {
|
|
|
112
112
|
artifactType: 'manifest',
|
|
113
113
|
rollout: 1,
|
|
114
114
|
};
|
|
115
|
-
await createBundleCommand.action(options, undefined);
|
|
115
|
+
await expect(createBundleCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
116
116
|
expect(mockConsola.error).toHaveBeenCalledWith('It is not yet possible to provide a URL when creating a bundle with an artifact type of `manifest`.');
|
|
117
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
118
117
|
});
|
|
119
118
|
it('should handle API error during creation', async () => {
|
|
120
119
|
const appId = 'app-123';
|
|
@@ -130,10 +129,8 @@ describe('apps-bundles-create', () => {
|
|
|
130
129
|
.post(`/v1/apps/${appId}/bundles`)
|
|
131
130
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
132
131
|
.reply(400, { message: 'Invalid bundle data' });
|
|
133
|
-
await createBundleCommand.action(options, undefined);
|
|
132
|
+
await expect(createBundleCommand.action(options, undefined)).rejects.toThrow();
|
|
134
133
|
expect(scope.isDone()).toBe(true);
|
|
135
|
-
expect(mockConsola.error).toHaveBeenCalled();
|
|
136
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
137
134
|
});
|
|
138
135
|
it('should handle private key file path', async () => {
|
|
139
136
|
const appId = 'app-123';
|
|
@@ -164,11 +161,14 @@ describe('apps-bundles-create', () => {
|
|
|
164
161
|
// Mock utility functions
|
|
165
162
|
const mockZip = await import('../../../utils/zip.js');
|
|
166
163
|
const mockBuffer = await import('../../../utils/buffer.js');
|
|
164
|
+
const mockPrivateKey = await import('../../../utils/private-key.js');
|
|
167
165
|
const mockHash = await import('../../../utils/hash.js');
|
|
168
166
|
const mockSignature = await import('../../../utils/signature.js');
|
|
169
167
|
vi.mocked(mockZip.default.isZipped).mockReturnValue(true);
|
|
170
168
|
vi.mocked(mockBuffer.createBufferFromPath).mockResolvedValue(testBuffer);
|
|
171
169
|
vi.mocked(mockBuffer.isPrivateKeyContent).mockReturnValue(false);
|
|
170
|
+
vi.mocked(mockPrivateKey.formatPrivateKey).mockReturnValue('formatted-private-key');
|
|
171
|
+
vi.mocked(mockBuffer.createBufferFromString).mockReturnValue(testBuffer);
|
|
172
172
|
vi.mocked(mockHash.createHash).mockResolvedValue(testHash);
|
|
173
173
|
vi.mocked(mockSignature.createSignature).mockResolvedValue(testSignature);
|
|
174
174
|
const scope = nock(DEFAULT_API_BASE_URL)
|
|
@@ -209,12 +209,14 @@ describe('apps-bundles-create', () => {
|
|
|
209
209
|
// Mock utility functions
|
|
210
210
|
const mockZip = await import('../../../utils/zip.js');
|
|
211
211
|
const mockBuffer = await import('../../../utils/buffer.js');
|
|
212
|
+
const mockPrivateKey = await import('../../../utils/private-key.js');
|
|
212
213
|
const mockHash = await import('../../../utils/hash.js');
|
|
213
214
|
const mockSignature = await import('../../../utils/signature.js');
|
|
214
215
|
vi.mocked(mockZip.default.isZipped).mockReturnValue(true);
|
|
215
216
|
vi.mocked(mockBuffer.createBufferFromPath).mockResolvedValue(testBuffer);
|
|
216
217
|
vi.mocked(mockBuffer.createBufferFromString).mockReturnValue(testBuffer);
|
|
217
218
|
vi.mocked(mockBuffer.isPrivateKeyContent).mockReturnValue(true);
|
|
219
|
+
vi.mocked(mockPrivateKey.formatPrivateKey).mockReturnValue('formatted-private-key');
|
|
218
220
|
vi.mocked(mockHash.createHash).mockResolvedValue(testHash);
|
|
219
221
|
vi.mocked(mockSignature.createSignature).mockResolvedValue(testSignature);
|
|
220
222
|
const scope = nock(DEFAULT_API_BASE_URL)
|
|
@@ -250,9 +252,8 @@ describe('apps-bundles-create', () => {
|
|
|
250
252
|
// Mock utility functions
|
|
251
253
|
const mockBuffer = await import('../../../utils/buffer.js');
|
|
252
254
|
vi.mocked(mockBuffer.isPrivateKeyContent).mockReturnValue(false);
|
|
253
|
-
await createBundleCommand.action(options, undefined);
|
|
255
|
+
await expect(createBundleCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
254
256
|
expect(mockConsola.error).toHaveBeenCalledWith('Private key file not found.');
|
|
255
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
256
257
|
});
|
|
257
258
|
it('should handle invalid private key format', async () => {
|
|
258
259
|
const appId = 'app-123';
|
|
@@ -264,11 +265,12 @@ describe('apps-bundles-create', () => {
|
|
|
264
265
|
artifactType: 'zip',
|
|
265
266
|
rollout: 1,
|
|
266
267
|
};
|
|
268
|
+
mockFileExistsAtPath.mockResolvedValue(true);
|
|
269
|
+
mockIsDirectory.mockResolvedValue(false);
|
|
267
270
|
// Mock utility functions
|
|
268
271
|
const mockBuffer = await import('../../../utils/buffer.js');
|
|
269
272
|
vi.mocked(mockBuffer.isPrivateKeyContent).mockReturnValue(false);
|
|
270
|
-
await createBundleCommand.action(options, undefined);
|
|
273
|
+
await expect(createBundleCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
271
274
|
expect(mockConsola.error).toHaveBeenCalledWith('Private key must be either a path to a .pem file or the private key content as plain text.');
|
|
272
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
273
275
|
});
|
|
274
276
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
2
|
-
import consola from 'consola';
|
|
3
|
-
import { z } from 'zod';
|
|
4
1
|
import appBundlesService from '../../../services/app-bundles.js';
|
|
5
2
|
import appsService from '../../../services/apps.js';
|
|
3
|
+
import authorizationService from '../../../services/authorization-service.js';
|
|
6
4
|
import organizationsService from '../../../services/organizations.js';
|
|
7
|
-
import { getMessageFromUnknownError } from '../../../utils/error.js';
|
|
8
5
|
import { prompt } from '../../../utils/prompt.js';
|
|
6
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
7
|
+
import consola from 'consola';
|
|
8
|
+
import { z } from 'zod';
|
|
9
9
|
export default defineCommand({
|
|
10
10
|
description: 'Delete an app bundle.',
|
|
11
11
|
options: defineOptions(z.object({
|
|
@@ -14,6 +14,10 @@ export default defineCommand({
|
|
|
14
14
|
})),
|
|
15
15
|
action: async (options, args) => {
|
|
16
16
|
let { appId, bundleId } = options;
|
|
17
|
+
if (!authorizationService.hasAuthorizationToken()) {
|
|
18
|
+
consola.error('You must be logged in to run this command.');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
17
21
|
// Prompt for missing arguments
|
|
18
22
|
if (!appId) {
|
|
19
23
|
const organizations = await organizationsService.findAll();
|
|
@@ -56,17 +60,10 @@ export default defineCommand({
|
|
|
56
60
|
return;
|
|
57
61
|
}
|
|
58
62
|
// Delete bundle
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
consola.success('Bundle deleted successfully.');
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
const message = getMessageFromUnknownError(error);
|
|
68
|
-
consola.error(message);
|
|
69
|
-
process.exit(1);
|
|
70
|
-
}
|
|
63
|
+
await appBundlesService.delete({
|
|
64
|
+
appId,
|
|
65
|
+
appBundleId: bundleId,
|
|
66
|
+
});
|
|
67
|
+
consola.success('Bundle deleted successfully.');
|
|
71
68
|
},
|
|
72
69
|
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { DEFAULT_API_BASE_URL } from '../../../config/consts.js';
|
|
2
2
|
import authorizationService from '../../../services/authorization-service.js';
|
|
3
3
|
import { prompt } from '../../../utils/prompt.js';
|
|
4
|
-
import userConfig from '../../../utils/
|
|
4
|
+
import userConfig from '../../../utils/user-config.js';
|
|
5
5
|
import consola from 'consola';
|
|
6
6
|
import nock from 'nock';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
8
|
import deleteBundleCommand from './delete.js';
|
|
9
9
|
// Mock dependencies
|
|
10
|
-
vi.mock('@/utils/
|
|
10
|
+
vi.mock('@/utils/user-config.js');
|
|
11
11
|
vi.mock('@/utils/prompt.js');
|
|
12
12
|
vi.mock('@/services/authorization-service.js');
|
|
13
13
|
vi.mock('consola');
|
|
@@ -20,6 +20,7 @@ describe('apps-bundles-delete', () => {
|
|
|
20
20
|
vi.clearAllMocks();
|
|
21
21
|
mockUserConfig.read.mockReturnValue({ token: 'test-token' });
|
|
22
22
|
mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue('test-token');
|
|
23
|
+
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(true);
|
|
23
24
|
vi.spyOn(process, 'exit').mockImplementation(() => undefined);
|
|
24
25
|
});
|
|
25
26
|
afterEach(() => {
|
|
@@ -132,9 +133,7 @@ describe('apps-bundles-delete', () => {
|
|
|
132
133
|
.delete(`/v1/apps/${appId}/bundles/${bundleId}`)
|
|
133
134
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
134
135
|
.reply(404, { message: 'Bundle not found' });
|
|
135
|
-
await deleteBundleCommand.action(options, undefined);
|
|
136
|
+
await expect(deleteBundleCommand.action(options, undefined)).rejects.toThrow();
|
|
136
137
|
expect(scope.isDone()).toBe(true);
|
|
137
|
-
expect(mockConsola.error).toHaveBeenCalled();
|
|
138
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
139
138
|
});
|
|
140
139
|
});
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
2
|
-
import consola from 'consola';
|
|
3
|
-
import { z } from 'zod';
|
|
4
1
|
import appBundlesService from '../../../services/app-bundles.js';
|
|
5
2
|
import appsService from '../../../services/apps.js';
|
|
6
3
|
import authorizationService from '../../../services/authorization-service.js';
|
|
7
4
|
import organizationsService from '../../../services/organizations.js';
|
|
8
|
-
import { getMessageFromUnknownError } from '../../../utils/error.js';
|
|
9
5
|
import { prompt } from '../../../utils/prompt.js';
|
|
6
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
7
|
+
import consola from 'consola';
|
|
8
|
+
import { z } from 'zod';
|
|
10
9
|
export default defineCommand({
|
|
11
10
|
description: 'Update an app bundle.',
|
|
12
11
|
options: defineOptions(z.object({
|
|
@@ -78,22 +77,15 @@ export default defineCommand({
|
|
|
78
77
|
});
|
|
79
78
|
}
|
|
80
79
|
// Update bundle
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
consola.success('Bundle updated successfully.');
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
const message = getMessageFromUnknownError(error);
|
|
95
|
-
consola.error(message);
|
|
96
|
-
process.exit(1);
|
|
97
|
-
}
|
|
80
|
+
await appBundlesService.update({
|
|
81
|
+
appId,
|
|
82
|
+
appBundleId: bundleId,
|
|
83
|
+
maxAndroidAppVersionCode: androidMax,
|
|
84
|
+
maxIosAppVersionCode: iosMax,
|
|
85
|
+
minAndroidAppVersionCode: androidMin,
|
|
86
|
+
minIosAppVersionCode: iosMin,
|
|
87
|
+
rolloutPercentage: rollout,
|
|
88
|
+
});
|
|
89
|
+
consola.success('Bundle updated successfully.');
|
|
98
90
|
},
|
|
99
91
|
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { DEFAULT_API_BASE_URL } from '../../../config/consts.js';
|
|
2
2
|
import authorizationService from '../../../services/authorization-service.js';
|
|
3
3
|
import { prompt } from '../../../utils/prompt.js';
|
|
4
|
-
import userConfig from '../../../utils/
|
|
4
|
+
import userConfig from '../../../utils/user-config.js';
|
|
5
5
|
import consola from 'consola';
|
|
6
6
|
import nock from 'nock';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
8
|
import updateBundleCommand from './update.js';
|
|
9
9
|
// Mock dependencies
|
|
10
|
-
vi.mock('@/utils/
|
|
10
|
+
vi.mock('@/utils/user-config.js');
|
|
11
11
|
vi.mock('@/utils/prompt.js');
|
|
12
12
|
vi.mock('@/services/authorization-service.js');
|
|
13
13
|
vi.mock('consola');
|
|
@@ -21,7 +21,9 @@ describe('apps-bundles-update', () => {
|
|
|
21
21
|
mockUserConfig.read.mockReturnValue({ token: 'test-token' });
|
|
22
22
|
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(true);
|
|
23
23
|
mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue('test-token');
|
|
24
|
-
vi.spyOn(process, 'exit').mockImplementation(() =>
|
|
24
|
+
vi.spyOn(process, 'exit').mockImplementation((code) => {
|
|
25
|
+
throw new Error(`Process exited with code ${code}`);
|
|
26
|
+
});
|
|
25
27
|
});
|
|
26
28
|
afterEach(() => {
|
|
27
29
|
nock.cleanAll();
|
|
@@ -32,9 +34,8 @@ describe('apps-bundles-update', () => {
|
|
|
32
34
|
const bundleId = 'bundle-456';
|
|
33
35
|
const options = { appId, bundleId };
|
|
34
36
|
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(false);
|
|
35
|
-
await updateBundleCommand.action(options, undefined);
|
|
37
|
+
await expect(updateBundleCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
36
38
|
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command.');
|
|
37
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
38
39
|
});
|
|
39
40
|
it('should update bundle with provided options', async () => {
|
|
40
41
|
const appId = 'app-123';
|
|
@@ -114,10 +115,8 @@ describe('apps-bundles-update', () => {
|
|
|
114
115
|
.patch(`/v1/apps/${appId}/bundles/${bundleId}`)
|
|
115
116
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
116
117
|
.reply(404, { message: 'Bundle not found' });
|
|
117
|
-
await updateBundleCommand.action(options, undefined);
|
|
118
|
+
await expect(updateBundleCommand.action(options, undefined)).rejects.toThrow();
|
|
118
119
|
expect(scope.isDone()).toBe(true);
|
|
119
|
-
expect(mockConsola.error).toHaveBeenCalled();
|
|
120
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
121
120
|
});
|
|
122
121
|
it('should handle error when no organizations exist', async () => {
|
|
123
122
|
const testToken = 'test-token';
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
2
|
-
import consola from 'consola';
|
|
3
|
-
import { z } from 'zod';
|
|
4
1
|
import appChannelsService from '../../../services/app-channels.js';
|
|
5
2
|
import appsService from '../../../services/apps.js';
|
|
3
|
+
import authorizationService from '../../../services/authorization-service.js';
|
|
6
4
|
import organizationsService from '../../../services/organizations.js';
|
|
7
5
|
import { getMessageFromUnknownError } from '../../../utils/error.js';
|
|
8
6
|
import { prompt } from '../../../utils/prompt.js';
|
|
7
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
8
|
+
import consola from 'consola';
|
|
9
|
+
import { z } from 'zod';
|
|
9
10
|
export default defineCommand({
|
|
10
11
|
description: 'Create a new app channel.',
|
|
11
12
|
options: defineOptions(z.object({
|
|
@@ -19,6 +20,10 @@ export default defineCommand({
|
|
|
19
20
|
})),
|
|
20
21
|
action: async (options, args) => {
|
|
21
22
|
let { appId, bundleLimit, ignoreErrors, name } = options;
|
|
23
|
+
if (!authorizationService.hasAuthorizationToken()) {
|
|
24
|
+
consola.error('You must be logged in to run this command.');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
22
27
|
// Validate the app ID
|
|
23
28
|
if (!appId) {
|
|
24
29
|
const organizations = await organizationsService.findAll();
|
|
@@ -62,9 +67,14 @@ export default defineCommand({
|
|
|
62
67
|
consola.info(`Channel ID: ${response.id}`);
|
|
63
68
|
}
|
|
64
69
|
catch (error) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
if (ignoreErrors) {
|
|
71
|
+
const message = getMessageFromUnknownError(error);
|
|
72
|
+
consola.error(message);
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
68
78
|
}
|
|
69
79
|
},
|
|
70
80
|
});
|
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
import { DEFAULT_API_BASE_URL } from '../../../config/consts.js';
|
|
2
|
+
import authorizationService from '../../../services/authorization-service.js';
|
|
2
3
|
import { prompt } from '../../../utils/prompt.js';
|
|
3
|
-
import userConfig from '../../../utils/
|
|
4
|
+
import userConfig from '../../../utils/user-config.js';
|
|
4
5
|
import consola from 'consola';
|
|
5
6
|
import nock from 'nock';
|
|
6
7
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
8
|
import createChannelCommand from './create.js';
|
|
8
9
|
// Mock dependencies
|
|
9
|
-
vi.mock('@/utils/
|
|
10
|
+
vi.mock('@/utils/user-config.js');
|
|
10
11
|
vi.mock('@/utils/prompt.js');
|
|
12
|
+
vi.mock('@/services/authorization-service.js');
|
|
11
13
|
vi.mock('consola');
|
|
12
14
|
describe('apps-channels-create', () => {
|
|
13
15
|
const mockUserConfig = vi.mocked(userConfig);
|
|
14
16
|
const mockPrompt = vi.mocked(prompt);
|
|
15
17
|
const mockConsola = vi.mocked(consola);
|
|
18
|
+
const mockAuthorizationService = vi.mocked(authorizationService);
|
|
16
19
|
beforeEach(() => {
|
|
17
20
|
vi.clearAllMocks();
|
|
18
21
|
mockUserConfig.read.mockReturnValue({ token: 'test-token' });
|
|
19
|
-
|
|
22
|
+
mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue('test-token');
|
|
23
|
+
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(true);
|
|
24
|
+
vi.spyOn(process, 'exit').mockImplementation((code) => {
|
|
25
|
+
throw new Error(`Process exited with code ${code}`);
|
|
26
|
+
});
|
|
20
27
|
});
|
|
21
28
|
afterEach(() => {
|
|
22
29
|
nock.cleanAll();
|
|
@@ -96,10 +103,9 @@ describe('apps-channels-create', () => {
|
|
|
96
103
|
.post('/v1/apps/app-123/channels')
|
|
97
104
|
.matchHeader('Authorization', 'Bearer test-token')
|
|
98
105
|
.reply(400, { message: 'Channel name already exists' });
|
|
99
|
-
await createChannelCommand.action(options, undefined);
|
|
106
|
+
await expect(createChannelCommand.action(options, undefined)).rejects.toThrow('Process exited with code 0');
|
|
100
107
|
expect(scope.isDone()).toBe(true);
|
|
101
108
|
expect(mockConsola.error).toHaveBeenCalled();
|
|
102
|
-
expect(process.exit).toHaveBeenCalledWith(0);
|
|
103
109
|
});
|
|
104
110
|
it('should handle error without ignoreErrors flag', async () => {
|
|
105
111
|
const options = { appId: 'app-123', name: 'production' };
|
|
@@ -107,9 +113,7 @@ describe('apps-channels-create', () => {
|
|
|
107
113
|
.post('/v1/apps/app-123/channels')
|
|
108
114
|
.matchHeader('Authorization', 'Bearer test-token')
|
|
109
115
|
.reply(400, { message: 'Channel name already exists' });
|
|
110
|
-
await createChannelCommand.action(options, undefined);
|
|
116
|
+
await expect(createChannelCommand.action(options, undefined)).rejects.toThrow();
|
|
111
117
|
expect(scope.isDone()).toBe(true);
|
|
112
|
-
expect(mockConsola.error).toHaveBeenCalled();
|
|
113
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
114
118
|
});
|
|
115
119
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
2
|
-
import consola from 'consola';
|
|
3
|
-
import { isCI } from 'std-env';
|
|
4
|
-
import { z } from 'zod';
|
|
5
1
|
import appChannelsService from '../../../services/app-channels.js';
|
|
6
2
|
import appsService from '../../../services/apps.js';
|
|
3
|
+
import authorizationService from '../../../services/authorization-service.js';
|
|
7
4
|
import organizationsService from '../../../services/organizations.js';
|
|
8
|
-
import { getMessageFromUnknownError } from '../../../utils/error.js';
|
|
9
5
|
import { prompt } from '../../../utils/prompt.js';
|
|
6
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
7
|
+
import consola from 'consola';
|
|
8
|
+
import { isCI } from 'std-env';
|
|
9
|
+
import { z } from 'zod';
|
|
10
10
|
export default defineCommand({
|
|
11
11
|
description: 'Delete an app channel.',
|
|
12
12
|
options: defineOptions(z.object({
|
|
@@ -22,6 +22,10 @@ export default defineCommand({
|
|
|
22
22
|
})),
|
|
23
23
|
action: async (options, args) => {
|
|
24
24
|
let { appId, channelId, name } = options;
|
|
25
|
+
if (!authorizationService.hasAuthorizationToken()) {
|
|
26
|
+
consola.error('You must be logged in to run this command.');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
25
29
|
if (!appId) {
|
|
26
30
|
const organizations = await organizationsService.findAll();
|
|
27
31
|
if (organizations.length === 0) {
|
|
@@ -63,18 +67,11 @@ export default defineCommand({
|
|
|
63
67
|
return;
|
|
64
68
|
}
|
|
65
69
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
consola.success('Channel deleted successfully.');
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
const message = getMessageFromUnknownError(error);
|
|
76
|
-
consola.error(message);
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
70
|
+
await appChannelsService.delete({
|
|
71
|
+
appId,
|
|
72
|
+
id: channelId,
|
|
73
|
+
name,
|
|
74
|
+
});
|
|
75
|
+
consola.success('Channel deleted successfully.');
|
|
79
76
|
},
|
|
80
77
|
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { DEFAULT_API_BASE_URL } from '../../../config/consts.js';
|
|
2
2
|
import authorizationService from '../../../services/authorization-service.js';
|
|
3
3
|
import { prompt } from '../../../utils/prompt.js';
|
|
4
|
-
import userConfig from '../../../utils/
|
|
4
|
+
import userConfig from '../../../utils/user-config.js';
|
|
5
5
|
import consola from 'consola';
|
|
6
6
|
import nock from 'nock';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
8
8
|
import deleteChannelCommand from './delete.js';
|
|
9
9
|
// Mock dependencies
|
|
10
|
-
vi.mock('@/utils/
|
|
10
|
+
vi.mock('@/utils/user-config.js');
|
|
11
11
|
vi.mock('@/utils/prompt.js');
|
|
12
12
|
vi.mock('@/services/authorization-service.js');
|
|
13
13
|
vi.mock('consola');
|
|
@@ -23,7 +23,10 @@ describe('apps-channels-delete', () => {
|
|
|
23
23
|
vi.clearAllMocks();
|
|
24
24
|
mockUserConfig.read.mockReturnValue({ token: 'test-token' });
|
|
25
25
|
mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue('test-token');
|
|
26
|
-
|
|
26
|
+
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(true);
|
|
27
|
+
vi.spyOn(process, 'exit').mockImplementation((code) => {
|
|
28
|
+
throw new Error(`Process exited with code ${code}`);
|
|
29
|
+
});
|
|
27
30
|
});
|
|
28
31
|
afterEach(() => {
|
|
29
32
|
nock.cleanAll();
|
|
@@ -132,9 +135,7 @@ describe('apps-channels-delete', () => {
|
|
|
132
135
|
.delete(`/v1/apps/${appId}/channels/${channelId}`)
|
|
133
136
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
134
137
|
.reply(404, { message: 'Channel not found' });
|
|
135
|
-
await deleteChannelCommand.action(options, undefined);
|
|
138
|
+
await expect(deleteChannelCommand.action(options, undefined)).rejects.toThrow();
|
|
136
139
|
expect(scope.isDone()).toBe(true);
|
|
137
|
-
expect(mockConsola.error).toHaveBeenCalled();
|
|
138
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
139
140
|
});
|
|
140
141
|
});
|