@capawesome/cli 4.1.0-dev.f5ef8ba.1771047437 → 4.2.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/commands/apps/builds/cancel.js +6 -41
  3. package/dist/commands/apps/builds/create.js +10 -55
  4. package/dist/commands/apps/builds/download.js +6 -41
  5. package/dist/commands/apps/builds/logs.js +6 -41
  6. package/dist/commands/apps/channels/create.js +6 -36
  7. package/dist/commands/apps/channels/create.test.js +5 -15
  8. package/dist/commands/apps/channels/delete.js +6 -36
  9. package/dist/commands/apps/channels/delete.test.js +6 -18
  10. package/dist/commands/apps/channels/get.js +3 -7
  11. package/dist/commands/apps/channels/list.js +6 -36
  12. package/dist/commands/apps/channels/list.test.js +4 -1
  13. package/dist/commands/apps/channels/pause.js +6 -40
  14. package/dist/commands/apps/channels/resume.js +6 -40
  15. package/dist/commands/apps/channels/update.js +6 -36
  16. package/dist/commands/apps/channels/update.test.js +14 -31
  17. package/dist/commands/apps/create.js +5 -23
  18. package/dist/commands/apps/create.test.js +13 -31
  19. package/dist/commands/apps/delete.js +6 -35
  20. package/dist/commands/apps/delete.test.js +11 -35
  21. package/dist/commands/apps/deployments/cancel.js +6 -41
  22. package/dist/commands/apps/deployments/create.js +6 -41
  23. package/dist/commands/apps/deployments/logs.js +6 -41
  24. package/dist/commands/apps/devices/delete.js +6 -36
  25. package/dist/commands/apps/devices/delete.test.js +14 -36
  26. package/dist/commands/apps/environments/create.js +6 -36
  27. package/dist/commands/apps/environments/delete.js +6 -36
  28. package/dist/commands/apps/environments/list.js +6 -36
  29. package/dist/commands/apps/environments/set.js +6 -36
  30. package/dist/commands/apps/environments/unset.js +6 -36
  31. package/dist/commands/apps/liveupdates/register.js +6 -40
  32. package/dist/commands/apps/liveupdates/register.test.js +2 -1
  33. package/dist/commands/apps/liveupdates/rollback.js +6 -41
  34. package/dist/commands/apps/liveupdates/rollout.js +6 -41
  35. package/dist/commands/apps/liveupdates/set-native-versions.js +3 -7
  36. package/dist/commands/apps/liveupdates/upload.js +6 -40
  37. package/dist/commands/apps/liveupdates/upload.test.js +2 -1
  38. package/dist/commands/organizations/create.js +3 -7
  39. package/dist/commands/organizations/create.test.js +3 -1
  40. package/dist/utils/auth.js +27 -0
  41. package/dist/utils/prompt.js +56 -11
  42. package/dist/utils/signature.js +2 -1
  43. package/package.json +6 -2
@@ -1,10 +1,8 @@
1
1
  import appEnvironmentsService from '../../../services/app-environments.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
5
2
  import { parseKeyValuePairs } from '../../../utils/app-environments.js';
3
+ import { withAuth } from '../../../utils/auth.js';
6
4
  import { isInteractive } from '../../../utils/environment.js';
7
- import { prompt } from '../../../utils/prompt.js';
5
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
8
6
  import { defineCommand, defineOptions } from '@robingenz/zli';
9
7
  import consola from 'consola';
10
8
  import fs from 'fs';
@@ -25,43 +23,15 @@ export default defineCommand({
25
23
  .describe('Environment secret in key=value format. Can be specified multiple times.'),
26
24
  secretFile: z.string().optional().describe('Path to a file containing environment secrets in .env format.'),
27
25
  })),
