@capawesome/cli 4.4.0 → 4.5.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 +15 -0
- package/dist/commands/apps/builds/create.js +29 -4
- package/dist/commands/apps/certificates/create.js +186 -0
- package/dist/commands/apps/certificates/delete.js +79 -0
- package/dist/commands/apps/certificates/get.js +80 -0
- package/dist/commands/apps/certificates/list.js +43 -0
- package/dist/commands/apps/certificates/update.js +50 -0
- package/dist/commands/apps/destinations/create.js +312 -0
- package/dist/commands/apps/destinations/delete.js +75 -0
- package/dist/commands/apps/destinations/get.js +76 -0
- package/dist/commands/apps/destinations/list.js +41 -0
- package/dist/commands/apps/destinations/update.js +69 -0
- package/dist/commands/apps/liveupdates/generate-signing-key.js +3 -0
- package/dist/index.js +15 -5
- package/dist/services/app-apple-api-keys.js +22 -0
- package/dist/services/app-certificates.js +65 -4
- package/dist/services/app-destinations.js +46 -4
- package/dist/services/app-google-service-account-keys.js +22 -0
- package/dist/services/app-provisioning-profiles.js +33 -0
- package/dist/services/apps.js +3 -0
- package/dist/services/update.js +7 -2
- package/dist/types/app-apple-api-key.js +1 -0
- package/dist/types/app-google-service-account-key.js +1 -0
- package/dist/types/app-provisioning-profile.js +1 -0
- package/dist/types/index.js +5 -0
- package/dist/utils/prompt.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import appAppleApiKeysService from '../../../services/app-apple-api-keys.js';
|
|
2
|
+
import appDestinationsService from '../../../services/app-destinations.js';
|
|
3
|
+
import appGoogleServiceAccountKeysService from '../../../services/app-google-service-account-keys.js';
|
|
4
|
+
import { withAuth } from '../../../utils/auth.js';
|
|
5
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
6
|
+
import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
|
|
7
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
8
|
+
import consola from 'consola';
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
export default defineCommand({
|
|
13
|
+
description: 'Create a new app destination.',
|
|
14
|
+
options: defineOptions(z.object({
|
|
15
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
16
|
+
name: z.string().optional().describe('Name of the destination.'),
|
|
17
|
+
platform: z.enum(['android', 'ios']).optional().describe('Platform of the destination (android, ios).'),
|
|
18
|
+
appleId: z.string().optional().describe('Apple ID for the destination.'),
|
|
19
|
+
appleAppId: z.string().optional().describe('Apple App ID for the destination.'),
|
|
20
|
+
appleTeamId: z.string().optional().describe('Apple Team ID for the destination.'),
|
|
21
|
+
appleAppPassword: z.string().optional().describe('Apple app-specific password for the destination.'),
|
|
22
|
+
appleApiKeyFile: z.string().optional().describe('Path to the Apple API key (.p8) file.'),
|
|
23
|
+
appleIssuerId: z.string().optional().describe('Apple Issuer ID for the destination.'),
|
|
24
|
+
androidPackageName: z.string().optional().describe('Android package name for the destination.'),
|
|
25
|
+
androidBuildArtifactType: z.enum(['aab', 'apk']).optional().describe('Android build artifact type (aab, apk).'),
|
|
26
|
+
androidReleaseStatus: z
|
|
27
|
+
.enum(['completed', 'draft'])
|
|
28
|
+
.optional()
|
|
29
|
+
.describe('Android release status (completed, draft).'),
|
|
30
|
+
googleServiceAccountKeyFile: z.string().optional().describe('Path to the Google service account key JSON file.'),
|
|
31
|
+
googlePlayTrack: z.string().optional().describe('Google Play track for the destination.'),
|
|
32
|
+
})),
|
|
33
|
+
action: withAuth(async (options, args) => {
|
|
34
|
+
let { appId, name, platform, appleId, appleAppId, appleTeamId, appleAppPassword, appleApiKeyFile, appleIssuerId, androidPackageName, androidBuildArtifactType, androidReleaseStatus, googleServiceAccountKeyFile, googlePlayTrack, } = options;
|
|
35
|
+
let appleApiKeyId;
|
|
36
|
+
let appAppleApiKeyId;
|
|
37
|
+
let appGoogleServiceAccountKeyId;
|
|
38
|
+
// 1. Select organization and app
|
|
39
|
+
if (!appId) {
|
|
40
|
+
if (!isInteractive()) {
|
|
41
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
const organizationId = await promptOrganizationSelection();
|
|
45
|
+
appId = await promptAppSelection(organizationId);
|
|
46
|
+
}
|
|
47
|
+
// 2. Enter destination name
|
|
48
|
+
if (!name) {
|
|
49
|
+
if (!isInteractive()) {
|
|
50
|
+
consola.error('You must provide the destination name when running in non-interactive environment.');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
name = await prompt('Enter the name of the destination:', { type: 'text' });
|
|
54
|
+
if (!name) {
|
|
55
|
+
consola.error('You must provide a destination name.');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// 3. Select platform
|
|
60
|
+
if (!platform) {
|
|
61
|
+
if (!isInteractive()) {
|
|
62
|
+
consola.error('You must provide the platform when running in non-interactive environment.');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
66
|
+
platform = await prompt('Select the platform:', {
|
|
67
|
+
type: 'select',
|
|
68
|
+
options: [
|
|
69
|
+
{ label: 'Android', value: 'android' },
|
|
70
|
+
{ label: 'iOS', value: 'ios' },
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
if (!platform) {
|
|
74
|
+
consola.error('You must select a platform.');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (platform === 'android') {
|
|
79
|
+
// 4. Ask for track
|
|
80
|
+
if (!googlePlayTrack) {
|
|
81
|
+
if (!isInteractive()) {
|
|
82
|
+
consola.error('You must provide the Google Play track when running in non-interactive environment.');
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
googlePlayTrack = await prompt('Enter the Google Play track:', { type: 'text' });
|
|
86
|
+
if (!googlePlayTrack) {
|
|
87
|
+
consola.error('You must provide a Google Play track.');
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// 5. Ask for package name
|
|
92
|
+
if (!androidPackageName) {
|
|
93
|
+
if (!isInteractive()) {
|
|
94
|
+
consola.error('You must provide the Android package name when running in non-interactive environment.');
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
androidPackageName = await prompt('Enter the Android package name:', { type: 'text' });
|
|
98
|
+
if (!androidPackageName) {
|
|
99
|
+
consola.error('You must provide an Android package name.');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// 6. Ask for publishing format
|
|
104
|
+
if (!androidBuildArtifactType) {
|
|
105
|
+
if (!isInteractive()) {
|
|
106
|
+
consola.error('You must provide the Android build artifact type when running in non-interactive environment.');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
110
|
+
androidBuildArtifactType = await prompt('Select the publishing format:', {
|
|
111
|
+
type: 'select',
|
|
112
|
+
options: [
|
|
113
|
+
{ label: 'AAB', value: 'aab' },
|
|
114
|
+
{ label: 'APK', value: 'apk' },
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
if (!androidBuildArtifactType) {
|
|
118
|
+
consola.error('You must select a publishing format.');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// 7. Ask for release status
|
|
123
|
+
if (!androidReleaseStatus) {
|
|
124
|
+
if (!isInteractive()) {
|
|
125
|
+
consola.error('You must provide the Android release status when running in non-interactive environment.');
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
129
|
+
androidReleaseStatus = await prompt('Select the release status:', {
|
|
130
|
+
type: 'select',
|
|
131
|
+
options: [
|
|
132
|
+
{ label: 'Draft', value: 'draft' },
|
|
133
|
+
{ label: 'Completed', value: 'completed' },
|
|
134
|
+
],
|
|
135
|
+
});
|
|
136
|
+
if (!androidReleaseStatus) {
|
|
137
|
+
consola.error('You must select a release status.');
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// 8. Ask for JSON key path
|
|
142
|
+
if (!googleServiceAccountKeyFile) {
|
|
143
|
+
if (!isInteractive()) {
|
|
144
|
+
consola.error('You must provide the Google service account key file when running in non-interactive environment.');
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
googleServiceAccountKeyFile = await prompt('Enter the path to the Google service account key JSON file:', {
|
|
148
|
+
type: 'text',
|
|
149
|
+
});
|
|
150
|
+
if (!googleServiceAccountKeyFile) {
|
|
151
|
+
consola.error('You must provide a Google service account key file path.');
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Upload Google service account key file
|
|
156
|
+
const buffer = fs.readFileSync(googleServiceAccountKeyFile);
|
|
157
|
+
const fileName = path.basename(googleServiceAccountKeyFile);
|
|
158
|
+
const key = await appGoogleServiceAccountKeysService.create({
|
|
159
|
+
appId,
|
|
160
|
+
buffer,
|
|
161
|
+
fileName,
|
|
162
|
+
});
|
|
163
|
+
appGoogleServiceAccountKeyId = key.id;
|
|
164
|
+
}
|
|
165
|
+
if (platform === 'ios') {
|
|
166
|
+
// 9. Ask for authentication method
|
|
167
|
+
let authMethod;
|
|
168
|
+
if (appleApiKeyFile || appleIssuerId) {
|
|
169
|
+
authMethod = 'apiKey';
|
|
170
|
+
}
|
|
171
|
+
else if (appleId || appleAppId || appleAppPassword) {
|
|
172
|
+
authMethod = 'password';
|
|
173
|
+
}
|
|
174
|
+
else if (isInteractive()) {
|
|
175
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
176
|
+
authMethod = await prompt('Select the authentication method:', {
|
|
177
|
+
type: 'select',
|
|
178
|
+
options: [
|
|
179
|
+
{ label: 'API Key', value: 'apiKey' },
|
|
180
|
+
{ label: 'Password', value: 'password' },
|
|
181
|
+
],
|
|
182
|
+
});
|
|
183
|
+
if (!authMethod) {
|
|
184
|
+
consola.error('You must select an authentication method.');
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
consola.error('You must provide authentication options when running in non-interactive environment. Either pass --apple-api-key-file and --apple-issuer-id for API Key authentication or --apple-id, --apple-app-id, and --apple-app-password for Password authentication.');
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
if (authMethod === 'apiKey') {
|
|
193
|
+
// 10. Ask for p8 key file path
|
|
194
|
+
if (!appleApiKeyFile) {
|
|
195
|
+
if (!isInteractive()) {
|
|
196
|
+
consola.error('You must provide the Apple API key file when running in non-interactive environment.');
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
appleApiKeyFile = await prompt('Enter the path to the Apple API key (.p8) file:', {
|
|
200
|
+
type: 'text',
|
|
201
|
+
});
|
|
202
|
+
if (!appleApiKeyFile) {
|
|
203
|
+
consola.error('You must provide an Apple API key file path.');
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Upload Apple API key file
|
|
208
|
+
const buffer = fs.readFileSync(appleApiKeyFile);
|
|
209
|
+
const fileName = path.basename(appleApiKeyFile);
|
|
210
|
+
const key = await appAppleApiKeysService.create({
|
|
211
|
+
appId,
|
|
212
|
+
buffer,
|
|
213
|
+
fileName,
|
|
214
|
+
});
|
|
215
|
+
appAppleApiKeyId = key.id;
|
|
216
|
+
// 11. Ask for key ID
|
|
217
|
+
if (!appleApiKeyId) {
|
|
218
|
+
if (!isInteractive()) {
|
|
219
|
+
consola.error('You must provide the Apple API Key ID when running in non-interactive environment.');
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
appleApiKeyId = await prompt('Enter the Apple API Key ID:', { type: 'text' });
|
|
223
|
+
if (!appleApiKeyId) {
|
|
224
|
+
consola.error('You must provide an Apple API Key ID.');
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
// 12. Ask for issuer ID
|
|
229
|
+
if (!appleIssuerId) {
|
|
230
|
+
if (!isInteractive()) {
|
|
231
|
+
consola.error('You must provide the Apple Issuer ID when running in non-interactive environment.');
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
appleIssuerId = await prompt('Enter the Apple Issuer ID:', { type: 'text' });
|
|
235
|
+
if (!appleIssuerId) {
|
|
236
|
+
consola.error('You must provide an Apple Issuer ID.');
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
else if (authMethod === 'password') {
|
|
242
|
+
// 13. Ask for Apple ID
|
|
243
|
+
if (!appleId) {
|
|
244
|
+
if (!isInteractive()) {
|
|
245
|
+
consola.error('You must provide the Apple ID when running in non-interactive environment.');
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
appleId = await prompt('Enter the Apple ID:', { type: 'text' });
|
|
249
|
+
if (!appleId) {
|
|
250
|
+
consola.error('You must provide an Apple ID.');
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// 14. Ask for Apple App ID
|
|
255
|
+
if (!appleAppId) {
|
|
256
|
+
if (!isInteractive()) {
|
|
257
|
+
consola.error('You must provide the Apple App ID when running in non-interactive environment.');
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
appleAppId = await prompt('Enter the Apple App ID:', { type: 'text' });
|
|
261
|
+
if (!appleAppId) {
|
|
262
|
+
consola.error('You must provide an Apple App ID.');
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// 15. Ask for App-specific password
|
|
267
|
+
if (!appleAppPassword) {
|
|
268
|
+
if (!isInteractive()) {
|
|
269
|
+
consola.error('You must provide the App-specific password when running in non-interactive environment.');
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
appleAppPassword = await prompt('Enter the App-specific password:', { type: 'text' });
|
|
273
|
+
if (!appleAppPassword) {
|
|
274
|
+
consola.error('You must provide an App-specific password.');
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// 16. Ask for team ID
|
|
280
|
+
if (!appleTeamId) {
|
|
281
|
+
if (!isInteractive()) {
|
|
282
|
+
consola.error('You must provide the Apple Team ID when running in non-interactive environment.');
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
appleTeamId = await prompt('Enter the Apple Team ID:', { type: 'text' });
|
|
286
|
+
if (!appleTeamId) {
|
|
287
|
+
consola.error('You must provide an Apple Team ID.');
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const response = await appDestinationsService.create({
|
|
293
|
+
appId,
|
|
294
|
+
name,
|
|
295
|
+
platform: platform,
|
|
296
|
+
appleId,
|
|
297
|
+
appleAppId,
|
|
298
|
+
appleTeamId,
|
|
299
|
+
appleAppPassword,
|
|
300
|
+
appleApiKeyId,
|
|
301
|
+
appleIssuerId,
|
|
302
|
+
appAppleApiKeyId,
|
|
303
|
+
androidPackageName,
|
|
304
|
+
androidBuildArtifactType,
|
|
305
|
+
androidReleaseStatus,
|
|
306
|
+
appGoogleServiceAccountKeyId,
|
|
307
|
+
googlePlayTrack,
|
|
308
|
+
});
|
|
309
|
+
consola.info(`Destination ID: ${response.id}`);
|
|
310
|
+
consola.success('Destination created successfully.');
|
|
311
|
+
}),
|
|
312
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import appDestinationsService from '../../../services/app-destinations.js';
|
|
2
|
+
import { withAuth } from '../../../utils/auth.js';
|
|
3
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
4
|
+
import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
|
|
5
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
6
|
+
import consola from 'consola';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
export default defineCommand({
|
|
9
|
+
description: 'Delete an app destination.',
|
|
10
|
+
options: defineOptions(z.object({
|
|
11
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
12
|
+
destinationId: z.string().optional().describe('ID of the destination.'),
|
|
13
|
+
name: z.string().optional().describe('Name of the destination.'),
|
|
14
|
+
platform: z.enum(['android', 'ios']).optional().describe('Platform of the destination (android, ios).'),
|
|
15
|
+
yes: z.boolean().optional().describe('Skip confirmation prompt.'),
|
|
16
|
+
}), { y: 'yes' }),
|
|
17
|
+
action: withAuth(async (options, args) => {
|
|
18
|
+
let { appId, destinationId, name, platform } = options;
|
|
19
|
+
if (!appId) {
|
|
20
|
+
if (!isInteractive()) {
|
|
21
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const organizationId = await promptOrganizationSelection();
|
|
25
|
+
appId = await promptAppSelection(organizationId);
|
|
26
|
+
}
|
|
27
|
+
if (!destinationId) {
|
|
28
|
+
if (name && platform) {
|
|
29
|
+
const destinations = await appDestinationsService.findAll({ appId, name, platform });
|
|
30
|
+
const firstDestination = destinations[0];
|
|
31
|
+
if (!firstDestination) {
|
|
32
|
+
consola.error(`No destination found with name '${name}' and platform '${platform}'.`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
destinationId = firstDestination.id;
|
|
36
|
+
}
|
|
37
|
+
else if (isInteractive()) {
|
|
38
|
+
if (!platform) {
|
|
39
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
40
|
+
platform = await prompt('Select the platform:', {
|
|
41
|
+
type: 'select',
|
|
42
|
+
options: [
|
|
43
|
+
{ label: 'Android', value: 'android' },
|
|
44
|
+
{ label: 'iOS', value: 'ios' },
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const destinations = await appDestinationsService.findAll({ appId, platform });
|
|
49
|
+
if (!destinations.length) {
|
|
50
|
+
consola.error('No destinations found for this app. Create one first.');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
54
|
+
destinationId = await prompt('Select the destination to delete:', {
|
|
55
|
+
type: 'select',
|
|
56
|
+
options: destinations.map((dest) => ({ label: dest.name, value: dest.id })),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
consola.error('You must provide the destination ID or --name and --platform when running in non-interactive environment.');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (!options.yes && isInteractive()) {
|
|
65
|
+
const confirmed = await prompt('Are you sure you want to delete this destination?', {
|
|
66
|
+
type: 'confirm',
|
|
67
|
+
});
|
|
68
|
+
if (!confirmed) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
await appDestinationsService.delete({ appId, destinationId });
|
|
73
|
+
consola.success('Destination deleted successfully.');
|
|
74
|
+
}),
|
|
75
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import appDestinationsService from '../../../services/app-destinations.js';
|
|
2
|
+
import { withAuth } from '../../../utils/auth.js';
|
|
3
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
4
|
+
import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
|
|
5
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
6
|
+
import consola from 'consola';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
export default defineCommand({
|
|
9
|
+
description: 'Get an existing app destination.',
|
|
10
|
+
options: defineOptions(z.object({
|
|
11
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
12
|
+
destinationId: z.string().optional().describe('ID of the destination.'),
|
|
13
|
+
json: z.boolean().optional().describe('Output in JSON format.'),
|
|
14
|
+
name: z.string().optional().describe('Name of the destination.'),
|
|
15
|
+
platform: z.enum(['android', 'ios']).optional().describe('Platform of the destination (android, ios).'),
|
|
16
|
+
})),
|
|
17
|
+
action: withAuth(async (options, args) => {
|
|
18
|
+
let { appId, destinationId, name, platform } = options;
|
|
19
|
+
if (!appId) {
|
|
20
|
+
if (!isInteractive()) {
|
|
21
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const organizationId = await promptOrganizationSelection();
|
|
25
|
+
appId = await promptAppSelection(organizationId);
|
|
26
|
+
}
|
|
27
|
+
if (!destinationId) {
|
|
28
|
+
if (name && platform) {
|
|
29
|
+
const destinations = await appDestinationsService.findAll({ appId, name, platform });
|
|
30
|
+
const firstDestination = destinations[0];
|
|
31
|
+
if (!firstDestination) {
|
|
32
|
+
consola.error(`No destination found with name '${name}' and platform '${platform}'.`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
destinationId = firstDestination.id;
|
|
36
|
+
}
|
|
37
|
+
else if (isInteractive()) {
|
|
38
|
+
if (!platform) {
|
|
39
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
40
|
+
platform = await prompt('Select the platform:', {
|
|
41
|
+
type: 'select',
|
|
42
|
+
options: [
|
|
43
|
+
{ label: 'Android', value: 'android' },
|
|
44
|
+
{ label: 'iOS', value: 'ios' },
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const destinations = await appDestinationsService.findAll({ appId, platform });
|
|
49
|
+
if (!destinations.length) {
|
|
50
|
+
consola.error('No destinations found for this app. Create one first.');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
54
|
+
destinationId = await prompt('Select the destination:', {
|
|
55
|
+
type: 'select',
|
|
56
|
+
options: destinations.map((dest) => ({ label: dest.name, value: dest.id })),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
consola.error('You must provide the destination ID or --name and --platform when running in non-interactive environment.');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const destination = await appDestinationsService.findOneById({
|
|
65
|
+
appId,
|
|
66
|
+
destinationId,
|
|
67
|
+
});
|
|
68
|
+
if (options.json) {
|
|
69
|
+
console.log(JSON.stringify(destination, null, 2));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.table(destination);
|
|
73
|
+
consola.success('Destination retrieved successfully.');
|
|
74
|
+
}
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import appDestinationsService from '../../../services/app-destinations.js';
|
|
2
|
+
import { withAuth } from '../../../utils/auth.js';
|
|
3
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
4
|
+
import { promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
|
|
5
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
6
|
+
import consola from 'consola';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
export default defineCommand({
|
|
9
|
+
description: 'Retrieve a list of existing app destinations.',
|
|
10
|
+
options: defineOptions(z.object({
|
|
11
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
12
|
+
json: z.boolean().optional().describe('Output in JSON format.'),
|
|
13
|
+
limit: z.coerce.number().optional().describe('Limit for pagination.'),
|
|
14
|
+
offset: z.coerce.number().optional().describe('Offset for pagination.'),
|
|
15
|
+
platform: z.enum(['android', 'ios']).optional().describe('Filter by platform.'),
|
|
16
|
+
})),
|
|
17
|
+
action: withAuth(async (options, args) => {
|
|
18
|
+
let { appId, json, limit, offset, platform } = options;
|
|
19
|
+
if (!appId) {
|
|
20
|
+
if (!isInteractive()) {
|
|
21
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const organizationId = await promptOrganizationSelection();
|
|
25
|
+
appId = await promptAppSelection(organizationId);
|
|
26
|
+
}
|
|
27
|
+
const destinations = await appDestinationsService.findAll({
|
|
28
|
+
appId,
|
|
29
|
+
limit,
|
|
30
|
+
offset,
|
|
31
|
+
platform,
|
|
32
|
+
});
|
|
33
|
+
if (json) {
|
|
34
|
+
console.log(JSON.stringify(destinations, null, 2));
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.table(destinations);
|
|
38
|
+
consola.success('Destinations retrieved successfully.');
|
|
39
|
+
}
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import appDestinationsService from '../../../services/app-destinations.js';
|
|
2
|
+
import { withAuth } from '../../../utils/auth.js';
|
|
3
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
4
|
+
import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
|
|
5
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
6
|
+
import consola from 'consola';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
export default defineCommand({
|
|
9
|
+
description: 'Update an existing app destination.',
|
|
10
|
+
options: defineOptions(z.object({
|
|
11
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
12
|
+
destinationId: z.string().optional().describe('ID of the destination.'),
|
|
13
|
+
name: z.string().optional().describe('Name of the destination.'),
|
|
14
|
+
appleId: z.string().optional().describe('Apple ID for the destination.'),
|
|
15
|
+
appleAppId: z.string().optional().describe('Apple App ID for the destination.'),
|
|
16
|
+
appleTeamId: z.string().optional().describe('Apple Team ID for the destination.'),
|
|
17
|
+
appleAppPassword: z.string().optional().describe('Apple app-specific password for the destination.'),
|
|
18
|
+
appleApiKeyId: z.string().optional().describe('Apple API Key ID for the destination.'),
|
|
19
|
+
appleIssuerId: z.string().optional().describe('Apple Issuer ID for the destination.'),
|
|
20
|
+
appAppleApiKeyId: z.string().optional().describe('App Apple API Key ID for the destination.'),
|
|
21
|
+
androidPackageName: z.string().optional().describe('Android package name for the destination.'),
|
|
22
|
+
androidBuildArtifactType: z.enum(['aab', 'apk']).optional().describe('Android build artifact type (aab, apk).'),
|
|
23
|
+
androidReleaseStatus: z
|
|
24
|
+
.enum(['completed', 'draft'])
|
|
25
|
+
.optional()
|
|
26
|
+
.describe('Android release status (completed, draft).'),
|
|
27
|
+
appGoogleServiceAccountKeyId: z
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('App Google Service Account Key ID for the destination.'),
|
|
31
|
+
googlePlayTrack: z.string().optional().describe('Google Play track for the destination.'),
|
|
32
|
+
})),
|
|
33
|
+
action: withAuth(async (options, args) => {
|
|
34
|
+
let { appId, destinationId, name, appleId, appleAppId, appleTeamId, appleAppPassword, appleApiKeyId, appleIssuerId, appAppleApiKeyId, androidPackageName, androidBuildArtifactType, androidReleaseStatus, appGoogleServiceAccountKeyId, googlePlayTrack, } = options;
|
|
35
|
+
if (!appId) {
|
|
36
|
+
if (!isInteractive()) {
|
|
37
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
const organizationId = await promptOrganizationSelection();
|
|
41
|
+
appId = await promptAppSelection(organizationId);
|
|
42
|
+
}
|
|
43
|
+
if (!destinationId) {
|
|
44
|
+
if (!isInteractive()) {
|
|
45
|
+
consola.error('You must provide the destination ID when running in non-interactive environment.');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
destinationId = await prompt('Enter the destination ID:', { type: 'text' });
|
|
49
|
+
}
|
|
50
|
+
await appDestinationsService.update({
|
|
51
|
+
appId,
|
|
52
|
+
destinationId,
|
|
53
|
+
name,
|
|
54
|
+
appleId,
|
|
55
|
+
appleAppId,
|
|
56
|
+
appleTeamId,
|
|
57
|
+
appleAppPassword,
|
|
58
|
+
appleApiKeyId,
|
|
59
|
+
appleIssuerId,
|
|
60
|
+
appAppleApiKeyId,
|
|
61
|
+
androidPackageName,
|
|
62
|
+
androidBuildArtifactType,
|
|
63
|
+
androidReleaseStatus,
|
|
64
|
+
appGoogleServiceAccountKeyId,
|
|
65
|
+
googlePlayTrack,
|
|
66
|
+
});
|
|
67
|
+
consola.success('Destination updated successfully.');
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
@@ -43,6 +43,9 @@ export default defineCommand({
|
|
|
43
43
|
// Resolve absolute paths
|
|
44
44
|
const absolutePublicKeyPath = pathModule.resolve(process.cwd(), publicKeyPath);
|
|
45
45
|
const absolutePrivateKeyPath = pathModule.resolve(process.cwd(), privateKeyPath);
|
|
46
|
+
// Ensure parent directories exist
|
|
47
|
+
await fs.mkdir(pathModule.dirname(absolutePublicKeyPath), { recursive: true });
|
|
48
|
+
await fs.mkdir(pathModule.dirname(absolutePrivateKeyPath), { recursive: true });
|
|
46
49
|
// Write the keys to files
|
|
47
50
|
await fs.writeFile(absolutePublicKeyPath, publicKey, 'utf8');
|
|
48
51
|
await fs.writeFile(absolutePrivateKeyPath, privateKey, 'utf8');
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,11 @@ const config = defineConfig({
|
|
|
30
30
|
'apps:bundles:create': await import('./commands/apps/bundles/create.js').then((mod) => mod.default),
|
|
31
31
|
'apps:bundles:delete': await import('./commands/apps/bundles/delete.js').then((mod) => mod.default),
|
|
32
32
|
'apps:bundles:update': await import('./commands/apps/bundles/update.js').then((mod) => mod.default),
|
|
33
|
+
'apps:certificates:create': await import('./commands/apps/certificates/create.js').then((mod) => mod.default),
|
|
34
|
+
'apps:certificates:delete': await import('./commands/apps/certificates/delete.js').then((mod) => mod.default),
|
|
35
|
+
'apps:certificates:get': await import('./commands/apps/certificates/get.js').then((mod) => mod.default),
|
|
36
|
+
'apps:certificates:list': await import('./commands/apps/certificates/list.js').then((mod) => mod.default),
|
|
37
|
+
'apps:certificates:update': await import('./commands/apps/certificates/update.js').then((mod) => mod.default),
|
|
33
38
|
'apps:channels:create': await import('./commands/apps/channels/create.js').then((mod) => mod.default),
|
|
34
39
|
'apps:channels:delete': await import('./commands/apps/channels/delete.js').then((mod) => mod.default),
|
|
35
40
|
'apps:channels:get': await import('./commands/apps/channels/get.js').then((mod) => mod.default),
|
|
@@ -37,17 +42,22 @@ const config = defineConfig({
|
|
|
37
42
|
'apps:channels:pause': await import('./commands/apps/channels/pause.js').then((mod) => mod.default),
|
|
38
43
|
'apps:channels:resume': await import('./commands/apps/channels/resume.js').then((mod) => mod.default),
|
|
39
44
|
'apps:channels:update': await import('./commands/apps/channels/update.js').then((mod) => mod.default),
|
|
40
|
-
'apps:environments:create': await import('./commands/apps/environments/create.js').then((mod) => mod.default),
|
|
41
|
-
'apps:environments:delete': await import('./commands/apps/environments/delete.js').then((mod) => mod.default),
|
|
42
|
-
'apps:environments:list': await import('./commands/apps/environments/list.js').then((mod) => mod.default),
|
|
43
|
-
'apps:environments:set': await import('./commands/apps/environments/set.js').then((mod) => mod.default),
|
|
44
|
-
'apps:environments:unset': await import('./commands/apps/environments/unset.js').then((mod) => mod.default),
|
|
45
45
|
'apps:deployments:create': await import('./commands/apps/deployments/create.js').then((mod) => mod.default),
|
|
46
46
|
'apps:deployments:cancel': await import('./commands/apps/deployments/cancel.js').then((mod) => mod.default),
|
|
47
47
|
'apps:deployments:logs': await import('./commands/apps/deployments/logs.js').then((mod) => mod.default),
|
|
48
|
+
'apps:destinations:create': await import('./commands/apps/destinations/create.js').then((mod) => mod.default),
|
|
49
|
+
'apps:destinations:delete': await import('./commands/apps/destinations/delete.js').then((mod) => mod.default),
|
|
50
|
+
'apps:destinations:get': await import('./commands/apps/destinations/get.js').then((mod) => mod.default),
|
|
51
|
+
'apps:destinations:list': await import('./commands/apps/destinations/list.js').then((mod) => mod.default),
|
|
52
|
+
'apps:destinations:update': await import('./commands/apps/destinations/update.js').then((mod) => mod.default),
|
|
48
53
|
'apps:devices:delete': await import('./commands/apps/devices/delete.js').then((mod) => mod.default),
|
|
49
54
|
'apps:devices:forcechannel': await import('./commands/apps/devices/forcechannel.js').then((mod) => mod.default),
|
|
50
55
|
'apps:devices:unforcechannel': await import('./commands/apps/devices/unforcechannel.js').then((mod) => mod.default),
|
|
56
|
+
'apps:environments:create': await import('./commands/apps/environments/create.js').then((mod) => mod.default),
|
|
57
|
+
'apps:environments:delete': await import('./commands/apps/environments/delete.js').then((mod) => mod.default),
|
|
58
|
+
'apps:environments:list': await import('./commands/apps/environments/list.js').then((mod) => mod.default),
|
|
59
|
+
'apps:environments:set': await import('./commands/apps/environments/set.js').then((mod) => mod.default),
|
|
60
|
+
'apps:environments:unset': await import('./commands/apps/environments/unset.js').then((mod) => mod.default),
|
|
51
61
|
'apps:liveupdates:bundle': await import('./commands/apps/liveupdates/bundle.js').then((mod) => mod.default),
|
|
52
62
|
'apps:liveupdates:generatesigningkey': await import('./commands/apps/liveupdates/generate-signing-key.js').then((mod) => mod.default),
|
|
53
63
|
'apps:liveupdates:rollback': await import('./commands/apps/liveupdates/rollback.js').then((mod) => mod.default),
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import authorizationService from '../services/authorization-service.js';
|
|
2
|
+
import httpClient from '../utils/http-client.js';
|
|
3
|
+
import FormData from 'form-data';
|
|
4
|
+
class AppAppleApiKeysServiceImpl {
|
|
5
|
+
httpClient;
|
|
6
|
+
constructor(httpClient) {
|
|
7
|
+
this.httpClient = httpClient;
|
|
8
|
+
}
|
|
9
|
+
async create(dto) {
|
|
10
|
+
const formData = new FormData();
|
|
11
|
+
formData.append('file', dto.buffer, { filename: dto.fileName });
|
|
12
|
+
const response = await this.httpClient.post(`/v1/apps/${dto.appId}/apple-api-keys`, formData, {
|
|
13
|
+
headers: {
|
|
14
|
+
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
|
|
15
|
+
...formData.getHeaders(),
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
return response.data;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const appAppleApiKeysService = new AppAppleApiKeysServiceImpl(httpClient);
|
|
22
|
+
export default appAppleApiKeysService;
|