@capawesome/cli 4.18.0-dev.9810ff51.1785743282 → 4.18.1

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.
Files changed (89) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/commands/apps/automations/create.js +3 -3
  3. package/dist/commands/apps/automations/delete.js +1 -1
  4. package/dist/commands/apps/automations/get.js +1 -1
  5. package/dist/commands/apps/automations/list.js +1 -1
  6. package/dist/commands/apps/automations/update.js +3 -3
  7. package/dist/commands/apps/builds/cancel.js +1 -1
  8. package/dist/commands/apps/builds/create.js +1 -1
  9. package/dist/commands/apps/builds/download.js +1 -1
  10. package/dist/commands/apps/builds/failure-summary.js +1 -1
  11. package/dist/commands/apps/builds/get.js +1 -1
  12. package/dist/commands/apps/builds/list.js +1 -1
  13. package/dist/commands/apps/builds/logs.js +1 -1
  14. package/dist/commands/apps/builds/run.js +1 -1
  15. package/dist/commands/apps/builds/share.js +1 -1
  16. package/dist/commands/apps/builds/unshare.js +1 -1
  17. package/dist/commands/apps/bundles/create.js +1 -1
  18. package/dist/commands/apps/bundles/delete.js +1 -1
  19. package/dist/commands/apps/bundles/update.js +1 -1
  20. package/dist/commands/apps/certificates/create.js +1 -1
  21. package/dist/commands/apps/certificates/delete.js +1 -1
  22. package/dist/commands/apps/certificates/get.js +1 -1
  23. package/dist/commands/apps/certificates/list.js +1 -1
  24. package/dist/commands/apps/certificates/update.js +1 -1
  25. package/dist/commands/apps/channels/create.js +1 -1
  26. package/dist/commands/apps/channels/delete.js +1 -1
  27. package/dist/commands/apps/channels/get.js +1 -1
  28. package/dist/commands/apps/channels/list.js +1 -1
  29. package/dist/commands/apps/channels/pause.js +1 -1
  30. package/dist/commands/apps/channels/resume.js +1 -1
  31. package/dist/commands/apps/channels/update.js +1 -1
  32. package/dist/commands/apps/configurations/create.js +1 -1
  33. package/dist/commands/apps/configurations/delete.js +1 -1
  34. package/dist/commands/apps/configurations/get.js +1 -1
  35. package/dist/commands/apps/configurations/list.js +1 -1
  36. package/dist/commands/apps/configurations/update.js +1 -1
  37. package/dist/commands/apps/create.js +1 -1
  38. package/dist/commands/apps/delete.js +1 -1
  39. package/dist/commands/apps/deployments/cancel.js +1 -1
  40. package/dist/commands/apps/deployments/create.js +1 -1
  41. package/dist/commands/apps/deployments/failure-summary.js +1 -1
  42. package/dist/commands/apps/deployments/get.js +1 -1
  43. package/dist/commands/apps/deployments/list.js +1 -1
  44. package/dist/commands/apps/deployments/logs.js +1 -1
  45. package/dist/commands/apps/destinations/create.js +1 -1
  46. package/dist/commands/apps/destinations/delete.js +1 -1
  47. package/dist/commands/apps/destinations/get.js +1 -1
  48. package/dist/commands/apps/destinations/list.js +1 -1
  49. package/dist/commands/apps/destinations/update.js +1 -1
  50. package/dist/commands/apps/devices/delete.js +1 -1
  51. package/dist/commands/apps/devices/forcechannel.js +1 -1
  52. package/dist/commands/apps/devices/probe.js +1 -1
  53. package/dist/commands/apps/devices/unforcechannel.js +1 -1
  54. package/dist/commands/apps/environments/create.js +1 -1
  55. package/dist/commands/apps/environments/delete.js +1 -1
  56. package/dist/commands/apps/environments/get.js +1 -1
  57. package/dist/commands/apps/environments/list.js +1 -1
  58. package/dist/commands/apps/environments/set.js +1 -1
  59. package/dist/commands/apps/environments/unset.js +1 -1
  60. package/dist/commands/apps/get.js +1 -1
  61. package/dist/commands/apps/link.js +1 -1
  62. package/dist/commands/apps/list.js +1 -1
  63. package/dist/commands/apps/liveupdates/bundle.js +1 -1
  64. package/dist/commands/apps/liveupdates/create.js +1 -1
  65. package/dist/commands/apps/liveupdates/generate-manifest.js +1 -1
  66. package/dist/commands/apps/liveupdates/generate-signing-key.js +1 -1
  67. package/dist/commands/apps/liveupdates/register.js +1 -1
  68. package/dist/commands/apps/liveupdates/rollback.js +1 -1
  69. package/dist/commands/apps/liveupdates/rollout.js +1 -1
  70. package/dist/commands/apps/liveupdates/set-native-versions.js +1 -1
  71. package/dist/commands/apps/liveupdates/upload.js +1 -1
  72. package/dist/commands/apps/transfer.js +1 -1
  73. package/dist/commands/apps/unlink.js +1 -1
  74. package/dist/commands/doctor.js +1 -1
  75. package/dist/commands/login.js +4 -4
  76. package/dist/commands/logout.js +1 -1
  77. package/dist/commands/manifests/generate.js +1 -1
  78. package/dist/commands/organizations/create.js +1 -1
  79. package/dist/commands/organizations/get.js +1 -1
  80. package/dist/commands/organizations/list.js +1 -1
  81. package/dist/commands/whoami.js +1 -1
  82. package/dist/index.js +2 -4
  83. package/dist/index.test.js +1 -1
  84. package/package.json +3 -3
  85. package/dist/commands/apps/import.js +0 -419
  86. package/dist/commands/apps/import.test.js +0 -308
  87. package/dist/utils/app-import.js +0 -10
  88. package/dist/utils/appflow-export.js +0 -391
  89. package/dist/utils/appflow-export.test.js +0 -277
