@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
|
@@ -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
|
});
|
|
@@ -0,0 +1,68 @@
|
|
|
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
|
+
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: 'Create a new environment.',
|
|
12
|
+
options: defineOptions(z.object({
|
|
13
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
14
|
+
name: z.string().optional().describe('Name of the environment.'),
|
|
15
|
+
})),
|
|
16
|
+
action: async (options, args) => {
|
|
17
|
+
let { appId, name } = 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 creating an environment.');
|
|
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 for which you want to create an environment.', {
|
|
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 an app for which you want to create an environment.');
|
|
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 creating an environment.');
|
|
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 create the environment for?', {
|
|
50
|
+
type: 'select',
|
|
51
|
+
options: apps.map((app) => ({ label: app.name, value: app.id })),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (!name) {
|
|
55
|
+
if (!isInteractive()) {
|
|
56
|
+
consola.error('You must provide the environment name when running in non-interactive environment.');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
name = await prompt('Enter the name of the environment:', { type: 'text' });
|
|
60
|
+
}
|
|
61
|
+
const response = await appEnvironmentsService.create({
|
|
62
|
+
appId,
|
|
63
|
+
name,
|
|
64
|
+
});
|
|
65
|
+
consola.info(`Environment ID: ${response.id}`);
|
|
66
|
+
consola.success('Environment created successfully.');
|
|
67
|
+
},
|
|
68
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
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
|
+
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: 'Delete an environment.',
|
|
12
|
+
options: defineOptions(z.object({
|
|
13
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
14
|
+
environmentId: z.string().optional().describe('ID of the environment. Either the ID or name must be provided.'),
|
|
15
|
+
name: z.string().optional().describe('Name of the environment. Either the ID or name must be provided.'),
|
|
16
|
+
yes: z.boolean().optional().describe('Skip confirmation prompt.'),
|
|
17
|
+
}), { y: 'yes' }),
|
|
18
|
+
action: async (options, args) => {
|
|
19
|
+
let { appId, environmentId, name } = options;
|
|
20
|
+
if (!authorizationService.hasAuthorizationToken()) {
|
|
21
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
if (!appId) {
|
|
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 deleting an environment.');
|
|
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 from which you want to delete an environment.', {
|
|
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 from which you want to delete an environment.');
|
|
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 deleting an environment.');
|
|
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 delete the environment from?', {
|
|
52
|
+
type: 'select',
|
|
53
|
+
options: apps.map((app) => ({ label: app.name, value: app.id })),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (!environmentId && !name) {
|
|
57
|
+
if (!isInteractive()) {
|
|
58
|
+
consola.error('You must provide either the environment ID or name when running in non-interactive environment.');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const environments = await appEnvironmentsService.findAll({ appId });
|
|
62
|
+
if (!environments.length) {
|
|
63
|
+
consola.error('No environments found for this app. Create one first.');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
67
|
+
const selectedEnvironmentId = await prompt('Select the environment to delete:', {
|
|
68
|
+
type: 'select',
|
|
69
|
+
options: environments.map((env) => ({ label: env.name, value: env.id })),
|
|
70
|
+
});
|
|
71
|
+
environmentId = selectedEnvironmentId;
|
|
72
|
+
}
|
|
73
|
+
if (!options.yes && isInteractive()) {
|
|
74
|
+
const confirmed = await prompt('Are you sure you want to delete this environment?', {
|
|
75
|
+
type: 'confirm',
|
|
76
|
+
});
|
|
77
|
+
if (!confirmed) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
await appEnvironmentsService.delete({
|
|
82
|
+
appId,
|
|
83
|
+
id: environmentId,
|
|
84
|
+
name,
|
|
85
|
+
});
|
|
86
|
+
consola.success('Environment deleted successfully.');
|
|
87
|
+
},
|
|
88
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
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: 'List all environments for an app.',
|
|
12
|
+
options: defineOptions(z.object({
|
|
13
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
14
|
+
json: z.boolean().optional().describe('Output in JSON format.'),
|
|
15
|
+
limit: z.coerce.number().optional().describe('Limit for pagination.'),
|
|
16
|
+
offset: z.coerce.number().optional().describe('Offset for pagination.'),
|
|
17
|
+
})),
|
|
18
|
+
action: async (options, args) => {
|
|
19
|
+
let { appId, json, limit, offset } = options;
|
|
20
|
+
if (!authorizationService.hasAuthorizationToken()) {
|
|
21
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
if (!appId) {
|
|
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 environments.');
|
|
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 environments.', {
|
|
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 environments.');
|
|
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 environments.');
|
|
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 environments for?', {
|
|
52
|
+
type: 'select',
|
|
53
|
+
options: apps.map((app) => ({ label: app.name, value: app.id })),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
const environments = await appEnvironmentsService.findAll({
|
|
57
|
+
appId,
|
|
58
|
+
limit,
|
|
59
|
+
offset,
|
|
60
|
+
});
|
|
61
|
+
if (json) {
|
|
62
|
+
console.log(JSON.stringify(environments, null, 2));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.table(environments);
|
|
66
|
+
consola.success('Environments retrieved successfully.');
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
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
|
+
import { parseKeyValuePairs } from '../../../utils/app-environments.js';
|
|
6
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
7
|
+
import { prompt } from '../../../utils/prompt.js';
|
|
8
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
9
|
+
import consola from 'consola';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
export default defineCommand({
|
|
13
|
+
description: 'Set environment variables and secrets.',
|
|
14
|
+
options: defineOptions(z.object({
|
|
15
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
16
|
+
environmentId: z.string().optional().describe('ID of the environment.'),
|
|
17
|
+
variable: z
|
|
18
|
+
.array(z.string())
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('Environment variable in key=value format. Can be specified multiple times.'),
|
|
21
|
+
variableFile: z.string().optional().describe('Path to a file containing environment variables in .env format.'),
|
|
22
|
+
secret: z
|
|
23
|
+
.array(z.string())
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Environment secret in key=value format. Can be specified multiple times.'),
|
|
26
|
+
secretFile: z.string().optional().describe('Path to a file containing environment secrets in .env format.'),
|
|
27
|
+
})),
|
|
28
|
+
action: async (options, args) => {
|
|
29
|
+
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
|
+
if (!appId) {
|
|
35
|
+
if (!isInteractive()) {
|
|
36
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
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
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (!environmentId) {
|
|
67
|
+
if (!isInteractive()) {
|
|
68
|
+
consola.error('You must provide an environment ID when running in non-interactive environment.');
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
const environments = await appEnvironmentsService.findAll({ appId });
|
|
72
|
+
if (!environments.length) {
|
|
73
|
+
consola.error('No environments found for this app. Create one first.');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
77
|
+
environmentId = await prompt('Select the environment:', {
|
|
78
|
+
type: 'select',
|
|
79
|
+
options: environments.map((env) => ({ label: env.name, value: env.id })),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// Parse variables from inline and file
|
|
83
|
+
const variablesMap = new Map();
|
|
84
|
+
if (variableFile) {
|
|
85
|
+
const fileContent = await fs.promises.readFile(variableFile, 'utf-8');
|
|
86
|
+
const fileVariables = parseKeyValuePairs(fileContent);
|
|
87
|
+
fileVariables.forEach((v) => variablesMap.set(v.key, v.value));
|
|
88
|
+
}
|
|
89
|
+
if (variable) {
|
|
90
|
+
const inlineVariables = parseKeyValuePairs(variable.join('\n'));
|
|
91
|
+
inlineVariables.forEach((v) => variablesMap.set(v.key, v.value));
|
|
92
|
+
}
|
|
93
|
+
const allVariables = Array.from(variablesMap.entries()).map(([key, value]) => ({ key, value }));
|
|
94
|
+
// Parse secrets from inline and file
|
|
95
|
+
const secretsMap = new Map();
|
|
96
|
+
if (secretFile) {
|
|
97
|
+
const fileContent = await fs.promises.readFile(secretFile, 'utf-8');
|
|
98
|
+
const fileSecrets = parseKeyValuePairs(fileContent);
|
|
99
|
+
fileSecrets.forEach((s) => secretsMap.set(s.key, s.value));
|
|
100
|
+
}
|
|
101
|
+
if (secret) {
|
|
102
|
+
const inlineSecrets = parseKeyValuePairs(secret.join('\n'));
|
|
103
|
+
inlineSecrets.forEach((s) => secretsMap.set(s.key, s.value));
|
|
104
|
+
}
|
|
105
|
+
const allSecrets = Array.from(secretsMap.entries()).map(([key, value]) => ({ key, value }));
|
|
106
|
+
if (!allVariables.length && !allSecrets.length) {
|
|
107
|
+
consola.error('You must provide at least one variable or secret to set.');
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
if (allVariables.length) {
|
|
111
|
+
await appEnvironmentsService.setVariables({
|
|
112
|
+
appId,
|
|
113
|
+
environmentId,
|
|
114
|
+
variables: allVariables,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
if (allSecrets.length) {
|
|
118
|
+
await appEnvironmentsService.setSecrets({
|
|
119
|
+
appId,
|
|
120
|
+
environmentId,
|
|
121
|
+
secrets: allSecrets,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
consola.success('Environment variables and secrets set successfully.');
|
|
125
|
+
},
|
|
126
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
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: 'Unset environment variables and secrets.',
|
|
12
|
+
options: defineOptions(z.object({
|
|
13
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
14
|
+
environmentId: z.string().optional().describe('ID of the environment.'),
|
|
15
|
+
variable: z
|
|
16
|
+
.array(z.string())
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Key of the environment variable to unset. Can be specified multiple times.'),
|
|
19
|
+
secret: z
|
|
20
|
+
.array(z.string())
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Key of the environment secret to unset. Can be specified multiple times.'),
|
|
23
|
+
})),
|
|
24
|
+
action: async (options, args) => {
|
|
25
|
+
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
|
+
if (!appId) {
|
|
31
|
+
if (!isInteractive()) {
|
|
32
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
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
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (!environmentId) {
|
|
63
|
+
if (!isInteractive()) {
|
|
64
|
+
consola.error('You must provide an environment ID when running in non-interactive environment.');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
const environments = await appEnvironmentsService.findAll({ appId });
|
|
68
|
+
if (!environments.length) {
|
|
69
|
+
consola.error('No environments found for this app. Create one first.');
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
73
|
+
environmentId = await prompt('Select the environment:', {
|
|
74
|
+
type: 'select',
|
|
75
|
+
options: environments.map((env) => ({ label: env.name, value: env.id })),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
if (!variableKeys?.length && !secretKeys?.length) {
|
|
79
|
+
consola.error('You must provide at least one variable key or secret key to unset.');
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
if (variableKeys?.length) {
|
|
83
|
+
await appEnvironmentsService.unsetVariables({
|
|
84
|
+
appId,
|
|
85
|
+
environmentId,
|
|
86
|
+
keys: variableKeys,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (secretKeys?.length) {
|
|
90
|
+
await appEnvironmentsService.unsetSecrets({
|
|
91
|
+
appId,
|
|
92
|
+
environmentId,
|
|
93
|
+
keys: secretKeys,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
consola.success('Environment variables and secrets unset successfully.');
|
|
97
|
+
},
|
|
98
|
+
});
|