@capawesome/cli 4.23.1 → 4.25.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
4
4
 
5
+ ## [4.25.0](https://github.com/capawesome-team/cli/compare/v4.24.0...v4.25.0) (2026-10-04)
6
+
7
+
8
+ ### Features
9
+
10
+ * **apps:** deploy automation builds to multiple channels ([#239](https://github.com/capawesome-team/cli/issues/239)) ([4904d60](https://github.com/capawesome-team/cli/commit/4904d607ec40f976bc002d9c4a44ed8374baec71))
11
+
12
+ ## [4.24.0](https://github.com/capawesome-team/cli/compare/v4.23.1...v4.24.0) (2026-10-01)
13
+
14
+
15
+ ### Features
16
+
17
+ * **apps:** add the `apps:update` command ([#237](https://github.com/capawesome-team/cli/issues/237)) ([274cd09](https://github.com/capawesome-team/cli/commit/274cd09ce2898aa309d2a6613d059e8fa999e5d9))
18
+
5
19
  ## [4.23.1](https://github.com/capawesome-team/cli/compare/v4.23.0...v4.23.1) (2026-10-01)
6
20
 
7
21
 
@@ -1,6 +1,7 @@
1
1
  import appAutomationsService from '../../../services/app-automations.js';
2
2
  import appsService from '../../../services/apps.js';
3
3
  import { withAuth } from '../../../utils/auth.js';
4
+ import { parseListOption } from '../../../utils/cli-options.js';
4
5
  import { isInteractive } from '../../../utils/environment.js';
5
6
  import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
6
7
  import consola from 'consola';
@@ -11,7 +12,10 @@ export default defineCommand({
11
12
  options: defineOptions(z.object({
12
13
  appId: z.string().optional().describe('ID of the app.'),
13
14
  certificate: z.string().optional().describe('The name of the certificate to use for the build.'),
14
- channel: z.string().optional().describe('The name of the channel to deploy to (Web only).'),
15
+ channel: z
16
+ .array(z.string())
17
+ .optional()
18
+ .describe('The name of a channel to deploy to (Web only). Can be specified multiple times or comma-separated.'),
15
19
  commitMessagePattern: z
16
20
  .string()
17
21
  .optional()
@@ -117,7 +121,7 @@ export default defineCommand({
117
121
  }
118
122
  const automation = await appAutomationsService.create({
119
123
  appCertificateName: options.certificate,
120
- appChannelName: options.channel,
124
+ appChannelNames: parseListOption(options.channel),
121
125
  appConfigurationName: options.configuration,
122
126
  appDestinationName: options.destination,
123
127
  appEnvironmentName: options.environment,
@@ -68,6 +68,52 @@ describe('apps-automations-create', () => {
68
68
  expect(mockConsola.info).toHaveBeenCalledWith(`Automation ID: ${automationId}`);
69
69
  expect(mockConsola.success).toHaveBeenCalledWith('Automation created successfully.');
70
70
  });
71
+ it('should send the channels as appChannelNames', async () => {
72
+ const appId = 'app-123';
73
+ const automationId = 'automation-456';
74
+ const options = {
75
+ appId,
76
+ channel: ['beta', 'alpha'],
77
+ name: 'nightly',
78
+ platform: 'web',
79
+ triggerType: 'branch',
80
+ };
81
+ const scope = nock(DEFAULT_API_BASE_URL)
82
+ .post(`/v1/apps/${appId}/automations`, {
83
+ appChannelNames: ['beta', 'alpha'],
84
+ name: 'nightly',
85
+ platform: 'web',
86
+ triggerType: 'branch',
87
+ })
88
+ .matchHeader('Authorization', 'Bearer test-token')
89
+ .reply(201, { id: automationId, name: 'nightly' });
90
+ await createAutomationCommand.action(options, undefined);
91
+ expect(scope.isDone()).toBe(true);
92
+ expect(mockConsola.success).toHaveBeenCalledWith('Automation created successfully.');
93
+ });
94
+ it('should split comma-separated channels into separate appChannelNames', async () => {
95
+ const appId = 'app-123';
96
+ const automationId = 'automation-456';
97
+ const options = {
98
+ appId,
99
+ channel: ['beta, alpha'],
100
+ name: 'nightly',
101
+ platform: 'web',
102
+ triggerType: 'branch',
103
+ };
104
+ const scope = nock(DEFAULT_API_BASE_URL)
105
+ .post(`/v1/apps/${appId}/automations`, {
106
+ appChannelNames: ['beta', 'alpha'],
107
+ name: 'nightly',
108
+ platform: 'web',
109
+ triggerType: 'branch',
110
+ })
111
+ .matchHeader('Authorization', 'Bearer test-token')
112
+ .reply(201, { id: automationId, name: 'nightly' });
113
+ await createAutomationCommand.action(options, undefined);
114
+ expect(scope.isDone()).toBe(true);
115
+ expect(mockConsola.success).toHaveBeenCalledWith('Automation created successfully.');
116
+ });
71
117
  it('should output JSON when json flag is set', async () => {
72
118
  const appId = 'app-123';
73
119
  const automationId = 'automation-456';
@@ -1,6 +1,6 @@
1
1
  import appAutomationsService from '../../../services/app-automations.js';
2
2
  import { withAuth } from '../../../utils/auth.js';
3
- import { clearableValue } from '../../../utils/cli-options.js';
3
+ import { clearableValue, parseListOption } from '../../../utils/cli-options.js';
4
4
  import { isInteractive } from '../../../utils/environment.js';
5
5
  import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
6
6
  import consola from 'consola';
@@ -17,9 +17,9 @@ export default defineCommand({
17
17
  .optional()
18
18
  .describe('The name of the certificate to use for the build. Pass an empty string to clear it.'),
19
19
  channel: z
20
- .string()
20
+ .array(z.string())
21
21
  .optional()
22
- .describe('The name of the channel to deploy to (Web only). Pass an empty string to clear it.'),
22
+ .describe('The name of a channel to deploy to (Web only). Can be specified multiple times or comma-separated. Pass an empty string to clear them.'),
23
23
  commitMessagePattern: z
24
24
  .string()
25
25
  .optional()
@@ -45,11 +45,11 @@ export default defineCommand({
45
45
  .optional()
46
46
  .describe('The platform for the build. Supported values are `android`, `ios`, and `web`.'),
47
47
  stack: z
48
- .enum(['macos-sequoia', 'macos-tahoe', 'macos-golden-gate'], {
48
+ .union([z.enum(['macos-sequoia', 'macos-tahoe', 'macos-golden-gate']), z.literal('')], {
49
49
  message: 'Build stack must be one of `macos-sequoia`, `macos-tahoe`, or `macos-golden-gate`.',
50
50
  })
51
51
  .optional()
52
- .describe('The build stack to use for the build process.'),
52
+ .describe("The build stack to use for the build process. Pass an empty string to use the app's default build stack."),
53
53
  triggerPattern: z
54
54
  .array(z.string())
55
55
  .optional()
@@ -95,13 +95,13 @@ export default defineCommand({
95
95
  }
96
96
  await appAutomationsService.update({
97
97
  appCertificateName: clearableValue(options.certificate),
98
- appChannelName: clearableValue(options.channel),
98
+ appChannelNames: parseListOption(options.channel),
99
99
  appConfigurationName: clearableValue(options.configuration),
100
100
  appDestinationName: clearableValue(options.destination),
101
101
  appEnvironmentName: clearableValue(options.environment),
102
102
  appId,
103
103
  automationId,
104
- buildStack: options.stack,
104
+ buildStack: clearableValue(options.stack),
105
105
  buildType: options.type,
106
106
  commitMessagePattern: clearableValue(options.commitMessagePattern),
107
107
  name: options.name,
@@ -71,6 +71,42 @@ describe('apps-automations-update', () => {
71
71
  expect(scope.isDone()).toBe(true);
72
72
  expect(mockConsola.success).toHaveBeenCalledWith('Automation updated successfully.');
73
73
  });
74
+ it('should send the channels as appChannelNames', async () => {
75
+ const appId = 'app-123';
76
+ const automationId = 'automation-456';
77
+ const options = { appId, automationId, channel: ['beta, alpha', 'gamma'] };
78
+ const scope = nock(DEFAULT_API_BASE_URL)
79
+ .patch(`/v1/apps/${appId}/automations/${automationId}`, { appChannelNames: ['beta', 'alpha', 'gamma'] })
80
+ .matchHeader('Authorization', 'Bearer test-token')
81
+ .reply(204);
82
+ await updateAutomationCommand.action(options, undefined);
83
+ expect(scope.isDone()).toBe(true);
84
+ expect(mockConsola.success).toHaveBeenCalledWith('Automation updated successfully.');
85
+ });
86
+ it('should clear the channels when an empty string is passed', async () => {
87
+ const appId = 'app-123';
88
+ const automationId = 'automation-456';
89
+ const options = { appId, automationId, channel: [''] };
90
+ const scope = nock(DEFAULT_API_BASE_URL)
91
+ .patch(`/v1/apps/${appId}/automations/${automationId}`, { appChannelNames: [] })
92
+ .matchHeader('Authorization', 'Bearer test-token')
93
+ .reply(204);
94
+ await updateAutomationCommand.action(options, undefined);
95
+ expect(scope.isDone()).toBe(true);
96
+ expect(mockConsola.success).toHaveBeenCalledWith('Automation updated successfully.');
97
+ });
98
+ it('should clear the build stack when an empty string is passed', async () => {
99
+ const appId = 'app-123';
100
+ const automationId = 'automation-456';
101
+ const options = { appId, automationId, stack: '' };
102
+ const scope = nock(DEFAULT_API_BASE_URL)
103
+ .patch(`/v1/apps/${appId}/automations/${automationId}`, { buildStack: null })
104
+ .matchHeader('Authorization', 'Bearer test-token')
105
+ .reply(204);
106
+ await updateAutomationCommand.action(options, undefined);
107
+ expect(scope.isDone()).toBe(true);
108
+ expect(mockConsola.success).toHaveBeenCalledWith('Automation updated successfully.');
109
+ });
74
110
  it('should output JSON when json flag is set', async () => {
75
111
  const appId = 'app-123';
76
112
  const automationId = 'automation-456';
@@ -415,7 +415,7 @@ const importApp = async (organizationId, outcome, gitConnectionIds) => {
415
415
  buildType: automation.buildType,
416
416
  enabled: automation.enabled,
417
417
  appCertificateName: automation.appCertificateName,
418
- appChannelName: automation.appChannelName,
418
+ appChannelNames: automation.appChannelNames,
419
419
  appConfigurationName: automation.appConfigurationName,
420
420
  appDestinationName: automation.appDestinationName,
421
421
  appEnvironmentName: automation.appEnvironmentName,
@@ -101,7 +101,18 @@ describe('apps-import', () => {
101
101
  destinationId: null,
102
102
  },
103
103
  ],
104
- 'web-build-automations.json': [],
104
+ 'web-build-automations.json': [
105
+ {
106
+ name: 'Web Prod',
107
+ gitBranch: 'main',
108
+ platform: 'web-deploy',
109
+ environmentId: null,
110
+ webhook: null,
111
+ automationEnabled: true,
112
+ channelIds: ['channel-uuid'],
113
+ webPreviewEnabled: false,
114
+ },
115
+ ],
105
116
  'signing-certificates/android/Debug-3/android-signing-certificate.json': {
106
117
  id: 3,
107
118
  name: 'Debug',
@@ -152,6 +163,12 @@ describe('apps-import', () => {
152
163
  body.appEnvironmentName === 'Production');
153
164
  })
154
165
  .reply(201, { id: 'automation-1', name: 'Android Release' })
166
+ .post(`/v1/apps/${appId}/automations`, (body) => {
167
+ return (body.name === 'Web Prod' &&
168
+ body.platform === 'web' &&
169
+ JSON.stringify(body.appChannelNames) === JSON.stringify(['Production']));
170
+ })
171
+ .reply(201, { id: 'automation-2', name: 'Web Prod' })
155
172
  .get(`/v1/organizations/${organizationId}/git-connections`)
156
173
  .query({ provider: 'github', restricted: 'false', limit: 1 })
157
174
  .reply(200, [{ id: 'git-connection-1', provider: 'github' }])
@@ -170,7 +187,7 @@ describe('apps-import', () => {
170
187
  name: 'My App',
171
188
  sourceId: '6668c18c',
172
189
  sourceName: 'My App',
173
- created: { automations: 1, certificates: 1, channels: 1, configurations: 1, destinations: 0, environments: 1 },
190
+ created: { automations: 2, certificates: 1, channels: 1, configurations: 1, destinations: 0, environments: 1 },
174
191
  errors: [],
175
192
  });
176
193
  expect(output.apps[0].webUrl).toContain(appId);
@@ -0,0 +1,79 @@
1
+ import appsService from '../../services/apps.js';
2
+ import { withAuth } from '../../utils/auth.js';
3
+ import { clearableValue } from '../../utils/cli-options.js';
4
+ import { isInteractive } from '../../utils/environment.js';
5
+ import { promptAppSelection, promptOrganizationSelection } from '../../utils/prompt.js';
6
+ import consola from 'consola';
7
+ import { z } from 'zod';
8
+ import { defineCommand, defineOptions } from 'zodline';
9
+ export default defineCommand({
10
+ description: 'Update an existing app.',
11
+ options: defineOptions(z.object({
12
+ appId: z.string().optional().describe('ID of the app.'),
13
+ channel: z
14
+ .string()
15
+ .optional()
16
+ .describe('The name of the default channel for live updates. Pass an empty string to clear it.'),
17
+ channelDiscovery: z
18
+ .union([z.boolean(), z.stringbool()], {
19
+ message: 'Channel discovery must be either `true` or `false`.',
20
+ })
21
+ .optional()
22
+ .describe('Enable or disable channel discovery. Supported values are `true` and `false`.'),
23
+ configuration: z
24
+ .string()
25
+ .optional()
26
+ .describe('The name of the default native configuration. Pass an empty string to clear it.'),
27
+ environment: z
28
+ .string()
29
+ .optional()
30
+ .describe('The name of the default environment. Pass an empty string to clear it.'),
31
+ json: z.boolean().optional().describe('Output in JSON format.'),
32
+ name: z.string().optional().describe('Name of the app.'),
33
+ nextBuildNumber: z.coerce
34
+ .number()
35
+ .int()
36
+ .optional()
37
+ .describe('The next build number. Must be greater than the latest build number.'),
38
+ stack: z
39
+ .union([z.enum(['macos-sequoia', 'macos-tahoe', 'macos-golden-gate']), z.literal('')], {
40
+ message: 'Build stack must be one of `macos-sequoia`, `macos-tahoe`, or `macos-golden-gate`.',
41
+ })
42
+ .optional()
43
+ .describe('The default build stack for builds without an explicit build stack. Must be one of `macos-sequoia`, `macos-tahoe`, or `macos-golden-gate`. Pass an empty string to use the Capawesome Cloud default.'),
44
+ type: z
45
+ .enum(['android', 'capacitor', 'cordova', 'ios'], {
46
+ message: 'Type must be one of `android`, `capacitor`, `cordova`, or `ios`.',
47
+ })
48
+ .optional()
49
+ .describe('Type of the app. Supported values are `android`, `capacitor`, `cordova`, and `ios`.'),
50
+ })),
51
+ action: withAuth(async (options, args) => {
52
+ let { appId, json } = options;
53
+ if (!appId) {
54
+ if (!isInteractive()) {
55
+ consola.error('You must provide an app ID when running in non-interactive environment.');
56
+ process.exit(1);
57
+ }
58
+ const organizationId = await promptOrganizationSelection();
59
+ appId = await promptAppSelection(organizationId);
60
+ }
61
+ const app = await appsService.update({
62
+ appChannelDiscoveryEnabled: options.channelDiscovery,
63
+ appChannelName: clearableValue(options.channel),
64
+ appConfigurationName: clearableValue(options.configuration),
65
+ appEnvironmentName: clearableValue(options.environment),
66
+ appId,
67
+ buildStack: clearableValue(options.stack),
68
+ name: options.name,
69
+ nextAppBuildNumber: options.nextBuildNumber,
70
+ type: options.type,
71
+ });
72
+ if (json) {
73
+ console.log(JSON.stringify(app, null, 2));
74
+ }
75
+ else {
76
+ consola.success('App updated successfully.');
77
+ }
78
+ }),
79
+ });
@@ -0,0 +1,117 @@
1
+ import { DEFAULT_API_BASE_URL } from '../../config/consts.js';
2
+ import authorizationService from '../../services/authorization-service.js';
3
+ import { promptAppSelection, promptOrganizationSelection } from '../../utils/prompt.js';
4
+ import userConfig from '../../utils/user-config.js';
5
+ import consola from 'consola';
6
+ import nock from 'nock';
7
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
8
+ import updateAppCommand from './update.js';
9
+ // Mock dependencies
10
+ vi.mock('@/utils/user-config.js');
11
+ vi.mock('@/utils/prompt.js');
12
+ vi.mock('@/services/authorization-service.js');
13
+ vi.mock('consola');
14
+ vi.mock('@/utils/environment.js', () => ({
15
+ isInteractive: () => true,
16
+ }));
17
+ describe('apps-update', () => {
18
+ const mockUserConfig = vi.mocked(userConfig);
19
+ const mockPromptOrganizationSelection = vi.mocked(promptOrganizationSelection);
20
+ const mockPromptAppSelection = vi.mocked(promptAppSelection);
21
+ const mockConsola = vi.mocked(consola);
22
+ const mockAuthorizationService = vi.mocked(authorizationService);
23
+ beforeEach(() => {
24
+ vi.clearAllMocks();
25
+ mockUserConfig.read.mockReturnValue({ token: 'test-token' });
26
+ mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue('test-token');
27
+ mockAuthorizationService.hasAuthorizationToken.mockReturnValue(true);
28
+ vi.spyOn(process, 'exit').mockImplementation((code) => {
29
+ throw new Error(`Process exited with code ${code}`);
30
+ });
31
+ });
32
+ afterEach(() => {
33
+ nock.cleanAll();
34
+ vi.restoreAllMocks();
35
+ });
36
+ it('should update app with provided options', async () => {
37
+ const appId = 'app-123';
38
+ const options = updateAppCommand.options.schema.parse({
39
+ appId,
40
+ channelDiscovery: 'false',
41
+ configuration: 'Production',
42
+ name: 'renamed',
43
+ nextBuildNumber: '42',
44
+ stack: 'macos-tahoe',
45
+ type: 'ios',
46
+ });
47
+ const scope = nock(DEFAULT_API_BASE_URL)
48
+ .patch(`/v1/apps/${appId}`, {
49
+ appChannelDiscoveryEnabled: false,
50
+ appConfigurationName: 'Production',
51
+ buildStack: 'macos-tahoe',
52
+ name: 'renamed',
53
+ nextAppBuildNumber: 42,
54
+ type: 'ios',
55
+ })
56
+ .matchHeader('Authorization', 'Bearer test-token')
57
+ .reply(200, { id: appId, name: 'renamed' });
58
+ await updateAppCommand.action(options, undefined);
59
+ expect(scope.isDone()).toBe(true);
60
+ expect(mockConsola.success).toHaveBeenCalledWith('App updated successfully.');
61
+ });
62
+ it('should clear defaults when an empty string is passed', async () => {
63
+ const appId = 'app-123';
64
+ const options = { appId, channel: '', environment: '', stack: '' };
65
+ const scope = nock(DEFAULT_API_BASE_URL)
66
+ .patch(`/v1/apps/${appId}`, {
67
+ appChannelName: null,
68
+ appEnvironmentName: null,
69
+ buildStack: null,
70
+ })
71
+ .matchHeader('Authorization', 'Bearer test-token')
72
+ .reply(200, { id: appId });
73
+ await updateAppCommand.action(options, undefined);
74
+ expect(scope.isDone()).toBe(true);
75
+ expect(mockConsola.success).toHaveBeenCalledWith('App updated successfully.');
76
+ });
77
+ it('should output JSON when json flag is set', async () => {
78
+ const appId = 'app-123';
79
+ const app = { id: appId, name: 'renamed', organizationId: 'org-1', type: 'capacitor' };
80
+ const options = { appId, json: true, name: 'renamed' };
81
+ const logSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
82
+ const scope = nock(DEFAULT_API_BASE_URL)
83
+ .patch(`/v1/apps/${appId}`, { name: 'renamed' })
84
+ .matchHeader('Authorization', 'Bearer test-token')
85
+ .reply(200, app);
86
+ await updateAppCommand.action(options, undefined);
87
+ expect(scope.isDone()).toBe(true);
88
+ expect(logSpy).toHaveBeenCalledWith(JSON.stringify(app, null, 2));
89
+ expect(mockConsola.success).not.toHaveBeenCalled();
90
+ });
91
+ it('should prompt for app when not provided', async () => {
92
+ const orgId = 'org-1';
93
+ const appId = 'app-1';
94
+ const options = { name: 'renamed' };
95
+ const scope = nock(DEFAULT_API_BASE_URL)
96
+ .patch(`/v1/apps/${appId}`, { name: 'renamed' })
97
+ .matchHeader('Authorization', 'Bearer test-token')
98
+ .reply(200, { id: appId, name: 'renamed' });
99
+ mockPromptOrganizationSelection.mockResolvedValueOnce(orgId);
100
+ mockPromptAppSelection.mockResolvedValueOnce(appId);
101
+ await updateAppCommand.action(options, undefined);
102
+ expect(scope.isDone()).toBe(true);
103
+ expect(mockPromptAppSelection).toHaveBeenCalledWith(orgId);
104
+ expect(mockConsola.success).toHaveBeenCalledWith('App updated successfully.');
105
+ });
106
+ it('should handle API error', async () => {
107
+ const appId = 'app-123';
108
+ const options = { appId, nextBuildNumber: 1 };
109
+ const scope = nock(DEFAULT_API_BASE_URL)
110
+ .patch(`/v1/apps/${appId}`)
111
+ .matchHeader('Authorization', 'Bearer test-token')
112
+ .reply(400, { message: 'The next build number must be greater than the latest build number.' });
113
+ await expect(updateAppCommand.action(options, undefined)).rejects.toThrow();
114
+ expect(scope.isDone()).toBe(true);
115
+ expect(mockConsola.success).not.toHaveBeenCalled();
116
+ });
117
+ });
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ const config = defineConfig({
32
32
  'apps:list': await import('./commands/apps/list.js').then((mod) => mod.default),
33
33
  'apps:transfer': await import('./commands/apps/transfer.js').then((mod) => mod.default),
34
34
  'apps:unlink': await import('./commands/apps/unlink.js').then((mod) => mod.default),
35
+ 'apps:update': await import('./commands/apps/update.js').then((mod) => mod.default),
35
36
  'apps:automations:create': await import('./commands/apps/automations/create.js').then((mod) => mod.default),
36
37
  'apps:automations:delete': await import('./commands/apps/automations/delete.js').then((mod) => mod.default),
37
38
  'apps:automations:get': await import('./commands/apps/automations/get.js').then((mod) => mod.default),
@@ -372,26 +372,14 @@ const parseAutomations = (appFolder, references, notes) => {
372
372
  .map((channelId) => resolveName(references.channelNamesById, channelId, automation.name, 'channel', notes))
373
373
  .filter((name) => !!name);
374
374
  const appEnvironmentName = resolveName(references.environmentNamesById, automation.environmentId, automation.name, 'environment', notes);
375
- if (channelNames.length === 0) {
376
- automations.push({
377
- name: automation.name,
378
- platform: 'web',
379
- triggerPattern: automation.gitBranch,
380
- enabled: automation.automationEnabled,
381
- appEnvironmentName,
382
- });
383
- continue;
384
- }
385
- for (const channelName of channelNames) {
386
- automations.push({
387
- name: channelNames.length > 1 ? `${automation.name} (${channelName})` : automation.name,
388
- platform: 'web',
389
- triggerPattern: automation.gitBranch,
390
- enabled: automation.automationEnabled,
391
- appChannelName: channelName,
392
- appEnvironmentName,
393
- });
394
- }
375
+ automations.push({
376
+ name: automation.name,
377
+ platform: 'web',
378
+ triggerPattern: automation.gitBranch,
379
+ enabled: automation.automationEnabled,
380
+ appChannelNames: channelNames,
381
+ appEnvironmentName,
382
+ });
395
383
  }
396
384
  return automations;
397
385
  };
@@ -190,19 +190,11 @@ describe('appflow-export', () => {
190
190
  appEnvironmentName: undefined,
191
191
  },
192
192
  {
193
- name: 'Web Prod (Staging)',
193
+ name: 'Web Prod',
194
194
  platform: 'web',
195
195
  triggerPattern: 'main',
196
196
  enabled: true,
197
- appChannelName: 'Staging',
198
- appEnvironmentName: 'Production',
199
- },
200
- {
201
- name: 'Web Prod (Production)',
202
- platform: 'web',
203
- triggerPattern: 'main',
204
- enabled: true,
205
- appChannelName: 'Production',
197
+ appChannelNames: ['Staging', 'Production'],
206
198
  appEnvironmentName: 'Production',
207
199
  },
208
200
  ]);
@@ -1,4 +1,4 @@
1
- export const clearableValue = (value) => (value === '' ? null : value);
1
+ export const clearableValue = (value) => value === '' ? null : value;
2
2
  export const parseListOption = (values) => {
3
3
  return values
4
4
  ?.flatMap((value) => value.split(','))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capawesome/cli",
3
- "version": "4.23.1",
3
+ "version": "4.25.0",
4
4
  "description": "The Capawesome Cloud Command Line Interface (CLI) to manage Live Updates and more.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -59,7 +59,7 @@
59
59
  "@napi-rs/keyring": "1.3.0",
60
60
  "@sentry/node": "10.58.0",
61
61
  "adm-zip": "0.6.1",
62
- "axios": "1.18.1",
62
+ "axios": "1.20.0",
63
63
  "axios-retry": "4.5.0",
64
64
  "c12": "3.3.3",
65
65
  "consola": "3.3.0",
@@ -73,7 +73,7 @@
73
73
  "semver": "7.6.3",
74
74
  "systeminformation": "5.31.17",
75
75
  "zod": "4.0.17",
76
- "zodline": "0.3.0"
76
+ "zodline": "0.3.2"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@ionic/prettier-config": "4.0.0",