28
- action: async (options, args) => {
26
+ action: withAuth(async (options, args) => {
29
27
  let { appId, environmentId, variable, variableFile, secret, secretFile } = options;
30
- if (!authorizationService.hasAuthorizationToken()) {
31
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
32
- process.exit(1);
33
- }
34
28
  if (!appId) {
35
29
  if (!isInteractive()) {
36
30
  consola.error('You must provide an app ID when running in non-interactive environment.');
37
31
  process.exit(1);
38
32
  }
39
- const organizations = await organizationsService.findAll();
40
- if (organizations.length === 0) {
41
- consola.error('You must create an organization before setting environment variables or secrets.');
42
- process.exit(1);
43
- }
44
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
45
- const organizationId = await prompt('Select the organization of the app for which you want to set environment variables or secrets.', {
46
- type: 'select',
47
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
48
- });
49
- if (!organizationId) {
50
- consola.error('You must select the organization of an app for which you want to set environment variables or secrets.');
51
- process.exit(1);
52
- }
53
- const apps = await appsService.findAll({
54
- organizationId,
55
- });
56
- if (!apps.length) {
57
- consola.error('You must create an app before setting environment variables or secrets.');
58
- process.exit(1);
59
- }
60
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
61
- appId = await prompt('Which app do you want to set the environment variables or secrets for?', {
62
- type: 'select',
63
- options: apps.map((app) => ({ label: app.name, value: app.id })),
64
- });
33
+ const organizationId = await promptOrganizationSelection();
34
+ appId = await promptAppSelection(organizationId);
65
35
  }
66
36
  if (!environmentId) {
67
37
  if (!isInteractive()) {
@@ -122,5 +92,5 @@ export default defineCommand({
122
92
  });
123
93
  }
124
94
  consola.success('Environment variables and secrets set successfully.');
125
- },
95
+ }),
126
96
  });
@@ -1,9 +1,7 @@
1
1
  import appEnvironmentsService from '../../../services/app-environments.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
2
+ import { withAuth } from '../../../utils/auth.js';
5
3
  import { isInteractive } from '../../../utils/environment.js';
6
- import { prompt } from '../../../utils/prompt.js';
4
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
7
5
  import { defineCommand, defineOptions } from '@robingenz/zli';
8
6
  import consola from 'consola';
9
7
  import { z } from 'zod';
@@ -21,43 +19,15 @@ export default defineCommand({
21
19
  .optional()
22
20
  .describe('Key of the environment secret to unset. Can be specified multiple times.'),
23
21
  })),
24
- action: async (options, args) => {
22
+ action: withAuth(async (options, args) => {
25
23
  let { appId, environmentId, variable: variableKeys, secret: secretKeys } = options;
26
- if (!authorizationService.hasAuthorizationToken()) {
27
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
28
- process.exit(1);
29
- }
30
24
  if (!appId) {
31
25
  if (!isInteractive()) {
32
26
  consola.error('You must provide an app ID when running in non-interactive environment.');
33
27
  process.exit(1);
34
28
  }
35
- const organizations = await organizationsService.findAll();
36
- if (organizations.length === 0) {
37
- consola.error('You must create an organization before unsetting environment variables or secrets.');
38
- process.exit(1);
39
- }
40
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
41
- const organizationId = await prompt('Select the organization of the app for which you want to unset environment variables or secrets.', {
42
- type: 'select',
43
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
44
- });
45
- if (!organizationId) {
46
- consola.error('You must select the organization of an app for which you want to unset environment variables or secrets.');
47
- process.exit(1);
48
- }
49
- const apps = await appsService.findAll({
50
- organizationId,
51
- });
52
- if (!apps.length) {
53
- consola.error('You must create an app before unsetting environment variables or secrets.');
54
- process.exit(1);
55
- }
56
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
57
- appId = await prompt('Which app do you want to unset the environment variables or secrets for?', {
58
- type: 'select',
59
- options: apps.map((app) => ({ label: app.name, value: app.id })),
60
- });
29
+ const organizationId = await promptOrganizationSelection();
30
+ appId = await promptAppSelection(organizationId);
61
31
  }
62
32
  if (!environmentId) {
63
33
  if (!isInteractive()) {
@@ -94,5 +64,5 @@ export default defineCommand({
94
64
  });
95
65
  }
96
66
  consola.success('Environment variables and secrets unset successfully.');
97
- },
67
+ }),
98
68
  });
@@ -1,14 +1,13 @@
1
1
  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
