@capawesome/cli 3.10.2 → 4.0.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 +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 +13 -4
- package/dist/commands/apps/channels/delete.test.js +22 -7
- 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 +36 -12
- package/dist/commands/apps/channels/list.test.js +3 -4
- 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 +68 -0
- package/dist/commands/apps/environments/delete.js +88 -0
- package/dist/commands/apps/environments/list.js +69 -0
- package/dist/commands/apps/environments/set.js +126 -0
- package/dist/commands/apps/environments/unset.js +98 -0
- 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 +13 -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/app-environments.js +67 -2
- package/dist/services/config.js +2 -0
- package/dist/utils/app-environments.js +35 -0
- package/dist/utils/app-environments.ts.test.js +76 -0
- 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()) {
|
|
@@ -64,12 +65,20 @@ export default defineCommand({
|
|
|
64
65
|
consola.error('You must provide either the channel ID or name when running in non-interactive environment.');
|
|
65
66
|
process.exit(1);
|
|
66
67
|
}
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
const channels = await appChannelsService.findAll({ appId });
|
|
69
|
+
if (!channels.length) {
|
|
70
|
+
consola.error('No channels found for this app. Create one first.');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
74
|
+
const selectedChannelId = await prompt('Select the channel to delete:', {
|
|
75
|
+
type: 'select',
|
|
76
|
+
options: channels.map((channel) => ({ label: channel.name, value: channel.id })),
|
|
69
77
|
});
|
|
78
|
+
channelId = selectedChannelId;
|
|
70
79
|
}
|
|
71
80
|
// Confirm deletion
|
|
72
|
-
if (isInteractive()) {
|
|
81
|
+
if (!options.yes && isInteractive()) {
|
|
73
82
|
const confirmed = await prompt('Are you sure you want to delete this channel?', {
|
|
74
83
|
type: 'confirm',
|
|
75
84
|
});
|
|
@@ -107,22 +107,37 @@ describe('apps-channels-delete', () => {
|
|
|
107
107
|
expect(deleteScope.isDone()).toBe(true);
|
|
108
108
|
expect(mockConsola.success).toHaveBeenCalledWith('Channel deleted successfully.');
|
|
109
109
|
});
|
|
110
|
-
it('should prompt for channel
|
|
110
|
+
it('should prompt for channel selection when channelId and name not provided', async () => {
|
|
111
111
|
const appId = 'app-123';
|
|
112
|
+
const channelId = 'channel-456';
|
|
112
113
|
const channelName = 'development';
|
|
113
114
|
const testToken = 'test-token';
|
|
114
115
|
const options = { appId };
|
|
115
|
-
const
|
|
116
|
-
.
|
|
117
|
-
.query(
|
|
116
|
+
const channelsListScope = nock(DEFAULT_API_BASE_URL)
|
|
117
|
+
.get(`/v1/apps/${appId}/channels`)
|
|
118
|
+
.query(true)
|
|
119
|
+
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
120
|
+
.reply(200, [
|
|
121
|
+
{
|
|
122
|
+
id: channelId,
|
|
123
|
+
name: channelName,
|
|
124
|
+
appId,
|
|
125
|
+
},
|
|
126
|
+
]);
|
|
127
|
+
const deleteScope = nock(DEFAULT_API_BASE_URL)
|
|
128
|
+
.delete(`/v1/apps/${appId}/channels/${channelId}`)
|
|
118
129
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
119
130
|
.reply(200);
|
|
120
131
|
mockPrompt
|
|
121
|
-
.mockResolvedValueOnce(
|
|
132
|
+
.mockResolvedValueOnce(channelId) // channel selection
|
|
122
133
|
.mockResolvedValueOnce(true); // confirmation
|
|
123
134
|
await deleteChannelCommand.action(options, undefined);
|
|
124
|
-
expect(
|
|
125
|
-
expect(
|
|
135
|
+
expect(channelsListScope.isDone()).toBe(true);
|
|
136
|
+
expect(deleteScope.isDone()).toBe(true);
|
|
137
|
+
expect(mockPrompt).toHaveBeenCalledWith('Select the channel to delete:', {
|
|
138
|
+
type: 'select',
|
|
139
|
+
options: [{ label: channelName, value: channelId }],
|
|
140
|
+
});
|
|
126
141
|
expect(mockConsola.success).toHaveBeenCalledWith('Channel deleted successfully.');
|
|
127
142
|
});
|
|
128
143
|
it('should handle API error during deletion', async () => {
|
|
@@ -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 };
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import appChannelsService from '../../../services/app-channels.js';
|
|
2
|
+
import appsService from '../../../services/apps.js';
|
|
2
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';
|
|
3
7
|
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
4
8
|
import consola from 'consola';
|
|
5
9
|
import { z } from 'zod';
|
|
@@ -18,27 +22,47 @@ export default defineCommand({
|
|
|
18
22
|
process.exit(1);
|
|
19
23
|
}
|
|
20
24
|
if (!appId) {
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
if (!isInteractive()) {
|
|
26
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const organizations = await organizationsService.findAll();
|
|
30
|
+
if (organizations.length === 0) {
|
|
31
|
+
consola.error('You must create an organization before listing channels.');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
35
|
+
const organizationId = await prompt('Select the organization of the app for which you want to list channels.', {
|
|
36
|
+
type: 'select',
|
|
37
|
+
options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
|
|
38
|
+
});
|
|
39
|
+
if (!organizationId) {
|
|
40
|
+
consola.error('You must select the organization of an app for which you want to list channels.');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
const apps = await appsService.findAll({
|
|
44
|
+
organizationId,
|
|
45
|
+
});
|
|
46
|
+
if (!apps.length) {
|
|
47
|
+
consola.error('You must create an app before listing channels.');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
51
|
+
appId = await prompt('Which app do you want to list the channels for?', {
|
|
52
|
+
type: 'select',
|
|
53
|
+
options: apps.map((app) => ({ label: app.name, value: app.id })),
|
|
54
|
+
});
|
|
23
55
|
}
|
|
24
56
|
const foundChannels = await appChannelsService.findAll({
|
|
25
57
|
appId,
|
|
26
58
|
limit,
|
|
27
59
|
offset,
|
|
28
60
|
});
|
|
29
|
-
const logData = foundChannels.map((channel) => ({
|
|
30
|
-
id: channel.id,
|
|
31
|
-
name: channel.name,
|
|
32
|
-
totalAppBundleLimit: channel.totalAppBundleLimit,
|
|
33
|
-
appId: channel.appId,
|
|
34
|
-
createdAt: channel.createdAt,
|
|
35
|
-
updatedAt: channel.updatedAt,
|
|
36
|
-
}));
|
|
37
61
|
if (json) {
|
|
38
|
-
console.log(JSON.stringify(
|
|
62
|
+
console.log(JSON.stringify(foundChannels, null, 2));
|
|
39
63
|
}
|
|
40
64
|
else {
|
|
41
|
-
console.table(
|
|
65
|
+
console.table(foundChannels);
|
|
42
66
|
consola.success('Channels retrieved successfully.');
|
|
43
67
|
}
|
|
44
68
|
},
|
|
@@ -38,7 +38,7 @@ describe('apps-channels-list', () => {
|
|
|
38
38
|
it('should require appId', async () => {
|
|
39
39
|
const options = { appId: undefined };
|
|
40
40
|
await expect(listChannelsCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
41
|
-
expect(mockConsola.error).toHaveBeenCalledWith('You must provide an app ID.');
|
|
41
|
+
expect(mockConsola.error).toHaveBeenCalledWith('You must provide an app ID when running in non-interactive environment.');
|
|
42
42
|
});
|
|
43
43
|
it('should list channels and display table format', async () => {
|
|
44
44
|
const appId = 'app-123';
|
|
@@ -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.',
|