@capawesome/cli 3.11.0 → 4.0.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.
- package/CHANGELOG.md +34 -0
- package/dist/commands/apps/builds/cancel.js +1 -1
- package/dist/commands/apps/builds/create.js +58 -50
- package/dist/commands/apps/builds/download.js +27 -3
- package/dist/commands/apps/bundles/create.js +5 -449
- package/dist/commands/apps/bundles/delete.js +3 -68
- package/dist/commands/apps/bundles/update.js +3 -66
- package/dist/commands/apps/channels/create.js +5 -8
- package/dist/commands/apps/channels/create.test.js +6 -9
- package/dist/commands/apps/channels/delete.js +3 -2
- package/dist/commands/apps/channels/get.js +2 -12
- package/dist/commands/apps/channels/get.test.js +1 -2
- package/dist/commands/apps/channels/list.js +2 -10
- package/dist/commands/apps/channels/list.test.js +2 -3
- package/dist/commands/apps/channels/pause.js +85 -0
- package/dist/commands/apps/channels/resume.js +85 -0
- package/dist/commands/apps/channels/update.js +4 -7
- package/dist/commands/apps/channels/update.test.js +2 -4
- package/dist/commands/apps/create.js +1 -1
- package/dist/commands/apps/delete.js +3 -2
- package/dist/commands/apps/deployments/cancel.js +1 -1
- package/dist/commands/apps/deployments/create.js +82 -31
- package/dist/commands/apps/devices/delete.js +3 -2
- package/dist/commands/apps/environments/create.js +1 -1
- package/dist/commands/apps/environments/delete.js +3 -2
- package/dist/commands/apps/liveupdates/bundle.js +117 -0
- package/dist/commands/apps/liveupdates/generate-manifest.js +39 -0
- package/dist/commands/{manifests/generate.test.js → apps/liveupdates/generate-manifest.test.js} +6 -6
- package/dist/commands/apps/liveupdates/register.js +291 -0
- package/dist/commands/apps/{bundles/create.test.js → liveupdates/register.test.js} +123 -111
- package/dist/commands/apps/liveupdates/rollback.js +171 -0
- package/dist/commands/apps/liveupdates/rollout.js +147 -0
- package/dist/commands/apps/liveupdates/upload.js +420 -0
- package/dist/commands/apps/liveupdates/upload.test.js +325 -0
- package/dist/commands/manifests/generate.js +2 -27
- package/dist/commands/organizations/create.js +1 -1
- package/dist/index.js +8 -0
- package/dist/services/app-builds.js +9 -2
- package/dist/services/app-channels.js +19 -0
- package/dist/services/app-deployments.js +24 -14
- package/dist/services/config.js +2 -0
- package/dist/utils/app-environments.js +2 -1
- package/dist/utils/time-format.js +26 -0
- package/package.json +3 -3
- package/dist/commands/apps/bundles/delete.test.js +0 -142
- package/dist/commands/apps/bundles/update.test.js +0 -144
- package/dist/utils/capacitor-config.js +0 -96
- package/dist/utils/package-json.js +0 -58
|
@@ -35,15 +35,15 @@ describe('apps-channels-create', () => {
|
|
|
35
35
|
it('should create channel with provided options', async () => {
|
|
36
36
|
const appId = 'app-123';
|
|
37
37
|
const channelName = 'production';
|
|
38
|
-
const
|
|
38
|
+
const protectedFlag = true;
|
|
39
39
|
const channelId = 'channel-456';
|
|
40
40
|
const testToken = 'test-token';
|
|
41
|
-
const options = { appId, name: channelName,
|
|
41
|
+
const options = { appId, name: channelName, protected: protectedFlag };
|
|
42
42
|
const scope = nock(DEFAULT_API_BASE_URL)
|
|
43
43
|
.post(`/v1/apps/${appId}/channels`, {
|
|
44
44
|
appId,
|
|
45
45
|
name: channelName,
|
|
46
|
-
|
|
46
|
+
protected: protectedFlag,
|
|
47
47
|
})
|
|
48
48
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
49
49
|
.reply(201, { id: channelId, name: channelName });
|
|
@@ -72,7 +72,7 @@ describe('apps-channels-create', () => {
|
|
|
72
72
|
.post(`/v1/apps/${appId}/channels`, {
|
|
73
73
|
appId,
|
|
74
74
|
name: channelName,
|
|
75
|
-
|
|
75
|
+
protected: undefined,
|
|
76
76
|
})
|
|
77
77
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
78
78
|
.reply(201, { id: channelId, name: channelName });
|
|
@@ -91,7 +91,7 @@ describe('apps-channels-create', () => {
|
|
|
91
91
|
.post('/v1/apps/app-123/channels', {
|
|
92
92
|
appId: 'app-123',
|
|
93
93
|
name: 'development',
|
|
94
|
-
|
|
94
|
+
protected: undefined,
|
|
95
95
|
})
|
|
96
96
|
.matchHeader('Authorization', 'Bearer test-token')
|
|
97
97
|
.reply(201, { id: 'channel-456', name: 'development' });
|
|
@@ -135,10 +135,7 @@ describe('apps-channels-create', () => {
|
|
|
135
135
|
const actualExpiresAt = new Date(body.expiresAt);
|
|
136
136
|
const timeDiff = Math.abs(actualExpiresAt.getTime() - expectedExpiresAt.getTime());
|
|
137
137
|
const oneMinute = 60 * 1000;
|
|
138
|
-
return (body.appId === appId &&
|
|
139
|
-
body.name === channelName &&
|
|
140
|
-
body.totalAppBundleLimit === undefined &&
|
|
141
|
-
timeDiff < oneMinute);
|
|
138
|
+
return (body.appId === appId && body.name === channelName && body.protected === undefined && timeDiff < oneMinute);
|
|
142
139
|
})
|
|
143
140
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
144
141
|
.reply(201, { id: channelId, name: channelName });
|
|
@@ -19,7 +19,8 @@ export default defineCommand({
|
|
|
19
19
|
.string()
|
|
20
20
|
.optional()
|
|
21
21
|
.describe('Name of the channel. Either the ID or name of the channel must be provided.'),
|
|
22
|
-
|
|
22
|
+
yes: z.boolean().optional().describe('Skip confirmation prompt.'),
|
|
23
|
+
}), { y: 'yes' }),
|
|
23
24
|
action: async (options, args) => {
|
|
24
25
|
let { appId, channelId, name } = options;
|
|
25
26
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
@@ -77,7 +78,7 @@ export default defineCommand({
|
|
|
77
78
|
channelId = selectedChannelId;
|
|
78
79
|
}
|
|
79
80
|
// Confirm deletion
|
|
80
|
-
if (isInteractive()) {
|
|
81
|
+
if (!options.yes && isInteractive()) {
|
|
81
82
|
const confirmed = await prompt('Are you sure you want to delete this channel?', {
|
|
82
83
|
type: 'confirm',
|
|
83
84
|
});
|
|
@@ -44,20 +44,10 @@ export default defineCommand({
|
|
|
44
44
|
process.exit(1);
|
|
45
45
|
}
|
|
46
46
|
if (json) {
|
|
47
|
-
console.log(JSON.stringify(
|
|
48
|
-
id: channel.id,
|
|
49
|
-
name: channel.name,
|
|
50
|
-
totalAppBundleLimit: channel.totalAppBundleLimit,
|
|
51
|
-
appId: channel.appId,
|
|
52
|
-
}, null, 2));
|
|
47
|
+
console.log(JSON.stringify(channel, null, 2));
|
|
53
48
|
}
|
|
54
49
|
else {
|
|
55
|
-
console.table(
|
|
56
|
-
id: channel.id,
|
|
57
|
-
name: channel.name,
|
|
58
|
-
totalAppBundleLimit: channel.totalAppBundleLimit,
|
|
59
|
-
appId: channel.appId,
|
|
60
|
-
});
|
|
50
|
+
console.table(channel);
|
|
61
51
|
consola.success('Channel retrieved successfully.');
|
|
62
52
|
}
|
|
63
53
|
},
|
|
@@ -60,7 +60,7 @@ describe('apps-channels-get', () => {
|
|
|
60
60
|
const channel = {
|
|
61
61
|
id: channelId,
|
|
62
62
|
name: 'production',
|
|
63
|
-
|
|
63
|
+
protectedAt: null,
|
|
64
64
|
appId,
|
|
65
65
|
};
|
|
66
66
|
const options = { appId, channelId };
|
|
@@ -80,7 +80,6 @@ describe('apps-channels-get', () => {
|
|
|
80
80
|
const channel = {
|
|
81
81
|
id: 'channel-789',
|
|
82
82
|
name: channelName,
|
|
83
|
-
totalAppBundleLimit: 5,
|
|
84
83
|
appId,
|
|
85
84
|
};
|
|
86
85
|
const options = { appId, name: channelName, json: true };
|
|
@@ -58,19 +58,11 @@ export default defineCommand({
|
|
|
58
58
|
limit,
|
|
59
59
|
offset,
|
|
60
60
|
});
|
|
61
|
-
const logData = foundChannels.map((channel) => ({
|
|
62
|
-
id: channel.id,
|
|
63
|
-
name: channel.name,
|
|
64
|
-
totalAppBundleLimit: channel.totalAppBundleLimit,
|
|
65
|
-
appId: channel.appId,
|
|
66
|
-
createdAt: channel.createdAt,
|
|
67
|
-
updatedAt: channel.updatedAt,
|
|
68
|
-
}));
|
|
69
61
|
if (json) {
|
|
70
|
-
console.log(JSON.stringify(
|
|
62
|
+
console.log(JSON.stringify(foundChannels, null, 2));
|
|
71
63
|
}
|
|
72
64
|
else {
|
|
73
|
-
console.table(
|
|
65
|
+
console.table(foundChannels);
|
|
74
66
|
consola.success('Channels retrieved successfully.');
|
|
75
67
|
}
|
|
76
68
|
},
|
|
@@ -47,7 +47,7 @@ describe('apps-channels-list', () => {
|
|
|
47
47
|
{
|
|
48
48
|
id: 'channel-1',
|
|
49
49
|
name: 'production',
|
|
50
|
-
|
|
50
|
+
protectedAt: null,
|
|
51
51
|
appId,
|
|
52
52
|
createdAt: '2023-01-01T00:00:00Z',
|
|
53
53
|
updatedAt: '2023-01-01T00:00:00Z',
|
|
@@ -55,7 +55,7 @@ describe('apps-channels-list', () => {
|
|
|
55
55
|
{
|
|
56
56
|
id: 'channel-2',
|
|
57
57
|
name: 'staging',
|
|
58
|
-
|
|
58
|
+
protectedAt: null,
|
|
59
59
|
appId,
|
|
60
60
|
createdAt: '2023-01-02T00:00:00Z',
|
|
61
61
|
updatedAt: '2023-01-02T00:00:00Z',
|
|
@@ -78,7 +78,6 @@ describe('apps-channels-list', () => {
|
|
|
78
78
|
{
|
|
79
79
|
id: 'channel-1',
|
|
80
80
|
name: 'production',
|
|
81
|
-
totalAppBundleLimit: 10,
|
|
82
81
|
appId,
|
|
83
82
|
createdAt: '2023-01-01T00:00:00Z',
|
|
84
83
|
updatedAt: '2023-01-01T00:00:00Z',
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import appChannelsService from '../../../services/app-channels.js';
|
|
2
|
+
import appsService from '../../../services/apps.js';
|
|
3
|
+
import authorizationService from '../../../services/authorization-service.js';
|
|
4
|
+
import organizationsService from '../../../services/organizations.js';
|
|
5
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
6
|
+
import { prompt } from '../../../utils/prompt.js';
|
|
7
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
8
|
+
import consola from 'consola';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
export default defineCommand({
|
|
11
|
+
description: 'Pause an app channel.',
|
|
12
|
+
options: defineOptions(z.object({
|
|
13
|
+
appId: z.string().uuid({ message: 'App ID must be a UUID.' }).optional().describe('ID of the app.'),
|
|
14
|
+
channel: z.string().optional().describe('Name of the channel to pause.'),
|
|
15
|
+
})),
|
|
16
|
+
action: async (options, args) => {
|
|
17
|
+
let { appId, channel } = options;
|
|
18
|
+
if (!authorizationService.hasAuthorizationToken()) {
|
|
19
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
if (!appId) {
|
|
23
|
+
if (!isInteractive()) {
|
|
24
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const organizations = await organizationsService.findAll();
|
|
28
|
+
if (organizations.length === 0) {
|
|
29
|
+
consola.error('You must create an organization before pausing a channel.');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
33
|
+
const organizationId = await prompt('Select the organization of the app.', {
|
|
34
|
+
type: 'select',
|
|
35
|
+
options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
|
|
36
|
+
});
|
|
37
|
+
if (!organizationId) {
|
|
38
|
+
consola.error('You must select the organization of the app.');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const apps = await appsService.findAll({
|
|
42
|
+
organizationId,
|
|
43
|
+
});
|
|
44
|
+
if (!apps.length) {
|
|
45
|
+
consola.error('You must create an app before pausing a channel.');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
49
|
+
appId = await prompt('Which app do you want to pause the channel in?', {
|
|
50
|
+
type: 'select',
|
|
51
|
+
options: apps.map((app) => ({ label: app.name, value: app.id })),
|
|
52
|
+
});
|
|
53
|
+
if (!appId) {
|
|
54
|
+
consola.error('You must select an app.');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!channel) {
|
|
59
|
+
if (!isInteractive()) {
|
|
60
|
+
consola.error('You must provide a channel when running in non-interactive environment.');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
64
|
+
channel = await prompt('Enter the name of the channel to pause:', {
|
|
65
|
+
type: 'text',
|
|
66
|
+
});
|
|
67
|
+
if (!channel) {
|
|
68
|
+
consola.error('You must provide a channel name.');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const channels = await appChannelsService.findAll({ appId, name: channel });
|
|
73
|
+
if (channels.length === 0) {
|
|
74
|
+
consola.error('Channel not found.');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
const channelId = channels[0]?.id;
|
|
78
|
+
if (!channelId) {
|
|
79
|
+
consola.error('Channel ID not found.');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
await appChannelsService.pause({ appId, channelId });
|
|
83
|
+
consola.success('Channel paused successfully.');
|
|
84
|
+
},
|
|
85
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import appChannelsService from '../../../services/app-channels.js';
|
|
2
|
+
import appsService from '../../../services/apps.js';
|
|
3
|
+
import authorizationService from '../../../services/authorization-service.js';
|
|
4
|
+
import organizationsService from '../../../services/organizations.js';
|
|
5
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
6
|
+
import { prompt } from '../../../utils/prompt.js';
|
|
7
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
8
|
+
import consola from 'consola';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
export default defineCommand({
|
|
11
|
+
description: 'Resume an app channel.',
|
|
12
|
+
options: defineOptions(z.object({
|
|
13
|
+
appId: z.string().uuid({ message: 'App ID must be a UUID.' }).optional().describe('ID of the app.'),
|
|
14
|
+
channel: z.string().optional().describe('Name of the channel to resume.'),
|
|
15
|
+
})),
|
|
16
|
+
action: async (options, args) => {
|
|
17
|
+
let { appId, channel } = options;
|
|
18
|
+
if (!authorizationService.hasAuthorizationToken()) {
|
|
19
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
if (!appId) {
|
|
23
|
+
if (!isInteractive()) {
|
|
24
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const organizations = await organizationsService.findAll();
|
|
28
|
+
if (organizations.length === 0) {
|
|
29
|
+
consola.error('You must create an organization before resuming a channel.');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
33
|
+
const organizationId = await prompt('Select the organization of the app.', {
|
|
34
|
+
type: 'select',
|
|
35
|
+
options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
|
|
36
|
+
});
|
|
37
|
+
if (!organizationId) {
|
|
38
|
+
consola.error('You must select the organization of the app.');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const apps = await appsService.findAll({
|
|
42
|
+
organizationId,
|
|
43
|
+
});
|
|
44
|
+
if (!apps.length) {
|
|
45
|
+
consola.error('You must create an app before resuming a channel.');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
49
|
+
appId = await prompt('Which app do you want to resume the channel in?', {
|
|
50
|
+
type: 'select',
|
|
51
|
+
options: apps.map((app) => ({ label: app.name, value: app.id })),
|
|
52
|
+
});
|
|
53
|
+
if (!appId) {
|
|
54
|
+
consola.error('You must select an app.');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!channel) {
|
|
59
|
+
if (!isInteractive()) {
|
|
60
|
+
consola.error('You must provide a channel when running in non-interactive environment.');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
64
|
+
channel = await prompt('Enter the name of the channel to resume:', {
|
|
65
|
+
type: 'text',
|
|
66
|
+
});
|
|
67
|
+
if (!channel) {
|
|
68
|
+
consola.error('You must provide a channel name.');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const channels = await appChannelsService.findAll({ appId, name: channel });
|
|
73
|
+
if (channels.length === 0) {
|
|
74
|
+
consola.error('Channel not found.');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
const channelId = channels[0]?.id;
|
|
78
|
+
if (!channelId) {
|
|
79
|
+
consola.error('Channel ID not found.');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
await appChannelsService.resume({ appId, channelId });
|
|
83
|
+
consola.success('Channel resumed successfully.');
|
|
84
|
+
},
|
|
85
|
+
});
|
|
@@ -2,24 +2,21 @@ import appChannelsService from '../../../services/app-channels.js';
|
|
|
2
2
|
import appsService from '../../../services/apps.js';
|
|
3
3
|
import authorizationService from '../../../services/authorization-service.js';
|
|
4
4
|
import organizationsService from '../../../services/organizations.js';
|
|
5
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
5
6
|
import { prompt } from '../../../utils/prompt.js';
|
|
6
7
|
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
7
8
|
import consola from 'consola';
|
|
8
|
-
import { isInteractive } from '../../../utils/environment.js';
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
export default defineCommand({
|
|
11
11
|
description: 'Update an existing app channel.',
|
|
12
12
|
options: defineOptions(z.object({
|
|
13
13
|
appId: z.string().optional().describe('ID of the app.'),
|
|
14
14
|
channelId: z.string().optional().describe('ID of the channel.'),
|
|
15
|
-
bundleLimit: z.coerce
|
|
16
|
-
.number()
|
|
17
|
-
.optional()
|
|
18
|
-
.describe('Maximum number of bundles that can be assigned to the channel. If more bundles are assigned, the oldest bundles will be automatically deleted.'),
|
|
19
15
|
name: z.string().optional().describe('Name of the channel.'),
|
|
16
|
+
protected: z.boolean().optional().describe('Whether to protect the channel or not.'),
|
|
20
17
|
})),
|
|
21
18
|
action: async (options, args) => {
|
|
22
|
-
let { appId, channelId,
|
|
19
|
+
let { appId, channelId, name, protected: _protected } = options;
|
|
23
20
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
24
21
|
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
25
22
|
process.exit(1);
|
|
@@ -72,7 +69,7 @@ export default defineCommand({
|
|
|
72
69
|
appId,
|
|
73
70
|
appChannelId: channelId,
|
|
74
71
|
name,
|
|
75
|
-
|
|
72
|
+
protected: _protected,
|
|
76
73
|
});
|
|
77
74
|
consola.success('Channel updated successfully.');
|
|
78
75
|
},
|
|
@@ -44,15 +44,13 @@ describe('apps-channels-update', () => {
|
|
|
44
44
|
const appId = 'app-123';
|
|
45
45
|
const channelId = 'channel-456';
|
|
46
46
|
const channelName = 'updated-production';
|
|
47
|
-
const bundleLimit = 15;
|
|
48
47
|
const testToken = 'test-token';
|
|
49
|
-
const options = { appId, channelId, name: channelName
|
|
48
|
+
const options = { appId, channelId, name: channelName };
|
|
50
49
|
const scope = nock(DEFAULT_API_BASE_URL)
|
|
51
50
|
.patch(`/v1/apps/${appId}/channels/${channelId}`, {
|
|
52
51
|
appId,
|
|
53
52
|
appChannelId: channelId,
|
|
54
53
|
name: channelName,
|
|
55
|
-
totalAppBundleLimit: bundleLimit,
|
|
56
54
|
})
|
|
57
55
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
58
56
|
.reply(200, { id: channelId, name: channelName });
|
|
@@ -67,7 +65,7 @@ describe('apps-channels-update', () => {
|
|
|
67
65
|
const testToken = 'test-token';
|
|
68
66
|
const organization = { id: orgId, name: 'Org 1' };
|
|
69
67
|
const app = { id: appId, name: 'App 1' };
|
|
70
|
-
const options = { channelId
|
|
68
|
+
const options = { channelId };
|
|
71
69
|
const orgsScope = nock(DEFAULT_API_BASE_URL)
|
|
72
70
|
.get('/v1/organizations')
|
|
73
71
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
@@ -46,7 +46,7 @@ export default defineCommand({
|
|
|
46
46
|
name = await prompt('Enter the name of the app:', { type: 'text' });
|
|
47
47
|
}
|
|
48
48
|
const response = await appsService.create({ name, organizationId });
|
|
49
|
-
consola.success('App created successfully.');
|
|
50
49
|
consola.info(`App ID: ${response.id}`);
|
|
50
|
+
consola.success('App created successfully.');
|
|
51
51
|
},
|
|
52
52
|
});
|
|
@@ -10,7 +10,8 @@ export default defineCommand({
|
|
|
10
10
|
description: 'Delete an app.',
|
|
11
11
|
options: defineOptions(z.object({
|
|
12
12
|
appId: z.string().optional().describe('ID of the app.'),
|
|
13
|
-
|
|
13
|
+
yes: z.boolean().optional().describe('Skip confirmation prompt.'),
|
|
14
|
+
}), { y: 'yes' }),
|
|
14
15
|
action: async (options, args) => {
|
|
15
16
|
let { appId } = options;
|
|
16
17
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
@@ -49,7 +50,7 @@ export default defineCommand({
|
|
|
49
50
|
options: apps.map((app) => ({ label: app.name, value: app.id })),
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
|
-
if (isInteractive()) {
|
|
53
|
+
if (!options.yes && isInteractive()) {
|
|
53
54
|
const confirmed = await prompt('Are you sure you want to delete this app?', {
|
|
54
55
|
type: 'confirm',
|
|
55
56
|
});
|
|
@@ -3,10 +3,10 @@ import appsService from '../../../services/apps.js';
|
|
|
3
3
|
import authorizationService from '../../../services/authorization-service.js';
|
|
4
4
|
import jobsService from '../../../services/jobs.js';
|
|
5
5
|
import organizationsService from '../../../services/organizations.js';
|
|
6
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
6
7
|
import { prompt } from '../../../utils/prompt.js';
|
|
7
8
|
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
8
9
|
import consola from 'consola';
|
|
9
|
-
import { isInteractive } from '../../../utils/environment.js';
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
export default defineCommand({
|
|
12
12
|
description: 'Cancel an ongoing app deployment.',
|
|
@@ -6,11 +6,11 @@ import appsService from '../../../services/apps.js';
|
|
|
6
6
|
import authorizationService from '../../../services/authorization-service.js';
|
|
7
7
|
import organizationsService from '../../../services/organizations.js';
|
|
8
8
|
import { unescapeAnsi } from '../../../utils/ansi.js';
|
|
9
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
9
10
|
import { prompt } from '../../../utils/prompt.js';
|
|
10
11
|
import { wait } from '../../../utils/wait.js';
|
|
11
12
|
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
12
13
|
import consola from 'consola';
|
|
13
|
-
import { isInteractive } from '../../../utils/environment.js';
|
|
14
14
|
import { z } from 'zod';
|
|
15
15
|
export default defineCommand({
|
|
16
16
|
description: 'Create a new app deployment.',
|
|
@@ -27,14 +27,16 @@ export default defineCommand({
|
|
|
27
27
|
})
|
|
28
28
|
.optional()
|
|
29
29
|
.describe('Build ID to deploy.'),
|
|
30
|
-
|
|
30
|
+
buildNumber: z.string().optional().describe('Build number to deploy (e.g., "1", "42").'),
|
|
31
|
+
channel: z.string().optional().describe('The name of the channel to deploy to (Web only).'),
|
|
32
|
+
destination: z.string().optional().describe('The name of the destination to deploy to (Android/iOS only).'),
|
|
31
33
|
detached: z
|
|
32
34
|
.boolean()
|
|
33
35
|
.optional()
|
|
34
36
|
.describe('Exit immediately after creating the deployment without waiting for completion.'),
|
|
35
37
|
})),
|
|
36
38
|
action: async (options) => {
|
|
37
|
-
let { appId, buildId, destination } = options;
|
|
39
|
+
let { appId, buildId, buildNumber, channel, destination } = options;
|
|
38
40
|
// Check if the user is logged in
|
|
39
41
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
40
42
|
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
@@ -77,22 +79,51 @@ export default defineCommand({
|
|
|
77
79
|
process.exit(1);
|
|
78
80
|
}
|
|
79
81
|
}
|
|
82
|
+
// Convert build number to build ID if provided
|
|
83
|
+
if (!buildId && buildNumber) {
|
|
84
|
+
const builds = await appBuildsService.findAll({ appId, numberAsString: buildNumber });
|
|
85
|
+
if (builds.length === 0) {
|
|
86
|
+
consola.error(`Build #${buildNumber} not found.`);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
buildId = builds[0]?.id;
|
|
90
|
+
}
|
|
80
91
|
// Prompt for build ID if not provided
|
|
81
92
|
if (!buildId) {
|
|
82
93
|
if (!isInteractive()) {
|
|
83
94
|
consola.error('You must provide a build ID when running in non-interactive environment.');
|
|
84
95
|
process.exit(1);
|
|
85
96
|
}
|
|
86
|
-
|
|
97
|
+
// Prompt for platform selection
|
|
98
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
99
|
+
const platform = await prompt('Select the platform of the build you want to deploy:', {
|
|
100
|
+
type: 'select',
|
|
101
|
+
options: [
|
|
102
|
+
{ label: 'Android', value: 'android' },
|
|
103
|
+
{ label: 'iOS', value: 'ios' },
|
|
104
|
+
{ label: 'Web', value: 'web' },
|
|
105
|
+
],
|
|
106
|
+
});
|
|
107
|
+
if (!platform) {
|
|
108
|
+
consola.error('You must select a platform.');
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
if (platform !== 'android' && platform !== 'ios' && platform !== 'web') {
|
|
112
|
+
consola.error('Invalid platform selected.');
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
const builds = await appBuildsService.findAll({ appId, platform: platform });
|
|
87
116
|
if (builds.length === 0) {
|
|
88
117
|
consola.error('You must create a build before creating a deployment.');
|
|
89
118
|
process.exit(1);
|
|
90
119
|
}
|
|
91
120
|
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
92
|
-
buildId = await prompt('
|
|
121
|
+
buildId = await prompt('Select the build you want to deploy:', {
|
|
93
122
|
type: 'select',
|
|
94
123
|
options: builds.map((build) => ({
|
|
95
|
-
label:
|
|
124
|
+
label: build.platform === 'web'
|
|
125
|
+
? `Build #${build.numberAsString}`
|
|
126
|
+
: `Build #${build.numberAsString} (${build.type})`,
|
|
96
127
|
value: build.id,
|
|
97
128
|
})),
|
|
98
129
|
});
|
|
@@ -103,31 +134,50 @@ export default defineCommand({
|
|
|
103
134
|
}
|
|
104
135
|
// Get build information to determine platform
|
|
105
136
|
const build = await appBuildsService.findOne({ appId, appBuildId: buildId });
|
|
106
|
-
//
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
137
|
+
// Handle destination/channel selection based on platform
|
|
138
|
+
if (build.platform === 'web') {
|
|
139
|
+
// Web deployments use channels
|
|
140
|
+
if (!channel) {
|
|
141
|
+
if (!isInteractive()) {
|
|
142
|
+
consola.error('You must provide a channel when running in non-interactive environment.');
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
channel = await prompt('Enter the channel name to deploy to:', {
|
|
146
|
+
type: 'text',
|
|
147
|
+
});
|
|
148
|
+
if (!channel) {
|
|
149
|
+
consola.error('You must enter a channel name to deploy to.');
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
119
152
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
options: destinations.map((dest) => ({
|
|
124
|
-
label: dest.name,
|
|
125
|
-
value: dest.name,
|
|
126
|
-
})),
|
|
127
|
-
});
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// Android/iOS deployments use destinations
|
|
128
156
|
if (!destination) {
|
|
129
|
-
|
|
130
|
-
|
|
157
|
+
if (!isInteractive()) {
|
|
158
|
+
consola.error('You must provide a destination when running in non-interactive environment.');
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
const destinations = await appDestinationsService.findAll({
|
|
162
|
+
appId,
|
|
163
|
+
platform: build.platform,
|
|
164
|
+
});
|
|
165
|
+
if (destinations.length === 0) {
|
|
166
|
+
consola.error(`You must create a destination for the ${build.platform} platform before creating a deployment.`);
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
170
|
+
destination = await prompt('Which destination do you want to deploy to:', {
|
|
171
|
+
type: 'select',
|
|
172
|
+
options: destinations.map((dest) => ({
|
|
173
|
+
label: dest.name,
|
|
174
|
+
value: dest.name,
|
|
175
|
+
})),
|
|
176
|
+
});
|
|
177
|
+
if (!destination) {
|
|
178
|
+
consola.error('You must select a destination to deploy to.');
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
131
181
|
}
|
|
132
182
|
}
|
|
133
183
|
// Create the deployment
|
|
@@ -135,11 +185,12 @@ export default defineCommand({
|
|
|
135
185
|
const response = await appDeploymentsService.create({
|
|
136
186
|
appId,
|
|
137
187
|
appBuildId: buildId,
|
|
138
|
-
appDestinationName: destination,
|
|
188
|
+
appDestinationName: build.platform === 'web' ? undefined : destination,
|
|
189
|
+
appChannelName: build.platform === 'web' ? channel : undefined,
|
|
139
190
|
});
|
|
140
|
-
consola.success('Deployment created successfully.');
|
|
141
191
|
consola.info(`Deployment ID: ${response.id}`);
|
|
142
192
|
consola.info(`Deployment URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/deployments/${response.id}`);
|
|
193
|
+
consola.success('Deployment created successfully.');
|
|
143
194
|
// Wait for deployment job to complete by default, unless --detached flag is set
|
|
144
195
|
const shouldWait = !options.detached;
|
|
145
196
|
if (shouldWait) {
|
|
@@ -12,7 +12,8 @@ export default defineCommand({
|
|
|
12
12
|
options: defineOptions(z.object({
|
|
13
13
|
appId: z.string().optional().describe('ID of the app.'),
|
|
14
14
|
deviceId: z.string().optional().describe('ID of the device.'),
|
|
15
|
-
|
|
15
|
+
yes: z.boolean().optional().describe('Skip confirmation prompt.'),
|
|
16
|
+
}), { y: 'yes' }),
|
|
16
17
|
action: async (options, args) => {
|
|
17
18
|
let { appId, deviceId } = options;
|
|
18
19
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
@@ -63,7 +64,7 @@ export default defineCommand({
|
|
|
63
64
|
});
|
|
64
65
|
}
|
|
65
66
|
// Confirm deletion
|
|
66
|
-
if (isInteractive()) {
|
|
67
|
+
if (!options.yes && isInteractive()) {
|
|
67
68
|
const confirmed = await prompt('Are you sure you want to delete this device?', {
|
|
68
69
|
type: 'confirm',
|
|
69
70
|
});
|