- import authorizationService from '../../../services/authorization-service.js';
5
- import organizationsService from '../../../services/organizations.js';
4
+ import { withAuth } from '../../../utils/auth.js';
6
5
  import { createBufferFromPath, createBufferFromString, isPrivateKeyContent } from '../../../utils/buffer.js';
7
6
  import { isInteractive } from '../../../utils/environment.js';
8
7
  import { fileExistsAtPath } from '../../../utils/file.js';
9
8
  import { createHash } from '../../../utils/hash.js';
10
9
  import { formatPrivateKey } from '../../../utils/private-key.js';
11
- import { prompt } from '../../../utils/prompt.js';
10
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
12
11
  import { createSignature } from '../../../utils/signature.js';
13
12
  import zip from '../../../utils/zip.js';
14
13
  import { defineCommand, defineOptions } from '@robingenz/zli';
@@ -101,13 +100,8 @@ export default defineCommand({
101
100
  url: z.string().optional().describe('The url to the self-hosted bundle file.'),
102
101
  yes: z.boolean().optional().describe('Skip confirmation prompts.'),
103
102
  }), { y: 'yes' }),
104
- action: async (options, args) => {
103
+ action: withAuth(async (options, args) => {
105
104
  let { androidEq, androidMax, androidMin, appId, channel, commitMessage, commitRef, commitSha, customProperty, expiresInDays, gitRef, iosEq, iosMax, iosMin, path, privateKey, rolloutPercentage, url, } = options;
106
- // Check if the user is logged in
107
- if (!authorizationService.hasAuthorizationToken()) {
108
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
109
- process.exit(1);
110
- }
111
105
  // Calculate the expiration date
112
106
  let expiresAt;
113
107
  if (expiresInDays) {
@@ -137,36 +131,8 @@ export default defineCommand({
137
131
  consola.error('You must provide an app ID when running in non-interactive environment.');
138
132
  process.exit(1);
139
133
  }
140
- const organizations = await organizationsService.findAll();
141
- if (organizations.length === 0) {
142
- consola.error('You must create an organization before registering a bundle.');
143
- process.exit(1);
144
- }
145
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
146
- const organizationId = await prompt('Select the organization of the app for which you want to register a bundle.', {
147
- type: 'select',
148
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
149
- });
150
- if (!organizationId) {
151
- consola.error('You must select the organization of an app for which you want to register a bundle.');
152
- process.exit(1);
153
- }
154
- const apps = await appsService.findAll({
155
- organizationId,
156
- });
157
- if (apps.length === 0) {
158
- consola.error('You must create an app before registering a bundle.');
159
- process.exit(1);
160
- }
161
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
162
- appId = await prompt('Which app do you want to deploy to:', {
163
- type: 'select',
164
- options: apps.map((app) => ({ label: app.name, value: app.id })),
165
- });
166
- if (!appId) {
167
- consola.error('You must select an app to deploy to.');
168
- process.exit(1);
169
- }
134
+ const organizationId = await promptOrganizationSelection({ allowCreate: true });
135
+ appId = await promptAppSelection(organizationId, { allowCreate: true });
170
136
  }
171
137
  // Prompt for channel if interactive
172
138
  if (!channel && !options.yes && isInteractive()) {
@@ -274,7 +240,7 @@ export default defineCommand({
274
240
  consola.info(`Deployment URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/deployments/${response.appDeploymentId}`);
275
241
  }
276
242
  consola.success('Live Update successfully registered.');
277
- },
243
+ }),
278
244
  });
279
245
  const parseCustomProperties = (customProperty) => {
280
246
  let customProperties;
@@ -8,6 +8,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
8
8
  import registerCommand from './register.js';
9
9
  // Mock dependencies
10
10
  vi.mock('@/utils/user-config.js');
11
+ vi.mock('@/utils/prompt.js');
11
12
  vi.mock('@/services/authorization-service.js');
12
13
  vi.mock('@/utils/file.js');
13
14
  vi.mock('@/utils/zip.js');
@@ -40,7 +41,7 @@ describe('apps-liveupdates-register', () => {
40
41
  const options = { appId, url: bundleUrl, rolloutPercentage: 1 };
41
42
  mockAuthorizationService.hasAuthorizationToken.mockReturnValue(false);
42
43
  await expect(registerCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
43
- expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command. Please run the `login` command first.');
44
+ expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command. Set the `CAPAWESOME_TOKEN` environment variable or use the `--token` option.');
44
45
  });
45
46
  it('should register bundle with self-hosted URL', async () => {
46
47
  const appId = 'app-123';
@@ -1,11 +1,9 @@
1
1
  import { DEFAULT_CONSOLE_BASE_URL } from '../../../config/consts.js';
2
2
  import appChannelsService from '../../../services/app-channels.js';
3
3
  import appDeploymentsService from '../../../services/app-deployments.js';
4
- import appsService from '../../../services/apps.js';
5
- import authorizationService from '../../../services/authorization-service.js';
6
- import organizationsService from '../../../services/organizations.js';
4
+ import { withAuth } from '../../../utils/auth.js';
7
5
  import { isInteractive } from '../../../utils/environment.js';
8
- import { prompt } from '../../../utils/prompt.js';
6
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
9
7
  import { formatTimeAgo } from '../../../utils/time-format.js';
10
8
  import { defineCommand, defineOptions } from '@robingenz/zli';
11
9
  import consola from 'consola';
@@ -34,49 +32,16 @@ export default defineCommand({
34
32
  .optional()
35
33
  .describe('Number of deployments to go back (1-5).'),
36
34
  })),
37
- action: async (options) => {
35
+ action: withAuth(async (options) => {
38
36
  let { appId, channel, steps } = options;
39
- // Check if the user is logged in
40
- if (!authorizationService.hasAuthorizationToken()) {
41
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
42
- process.exit(1);
43
- }
44
37
  // Prompt for app ID if not provided
45
38
  if (!appId) {
46
39
  if (!isInteractive()) {
47
40
  consola.error('You must provide an app ID when running in non-interactive environment.');
48
41
  process.exit(1);
49
42
  }
50
- const organizations = await organizationsService.findAll();
51
- if (organizations.length === 0) {
52
- consola.error('You must create an organization before rolling back a deployment.');
53
- process.exit(1);
54
- }
55
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
56
- const organizationId = await prompt('Select the organization of the app for which you want to rollback a deployment.', {
57
- type: 'select',
58
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
59
- });
60
- if (!organizationId) {
61
- consola.error('You must select the organization of an app for which you want to rollback a deployment.');
62
- process.exit(1);
63
- }
64
- const apps = await appsService.findAll({
65
- organizationId,
66
- });
67
- if (apps.length === 0) {
68
- consola.error('You must create an app before rolling back a deployment.');
69
- process.exit(1);
70
- }
71
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
72
- appId = await prompt('Which app do you want to rollback a deployment for:', {
73
- type: 'select',
74
- options: apps.map((app) => ({ label: app.name, value: app.id })),
75
- });
76
- if (!appId) {
77
- consola.error('You must select an app to rollback a deployment for.');
78
- process.exit(1);
79
- }
43
+ const organizationId = await promptOrganizationSelection();
44
+ appId = await promptAppSelection(organizationId);
80
45
  }
81
46
  // Prompt for channel name if not provided
82
47
  if (!channel) {
@@ -167,5 +132,5 @@ export default defineCommand({
167
132
  consola.info(`Deployment ID: ${response.id}`);
168
133
  consola.info(`Deployment URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/deployments/${response.id}`);
169
134
  consola.success(`Rolled back to Build #${selectedAppDeployment.appBuild?.numberAsString} (${selectedIndex} step${selectedIndex === 1 ? '' : 's'} back).`);
170
- },
135
+ }),
171
136
  });
@@ -1,11 +1,9 @@
1
1
  import { DEFAULT_CONSOLE_BASE_URL } from '../../../config/consts.js';
2
2
  import appChannelsService from '../../../services/app-channels.js';
3
3
  import appDeploymentsService from '../../../services/app-deployments.js';
4
- import appsService from '../../../services/apps.js';
5
- import authorizationService from '../../../services/authorization-service.js';
6
- import organizationsService from '../../../services/organizations.js';
4
+ import { withAuth } from '../../../utils/auth.js';
7
5
  import { isInteractive } from '../../../utils/environment.js';
8
- import { prompt } from '../../../utils/prompt.js';
6
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
9
7
  import { defineCommand, defineOptions } from '@robingenz/zli';
10
8
  import consola from 'consola';
11
9
  import { z } from 'zod';
@@ -33,49 +31,16 @@ export default defineCommand({
33
31
  .optional()
34
32
  .describe('Rollout percentage (0-100).'),
35
33
  })),
36
- action: async (options) => {
34
+ action: withAuth(async (options) => {
37
35
  let { appId, channel, percentage } = options;
38
- // Check if the user is logged in
39
- if (!authorizationService.hasAuthorizationToken()) {
40
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
41
- process.exit(1);
42
- }
43
36
  // Prompt for app ID if not provided
44
37
  if (!appId) {
45
38
  if (!isInteractive()) {
46
39
  consola.error('You must provide an app ID when running in non-interactive environment.');
47
40
  process.exit(1);
48
41
  }
49
- const organizations = await organizationsService.findAll();
50
- if (organizations.length === 0) {
51
- consola.error('You must create an organization before updating a rollout percentage.');
52
- process.exit(1);
53
- }
54
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
55
- const organizationId = await prompt('Select the organization of the app for which you want to update the rollout percentage.', {
56
- type: 'select',
57
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
58
- });
59
- if (!organizationId) {
60
- consola.error('You must select the organization of an app for which you want to update the rollout percentage.');
61
- process.exit(1);
62
- }
63
- const apps = await appsService.findAll({
64
- organizationId,
65
- });
66
- if (apps.length === 0) {
67
- consola.error('You must create an app before updating a rollout percentage.');
68
- process.exit(1);
69
- }
70
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
71
- appId = await prompt('Which app do you want to update the rollout percentage for:', {
72
- type: 'select',
73
- options: apps.map((app) => ({ label: app.name, value: app.id })),
74
- });
75
- if (!appId) {
76
- consola.error('You must select an app to update the rollout percentage for.');
77
- process.exit(1);
78
- }
42
+ const organizationId = await promptOrganizationSelection();
43
+ appId = await promptAppSelection(organizationId);
79
44
  }
80
45
  // Prompt for channel name if not provided
81
46
  if (!channel) {
@@ -143,5 +108,5 @@ export default defineCommand({
143
108
  consola.info(`Deployment ID: ${response.id}`);
144
109
  consola.info(`Deployment URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/deployments/${response.id}`);
145
110
  consola.success(`Rolled out to ${percentage}%.`);
146
- },
111
+ }),
147
112
  });
@@ -1,5 +1,5 @@
1
1
  import appBuildsService from '../../../services/app-builds.js';
2
- import authorizationService from '../../../services/authorization-service.js';
2
+ import { withAuth } from '../../../utils/auth.js';
3
3
  import { isInteractive } from '../../../utils/environment.js';
4
4
  import { defineCommand, defineOptions } from '@robingenz/zli';
5
5
  import consola from 'consola';
@@ -50,12 +50,8 @@ export default defineCommand({
50
50
  .optional()
51
51
  .describe('The minimum iOS bundle version (`CFBundleVersion`) that the build supports.'),
52
52
  })),
53
- action: async (options) => {
53
+ action: withAuth(async (options) => {
54
54
  const { appId, buildId, androidEq, androidMax, androidMin, iosEq, iosMax, iosMin } = options;
55
- if (!authorizationService.hasAuthorizationToken()) {
56
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
57
- process.exit(1);
58
- }
59
55
  if (!appId) {
60
56
  if (!isInteractive()) {
61
57
  consola.error('You must provide an app ID when running in non-interactive environment.');
@@ -84,5 +80,5 @@ export default defineCommand({
84
80
  minIosAppVersionCode: iosMin,
85
81
  });
86
82
  consola.success('Native version constraints set successfully.');
87
- },
83
+ }),
88
84
  });
@@ -2,15 +2,14 @@ import { DEFAULT_CONSOLE_BASE_URL, MAX_CONCURRENT_UPLOADS } from '../../../confi
2
2
  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
- import authorizationService from '../../../services/authorization-service.js';
6
- import organizationsService from '../../../services/organizations.js';
5
+ import { withAuth } from '../../../utils/auth.js';
7
6
  import { createBufferFromPath, createBufferFromReadStream, createBufferFromString, isPrivateKeyContent, } from '../../../utils/buffer.js';
8
7
  import { isInteractive } from '../../../utils/environment.js';
9
8
  import { fileExistsAtPath, getFilesInDirectoryAndSubdirectories, isDirectory } from '../../../utils/file.js';
10
9
  import { createHash } from '../../../utils/hash.js';
11
10
  import { generateManifestJson } from '../../../utils/manifest.js';
12
11
  import { formatPrivateKey } from '../../../utils/private-key.js';
13
- import { prompt } from '../../../utils/prompt.js';
12
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
14
13
  import { createSignature } from '../../../utils/signature.js';
15
14
  import zip from '../../../utils/zip.js';
16
15
  import { defineCommand, defineOptions } from '@robingenz/zli';
@@ -114,13 +113,8 @@ export default defineCommand({
114
113
  .describe('The percentage of devices to deploy the bundle to. Must be an integer between 0 and 100.'),
115
114
  yes: z.boolean().optional().describe('Skip confirmation prompt.'),
116
115
  }), { y: 'yes' }),
117
- action: async (options, args) => {
116
+ action: withAuth(async (options, args) => {
118
117
  let { androidEq, androidMax, androidMin, appId, artifactType, channel, commitMessage, commitRef, commitSha, customProperty, expiresInDays, gitRef, iosEq, iosMax, iosMin, path, privateKey, rolloutPercentage, } = options;
119
- // Check if the user is logged in
120
- if (!authorizationService.hasAuthorizationToken()) {
121
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
122
- process.exit(1);
123
- }
124
118
  // Calculate the expiration date
125
119
  let expiresAt;
126
120
  if (expiresInDays) {
@@ -180,36 +174,8 @@ export default defineCommand({
180
174
  consola.error('You must provide an app ID when running in non-interactive environment.');
181
175
  process.exit(1);
182
176
  }
183
- const organizations = await organizationsService.findAll();
184
- if (organizations.length === 0) {
185
- consola.error('You must create an organization before creating a bundle.');
186
- process.exit(1);
187
- }
188
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
189
- const organizationId = await prompt('Select the organization of the app for which you want to create a bundle.', {
190
- type: 'select',
191
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
192
- });
193
- if (!organizationId) {
194
- consola.error('You must select the organization of an app for which you want to create a bundle.');
195
- process.exit(1);
196
- }
197
- const apps = await appsService.findAll({
198
- organizationId,
199
- });
200
- if (apps.length === 0) {
201
- consola.error('You must create an app before creating a bundle.');
202
- process.exit(1);
203
- }
204
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
205
- appId = await prompt('Which app do you want to deploy to:', {
206
- type: 'select',
207
- options: apps.map((app) => ({ label: app.name, value: app.id })),
208
- });
209
- if (!appId) {
210
- consola.error('You must select an app to deploy to.');
211
- process.exit(1);
212
- }
177
+ const organizationId = await promptOrganizationSelection({ allowCreate: true });
178
+ appId = await promptAppSelection(organizationId, { allowCreate: true });
213
179
  }
214
180
  // Prompt for channel if interactive
215
181
  if (!channel && !options.yes && isInteractive()) {
@@ -311,7 +277,7 @@ export default defineCommand({
311
277
  consola.info(`Deployment URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/deployments/${response.appDeploymentId}`);
312
278
  }
313
279
  consola.success('Live Update successfully uploaded.');
314
- },
280
+ }),
315
281
  });
316
282
  const uploadFile = async (options) => {
317
283
  let { appId, appBundleId, buffer, href, mimeType, name, privateKeyBuffer, retryOnFailure } = options;
@@ -9,6 +9,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
9
9
  import uploadCommand from './upload.js';
10
10
  // Mock dependencies
11
11
  vi.mock('@/utils/user-config.js');
12
+ vi.mock('@/utils/prompt.js');
12
13
  vi.mock('@/services/authorization-service.js');
13
14
  vi.mock('@/utils/file.js');
14
15
  vi.mock('@/utils/zip.js');
@@ -46,7 +47,7 @@ describe('apps-liveupdates-upload', () => {
46
47
  const options = { appId, path: './dist', artifactType: 'zip', rollout: 1 };
47
48
  mockAuthorizationService.hasAuthorizationToken.mockReturnValue(false);
48
49
  await expect(uploadCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
49
- expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command. Please run the `login` command first.');
50
+ expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command. Set the `CAPAWESOME_TOKEN` environment variable or use the `--token` option.');
50
51
  });
51
52
  it('should handle path validation errors', async () => {
52
53
  const appId = 'app-123';
@@ -1,5 +1,5 @@
1
- import authorizationService from '../../services/authorization-service.js';
2
1
  import organizationsService from '../../services/organizations.js';
2
+ import { withAuth } from '../../utils/auth.js';
3
3
  import { isInteractive } from '../../utils/environment.js';
4
4
  import { prompt } from '../../utils/prompt.js';
5
5
  import { defineCommand, defineOptions } from '@robingenz/zli';
@@ -10,12 +10,8 @@ export default defineCommand({
10
10
  options: defineOptions(z.object({
11
11
  name: z.string().optional().describe('Name of the organization.'),
12
12
  })),
13
- action: async (options, args) => {
13
+ action: withAuth(async (options, args) => {
14
14
  let { name } = options;
15
- if (!authorizationService.hasAuthorizationToken()) {
16
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
17
- process.exit(1);
18
- }
19
15
  if (!name) {
20
16
  if (!isInteractive()) {
21
17
  consola.error('You must provide the organization name when running in non-interactive environment.');
@@ -26,5 +22,5 @@ export default defineCommand({
26
22
  const response = await organizationsService.create({ name });
27
23
  consola.info(`Organization ID: ${response.id}`);
28
24
  consola.success('Organization created successfully.');
29
- },
25
+ }),
30
26
  });
@@ -66,8 +66,10 @@ describe('organizations-create', () => {
66
66
  const organizationName = 'Test Organization';
67
67
  const options = { name: organizationName };
68
68
  mockAuthorizationService.hasAuthorizationToken.mockReturnValue(false);
69
+ mockPrompt.mockResolvedValueOnce(false);
69
70
  await expect(createOrganizationCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
70
- expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command. Please run the `login` command first.');
71
+ expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command.');
72
+ expect(mockConsola.error).toHaveBeenCalledWith('Please run the `login` command first.');
71
73
  });
72
74
  it('should handle API error during creation', async () => {
73
75
  const organizationName = 'Test Organization';
@@ -0,0 +1,27 @@
1
+ import authorizationService from '../services/authorization-service.js';
2
+ import { isInteractive } from '../utils/environment.js';
3
+ import { prompt } from '../utils/prompt.js';
4
+ import consola from 'consola';
5
+ export function withAuth(action) {
6
+ return async (options, args) => {
7
+ if (!authorizationService.hasAuthorizationToken()) {
8
+ if (!isInteractive()) {
9
+ consola.error('You must be logged in to run this command. Set the `CAPAWESOME_TOKEN` environment variable or use the `--token` option.');
10
+ process.exit(1);
11
+ }
12
+ consola.error('You must be logged in to run this command.');
13
+ const shouldLogin = await prompt('Do you want to login now?', {
14
+ type: 'confirm',
15
+ initial: true,
16
+ });
17
+ if (shouldLogin) {
18
+ await (await import('../commands/login.js').then((mod) => mod.default)).action({}, undefined);
19
+ }
20
+ else {
21
+ consola.error('Please run the `login` command first.');
22
+ process.exit(1);
23
+ }
24
+ }
25
+ return action(options, args);
26
+ };
27
+ }