@@ -1,308 +0,0 @@
1
- import { DEFAULT_API_BASE_URL } from '../../config/consts.js';
2
- import authorizationService from '../../services/authorization-service.js';
3
- import userConfig from '../../utils/user-config.js';
4
- import zip from '../../utils/zip.js';
5
- import consola from 'consola';
6
- import fs from 'fs';
7
- import nock from 'nock';
8
- import os from 'os';
9
- import path from 'path';
10
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
11
- import importCommand from './import.js';
12
- // Mock dependencies
13
- vi.mock('@/utils/user-config.js');
14
- vi.mock('@/utils/prompt.js');
15
- vi.mock('@/services/authorization-service.js');
16
- vi.mock('consola');
17
- vi.mock('@/utils/environment.js', () => ({
18
- isInteractive: () => false,
19
- }));
20
- describe('apps-import', () => {
21
- const mockUserConfig = vi.mocked(userConfig);
22
- const mockAuthorizationService = vi.mocked(authorizationService);
23
- const organizationId = 'org-123';
24
- let fixtureDirectory;
25
- let exportFile;
26
- let consoleLogSpy;
27
- beforeEach(() => {
28
- vi.clearAllMocks();
29
- mockUserConfig.read.mockReturnValue({ token: 'test-token' });
30
- mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue('test-token');
31
- mockAuthorizationService.hasAuthorizationToken.mockReturnValue(true);
32
- vi.spyOn(process, 'exit').mockImplementation((code) => {
33
- throw new Error(`Process exited with code ${code}`);
34
- });
35
- consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => undefined);
36
- vi.spyOn(console, 'table').mockImplementation(() => undefined);
37
- fixtureDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'apps-import-test-'));
38
- });
39
- afterEach(() => {
40
- nock.cleanAll();
41
- vi.restoreAllMocks();
42
- fs.rmSync(fixtureDirectory, { recursive: true, force: true });
43
- });
44
- const writeExportFile = async (apps) => {
45
- const exportDirectory = path.join(fixtureDirectory, 'export');
46
- for (const app of apps) {
47
- for (const [fileName, content] of Object.entries(app.files)) {
48
- const filePath = path.join(exportDirectory, 'apps', app.folder, fileName);
49
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
50
- if (typeof content === 'string' || Buffer.isBuffer(content)) {
51
- fs.writeFileSync(filePath, content);
52
- }
53
- else {
54
- fs.writeFileSync(filePath, JSON.stringify(content));
55
- }
56
- }
57
- }
58
- const buffer = await zip.zipFolder(exportDirectory);
59
- exportFile = path.join(fixtureDirectory, 'export.zip');
60
- fs.writeFileSync(exportFile, buffer);
61
- };
62
- const getJsonOutput = () => {
63
- const jsonCall = consoleLogSpy.mock.calls.find((call) => {
64
- try {
65
- JSON.parse(call[0]);
66
- return true;
67
- }
68
- catch {
69
- return false;
70
- }
71
- });
72
- expect(jsonCall).toBeDefined();
73
- return JSON.parse(jsonCall?.[0]);
74
- };
75
- it('should import an app with all resources', async () => {
76
- await writeExportFile([
77
- {
78
- folder: 'My App-6668c18c',
79
- files: {
80
- 'app-detail.json': { id: '6668c18c', name: 'My App', appType: 'capacitor' },
81
- 'repo-association.json': {
82
- gitProvider: 'github',
83
- cloneUrl: 'https://github.com/robingenz/appflow-export-test.git',
84
- },
85
- 'environments.json': [{ id: 1, name: 'Production', vars: { KEY: 'value' }, secrets: { API_KEY: 'secret' } }],
86
- 'live-update-channels.json': [{ id: 'channel-uuid', name: 'Production' }],
87
- 'native-configs.json': [
88
- { id: 2, name: 'Production', configs: { base: { name: 'My App', bundle_id: 'dev.example.app' } } },
89
- ],
90
- 'native-build-automations.json': [
91
- {
92
- name: 'Android Release',
93
- gitBranch: 'main',
94
- platform: 'android',
95
- buildType: 'release',
96
- environmentId: 1,
97
- webhook: null,
98
- automationEnabled: false,
99
- nativeConfigId: 2,
100
- signingCertificateId: 3,
101
- destinationId: null,
102
- },
103
- ],
104
- 'web-build-automations.json': [],
105
- 'signing-certificates/android/Debug-3/android-signing-certificate.json': {
106
- id: 3,
107
- name: 'Debug',
108
- keystoreFile: 'keystore.jks',
109
- keystorePassword: 'test1234',
110
- keyAlias: 'key',
111
- keyPassword: 'test1234',
112
- },
113
- 'signing-certificates/android/Debug-3/keystore.jks': Buffer.from('keystore'),
114
- },
115
- },
116
- ]);
117
- const appId = 'app-456';
118
- const scope = nock(DEFAULT_API_BASE_URL)
119
- .get('/v1/apps')
120
- .query({ organizationId, limit: 50, offset: 0 })
121
- .reply(200, [])
122
- .post('/v1/apps', { name: 'My App', type: 'capacitor' })
123
- .query({ organizationId })
124
- .reply(201, { id: appId, name: 'My App', type: 'capacitor' })
125
- .post(`/v1/apps/${appId}/certificates`)
126
- .reply(201, { id: 'certificate-1', name: 'Debug' })
127
- .post(`/v1/apps/${appId}/environments`, { appId, name: 'Production' })
128
- .reply(201, { id: 'environment-1', name: 'Production' })
129
- .post(`/v1/apps/${appId}/environments/environment-1/variables/set`, [{ key: 'KEY', value: 'value' }])
130
- .reply(200)
131
- .post(`/v1/apps/${appId}/environments/environment-1/secrets/set`, [{ key: 'API_KEY', value: 'secret' }])
132
- .reply(200)
133
- .get(`/v1/apps/${appId}/channels`)
134
- .reply(200, [])
135
- .post(`/v1/apps/${appId}/channels`, { appId, name: 'Production' })
136
- .reply(201, { id: 'channel-1', name: 'Production' })
137
- .post(`/v1/apps/${appId}/configurations`, {
138
- name: 'Production',
139
- displayName: 'My App',
140
- packageName: 'dev.example.app',
141
- })
142
- .reply(201, { id: 'configuration-1', name: 'Production' })
143
- .post(`/v1/apps/${appId}/automations`, (body) => {
144
- return (body.name === 'Android Release' &&
145
- body.platform === 'android' &&
146
- body.triggerType === 'branch' &&
147
- body.triggerPattern === 'main' &&
148
- body.buildType === 'release' &&
149
- body.enabled === false &&
150
- body.appCertificateName === 'Debug' &&
151
- body.appConfigurationName === 'Production' &&
152
- body.appEnvironmentName === 'Production');
153
- })
154
- .reply(201, { id: 'automation-1', name: 'Android Release' })
155
- .put(`/v1/apps/${appId}/repository`, {
156
- ownerSlug: 'robingenz',
157
- provider: 'github',
158
- repositorySlug: 'appflow-export-test',
159
- })
160
- .reply(200, { id: appId });
161
- await importCommand.action({ file: exportFile, organizationId, json: true }, undefined);
162
- expect(scope.isDone()).toBe(true);
163
- const output = getJsonOutput();
164
- expect(output.dryRun).toBe(false);
165
- expect(output.apps).toHaveLength(1);
166
- expect(output.apps[0]).toMatchObject({
167
- id: appId,
168
- name: 'My App',
169
- sourceId: '6668c18c',
170
- sourceName: 'My App',
171
- created: { automations: 1, certificates: 1, channels: 1, configurations: 1, destinations: 0, environments: 1 },
172
- errors: [],
173
- });
174
- expect(output.apps[0].webUrl).toContain(appId);
175
- });
176
- it('should rename the app if the name is already taken', async () => {
177
- await writeExportFile([
178
- {
179
- folder: 'My App-6668c18c',
180
- files: { 'app-detail.json': { id: '6668c18c', name: 'My App', appType: 'capacitor' } },
181
- },
182
- ]);
183
- const scope = nock(DEFAULT_API_BASE_URL)
184
- .get('/v1/apps')
185
- .query({ organizationId, limit: 50, offset: 0 })
186
- .reply(200, [{ id: 'app-1', name: 'My App', type: 'capacitor' }])
187
- .post('/v1/apps', { name: 'My App (2)', type: 'capacitor' })
188
- .query({ organizationId })
189
- .reply(201, { id: 'app-789', name: 'My App (2)', type: 'capacitor' })
190
- .get('/v1/apps/app-789/channels')
191
- .reply(200, []);
192
- await importCommand.action({ file: exportFile, organizationId, json: true }, undefined);
193
- expect(scope.isDone()).toBe(true);
194
- const output = getJsonOutput();
195
- expect(output.apps[0].name).toBe('My App (2)');
196
- expect(output.apps[0].notes).toContainEqual(expect.stringContaining('`My App (2)`'));
197
- });
198
- it('should continue with the remaining resources if one fails', async () => {
199
- await writeExportFile([
200
- {
201
- folder: 'My App-6668c18c',
202
- files: {
203
- 'app-detail.json': { id: '6668c18c', name: 'My App', appType: 'capacitor' },
204
- 'environments.json': [{ id: 1, name: 'Production', vars: { KEY: 'value' }, secrets: null }],
205
- 'live-update-channels.json': [{ id: 'channel-uuid', name: 'Production' }],
206
- },
207
- },
208
- ]);
209
- const appId = 'app-456';
210
- const scope = nock(DEFAULT_API_BASE_URL)
211
- .get('/v1/apps')
212
- .query({ organizationId, limit: 50, offset: 0 })
213
- .reply(200, [])
214
- .post('/v1/apps', { name: 'My App', type: 'capacitor' })
215
- .query({ organizationId })
216
- .reply(201, { id: appId, name: 'My App', type: 'capacitor' })
217
- .post(`/v1/apps/${appId}/environments`, { appId, name: 'Production' })
218
- .reply(400, { message: 'Bad Request' })
219
- .get(`/v1/apps/${appId}/channels`)
220
- .reply(200, [])
221
- .post(`/v1/apps/${appId}/channels`, { appId, name: 'Production' })
222
- .reply(201, { id: 'channel-1', name: 'Production' });
223
- await expect(importCommand.action({ file: exportFile, organizationId, json: true }, undefined)).rejects.toThrow('Process exited with code 1');
224
- expect(scope.isDone()).toBe(true);
225
- const output = getJsonOutput();
226
- expect(output.apps[0].created).toMatchObject({ channels: 1, environments: 0 });
227
- expect(output.apps[0].errors).toContainEqual(expect.stringContaining('Production'));
228
- });
229
- it('should not create any resources with the dry run option', async () => {
230
- await writeExportFile([
231
- {
232
- folder: 'My App-6668c18c',
233
- files: {
234
- 'app-detail.json': { id: '6668c18c', name: 'My App', appType: 'capacitor' },
235
- 'environments.json': [{ id: 1, name: 'Production', vars: { KEY: 'value' }, secrets: null }],
236
- },
237
- },
238
- ]);
239
- const scope = nock(DEFAULT_API_BASE_URL)
240
- .get('/v1/apps')
241
- .query({ organizationId, limit: 50, offset: 0 })
242
- .reply(200, []);
243
- await importCommand.action({ file: exportFile, organizationId, dryRun: true, json: true }, undefined);
244
- expect(scope.isDone()).toBe(true);
245
- const output = getJsonOutput();
246
- expect(output.dryRun).toBe(true);
247
- expect(output.apps[0].id).toBeNull();
248
- expect(output.apps[0].webUrl).toBeNull();
249
- });
250
- it('should only import the apps matching the include filters', async () => {
251
- await writeExportFile([
252
- {
253
- folder: 'App One-11111111',
254
- files: { 'app-detail.json': { id: '11111111', name: 'App One', appType: 'capacitor' } },
255
- },
256
- {
257
- folder: 'App Two-22222222',
258
- files: { 'app-detail.json': { id: '22222222', name: 'App Two', appType: 'capacitor' } },
259
- },
260
- {
261
- folder: 'App Three-33333333',
262
- files: { 'app-detail.json': { id: '33333333', name: 'App Three', appType: 'capacitor' } },
263
- },
264
- ]);
265
- const scope = nock(DEFAULT_API_BASE_URL)
266
- .get('/v1/apps')
267
- .query({ organizationId, limit: 50, offset: 0 })
268
- .reply(200, [])
269
- .post('/v1/apps', { name: 'App One', type: 'capacitor' })
270
- .query({ organizationId })
271
- .reply(201, { id: 'app-1', name: 'App One', type: 'capacitor' })
272
- .get('/v1/apps/app-1/channels')
273
- .reply(200, [])
274
- .post('/v1/apps', { name: 'App Three', type: 'capacitor' })
275
- .query({ organizationId })
276
- .reply(201, { id: 'app-3', name: 'App Three', type: 'capacitor' })
277
- .get('/v1/apps/app-3/channels')
278
- .reply(200, []);
279
- await importCommand.action({ file: exportFile, organizationId, include: ['App One,33333333'], json: true }, undefined);
280
- expect(scope.isDone()).toBe(true);
281
- const output = getJsonOutput();
282
- expect(output.apps).toHaveLength(2);
283
- expect(output.apps.map((app) => app.sourceName)).toEqual(['App One', 'App Three']);
284
- });
285
- it('should report skipped apps with an unsupported app type', async () => {
286
- await writeExportFile([
287
- {
288
- folder: 'RN App-22222222',
289
- files: { 'app-detail.json': { id: '22222222', name: 'RN App', appType: 'react_native' } },
290
- },
291
- ]);
292
- const scope = nock(DEFAULT_API_BASE_URL)
293
- .get('/v1/apps')
294
- .query({ organizationId, limit: 50, offset: 0 })
295
- .reply(200, []);
296
- await importCommand.action({ file: exportFile, organizationId, json: true }, undefined);
297
- expect(scope.isDone()).toBe(true);
298
- const output = getJsonOutput();
299
- expect(output.apps).toEqual([]);
300
- expect(output.skippedApps).toEqual([
301
- { sourceId: '22222222', sourceName: 'RN App', reason: expect.stringContaining('react_native') },
302
- ]);
303
- });
304
- it('should error in non-interactive environment if no file is provided', async () => {
305
- await expect(importCommand.action({ organizationId }, undefined)).rejects.toThrow('Process exited with code 1');
306
- expect(vi.mocked(consola).error).toHaveBeenCalledWith('You must provide the export file path when running in non-interactive environment.');
307
- });
308
- });
@@ -1,10 +0,0 @@
1
- export const generateUniqueAppName = (name, takenNames) => {
2
- if (!takenNames.has(name)) {
3
- return name;
4
- }
5
- let counter = 2;
6
- while (takenNames.has(`${name} (${counter})`)) {
7
- counter++;
8
- }
9
- return `${name} (${counter})`;
10
- };