@capawesome/cli 4.4.0 → 4.6.0-dev.0108a83.1774286472
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 +22 -0
- package/dist/commands/apps/builds/create.js +75 -8
- package/dist/commands/apps/certificates/create.js +170 -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 +52 -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/devices/probe.js +70 -0
- package/dist/commands/apps/liveupdates/bundle.js +6 -1
- package/dist/commands/apps/liveupdates/generate-manifest.js +6 -1
- package/dist/commands/apps/liveupdates/generate-signing-key.js +3 -0
- package/dist/commands/apps/liveupdates/upload.js +8 -1
- package/dist/index.js +16 -5
- package/dist/services/app-apple-api-keys.js +22 -0
- package/dist/services/app-build-sources.js +112 -0
- package/dist/services/app-certificates.js +64 -4
- package/dist/services/app-destinations.js +46 -4
- package/dist/services/app-devices.js +36 -0
- 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-build-source.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 +6 -0
- package/dist/utils/file.js +4 -0
- package/dist/utils/prompt.js +1 -1
- package/dist/utils/zip.js +19 -2
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [4.6.0](https://github.com/capawesome-team/cli/compare/v4.5.0...v4.6.0) (2026-03-18)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* add `apps:devices:probe` command ([#129](https://github.com/capawesome-team/cli/issues/129)) ([33607cd](https://github.com/capawesome-team/cli/commit/33607cdb2e65e26c48d114d694b876db3762b8ec))
|
|
11
|
+
|
|
12
|
+
## [4.5.0](https://github.com/capawesome-team/cli/compare/v4.4.0...v4.5.0) (2026-03-15)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* add certificate and destination CRUD commands ([#126](https://github.com/capawesome-team/cli/issues/126)) ([2d5e3cf](https://github.com/capawesome-team/cli/commit/2d5e3cfc80518fc1237dbc926a57c4a2eddf2b31))
|
|
18
|
+
* **apps:builds:create:** add `--channel` and `--destination` options ([#127](https://github.com/capawesome-team/cli/issues/127)) ([baab841](https://github.com/capawesome-team/cli/commit/baab841d330c7bf230dbbf8e0819f3aaf248867b))
|
|
19
|
+
* **update:** add dev version detection in update check ([#128](https://github.com/capawesome-team/cli/issues/128)) ([5004095](https://github.com/capawesome-team/cli/commit/50040958cd2a873ff296981b0950b3a799505a93))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* **apps:builds:create:** allow certificate selection for web platform ([a038251](https://github.com/capawesome-team/cli/commit/a03825128b8380f86dcf85e8d3507b3a41d9f775))
|
|
25
|
+
* create parent directories before writing signing key files ([#125](https://github.com/capawesome-team/cli/issues/125)) ([e152606](https://github.com/capawesome-team/cli/commit/e152606d1d9c1758e922379e5bbda0764261c711))
|
|
26
|
+
|
|
5
27
|
## [4.4.0](https://github.com/capawesome-team/cli/compare/v4.3.0...v4.4.0) (2026-03-13)
|
|
6
28
|
|
|
7
29
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DEFAULT_CONSOLE_BASE_URL } from '../../../config/consts.js';
|
|
2
|
+
import appBuildSourcesService from '../../../services/app-build-sources.js';
|
|
2
3
|
import appBuildsService from '../../../services/app-builds.js';
|
|
3
4
|
import appCertificatesService from '../../../services/app-certificates.js';
|
|
4
5
|
import appEnvironmentsService from '../../../services/app-environments.js';
|
|
@@ -7,6 +8,7 @@ import { withAuth } from '../../../utils/auth.js';
|
|
|
7
8
|
import { isInteractive } from '../../../utils/environment.js';
|
|
8
9
|
import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
|
|
9
10
|
import { wait } from '../../../utils/wait.js';
|
|
11
|
+
import zip from '../../../utils/zip.js';
|
|
10
12
|
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
11
13
|
import consola from 'consola';
|
|
12
14
|
import fs from 'fs/promises';
|
|
@@ -32,6 +34,8 @@ export default defineCommand({
|
|
|
32
34
|
.optional()
|
|
33
35
|
.describe('App ID to create the build for.'),
|
|
34
36
|
certificate: z.string().optional().describe('The name of the certificate to use for the build.'),
|
|
37
|
+
channel: z.string().optional().describe('The name of the channel to deploy to (Web only).'),
|
|
38
|
+
destination: z.string().optional().describe('The name of the destination to deploy to (Android/iOS only).'),
|
|
35
39
|
detached: z
|
|
36
40
|
.boolean()
|
|
37
41
|
.optional()
|
|
@@ -43,6 +47,7 @@ export default defineCommand({
|
|
|
43
47
|
.optional()
|
|
44
48
|
.describe('Download the generated IPA file (iOS only). Optionally provide a file path.'),
|
|
45
49
|
json: z.boolean().optional().describe('Output in JSON format.'),
|
|
50
|
+
path: z.string().optional().describe('Path to local source files to upload.'),
|
|
46
51
|
platform: z
|
|
47
52
|
.enum(['ios', 'android', 'web'], {
|
|
48
53
|
message: 'Platform must be either `ios`, `android`, or `web`.',
|
|
@@ -66,12 +71,42 @@ export default defineCommand({
|
|
|
66
71
|
yes: z.boolean().optional().describe('Skip confirmation prompts.'),
|
|
67
72
|
}), { y: 'yes' }),
|
|
68
73
|
action: withAuth(async (options) => {
|
|
69
|
-
let { appId, platform, type, gitRef, environment, certificate, json, stack } = options;
|
|
74
|
+
let { appId, platform, type, gitRef, environment, certificate, json, stack, path: sourcePath } = options;
|
|
70
75
|
// Validate that detached flag cannot be used with artifact flags
|
|
71
76
|
if (options.detached && (options.apk || options.aab || options.ipa || options.zip)) {
|
|
72
77
|
consola.error('The --detached flag cannot be used with --apk, --aab, --ipa, or --zip flags.');
|
|
73
78
|
process.exit(1);
|
|
74
79
|
}
|
|
80
|
+
// Validate that detached flag cannot be used with channel or destination
|
|
81
|
+
if (options.detached && (options.channel || options.destination)) {
|
|
82
|
+
consola.error('The --detached flag cannot be used with --channel or --destination flags.');
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
// Validate that channel and destination cannot be used together
|
|
86
|
+
if (options.channel && options.destination) {
|
|
87
|
+
consola.error('The --channel and --destination flags cannot be used together.');
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
// Validate that path and gitRef cannot be used together
|
|
91
|
+
if (sourcePath && gitRef) {
|
|
92
|
+
consola.error('The --path and --git-ref flags cannot be used together.');
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
// Validate path if provided
|
|
96
|
+
if (sourcePath) {
|
|
97
|
+
const resolvedPath = path.resolve(sourcePath);
|
|
98
|
+
const stat = await fs.stat(resolvedPath).catch(() => null);
|
|
99
|
+
if (!stat || !stat.isDirectory()) {
|
|
100
|
+
consola.error('The --path must point to an existing directory.');
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
const packageJsonPath = path.join(resolvedPath, 'package.json');
|
|
104
|
+
const packageJsonStat = await fs.stat(packageJsonPath).catch(() => null);
|
|
105
|
+
if (!packageJsonStat || !packageJsonStat.isFile()) {
|
|
106
|
+
consola.error('The directory specified by --path must contain a package.json file.');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
75
110
|
// Prompt for app ID if not provided
|
|
76
111
|
if (!appId) {
|
|
77
112
|
if (!isInteractive()) {
|
|
@@ -101,10 +136,10 @@ export default defineCommand({
|
|
|
101
136
|
process.exit(1);
|
|
102
137
|
}
|
|
103
138
|
}
|
|
104
|
-
// Prompt for git ref if not provided
|
|
105
|
-
if (!gitRef) {
|
|
139
|
+
// Prompt for git ref if not provided and no path specified
|
|
140
|
+
if (!sourcePath && !gitRef) {
|
|
106
141
|
if (!isInteractive()) {
|
|
107
|
-
consola.error('You must provide a git ref when running in non-interactive environment.');
|
|
142
|
+
consola.error('You must provide a git ref or path when running in non-interactive environment.');
|
|
108
143
|
process.exit(1);
|
|
109
144
|
}
|
|
110
145
|
gitRef = await prompt('Enter the Git reference (branch, tag, or commit SHA):', {
|
|
@@ -133,6 +168,16 @@ export default defineCommand({
|
|
|
133
168
|
consola.error(`Invalid build type for Android. Supported values are: ${ANDROID_BUILD_TYPES.map((t) => `\`${t}\``).join(', ')}.`);
|
|
134
169
|
process.exit(1);
|
|
135
170
|
}
|
|
171
|
+
// Validate that channel is only used with web platform
|
|
172
|
+
if (options.channel && platform !== 'web') {
|
|
173
|
+
consola.error('The --channel flag can only be used with the web platform.');
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
// Validate that destination is only used with non-web platforms
|
|
177
|
+
if (options.destination && platform === 'web') {
|
|
178
|
+
consola.error('The --destination flag cannot be used with the web platform.');
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
136
181
|
// Prompt for environment if not provided
|
|
137
182
|
if (!environment && !options.yes && isInteractive()) {
|
|
138
183
|
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
@@ -154,8 +199,8 @@ export default defineCommand({
|
|
|
154
199
|
}
|
|
155
200
|
}
|
|
156
201
|
}
|
|
157
|
-
// Prompt for certificate if not provided
|
|
158
|
-
if (!certificate && !options.yes && isInteractive()
|
|
202
|
+
// Prompt for certificate if not provided
|
|
203
|
+
if (!certificate && !options.yes && isInteractive()) {
|
|
159
204
|
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
160
205
|
const selectCertificate = await prompt('Do you want to select a certificate?', {
|
|
161
206
|
type: 'confirm',
|
|
@@ -175,9 +220,28 @@ export default defineCommand({
|
|
|
175
220
|
}
|
|
176
221
|
}
|
|
177
222
|
}
|
|
223
|
+
// Upload source files if path is provided
|
|
224
|
+
let appBuildSourceId;
|
|
225
|
+
if (sourcePath) {
|
|
226
|
+
const resolvedPath = path.resolve(sourcePath);
|
|
227
|
+
consola.start('Zipping source files...');
|
|
228
|
+
const buffer = await zip.zipFolderWithGitignore(resolvedPath);
|
|
229
|
+
consola.start('Uploading source files...');
|
|
230
|
+
const appBuildSource = await appBuildSourcesService.create({
|
|
231
|
+
appId,
|
|
232
|
+
fileSizeInBytes: buffer.byteLength,
|
|
233
|
+
buffer,
|
|
234
|
+
name: 'source.zip',
|
|
235
|
+
}, (currentPart, totalParts) => {
|
|
236
|
+
consola.start(`Uploading source files (${currentPart}/${totalParts})...`);
|
|
237
|
+
});
|
|
238
|
+
appBuildSourceId = appBuildSource.id;
|
|
239
|
+
consola.success('Source files uploaded successfully.');
|
|
240
|
+
}
|
|
178
241
|
// Create the app build
|
|
179
242
|
consola.start('Creating build...');
|
|
180
243
|
const response = await appBuildsService.create({
|
|
244
|
+
appBuildSourceId,
|
|
181
245
|
appCertificateName: certificate,
|
|
182
246
|
appEnvironmentName: environment,
|
|
183
247
|
appId,
|
|
@@ -288,8 +352,7 @@ export default defineCommand({
|
|
|
288
352
|
numberAsString: response.numberAsString,
|
|
289
353
|
}, null, 2));
|
|
290
354
|
}
|
|
291
|
-
|
|
292
|
-
process.exit(0);
|
|
355
|
+
break;
|
|
293
356
|
}
|
|
294
357
|
else if (jobStatus === 'failed') {
|
|
295
358
|
consola.error('Build failed.');
|
|
@@ -331,6 +394,10 @@ export default defineCommand({
|
|
|
331
394
|
consola.success(`Build completed successfully.`);
|
|
332
395
|
}
|
|
333
396
|
}
|
|
397
|
+
// Create deployment if channel or destination is set
|
|
398
|
+
if (options.channel || options.destination) {
|
|
399
|
+
await (await import('../../../commands/apps/deployments/create.js').then((mod) => mod.default)).action({ appId, buildId: response.id, channel: options.channel, destination: options.destination }, undefined);
|
|
400
|
+
}
|
|
334
401
|
}),
|
|
335
402
|
});
|
|
336
403
|
/**
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import appCertificatesService from '../../../services/app-certificates.js';
|
|
2
|
+
import appProvisioningProfilesService from '../../../services/app-provisioning-profiles.js';
|
|
3
|
+
import { withAuth } from '../../../utils/auth.js';
|
|
4
|
+
import { isInteractive } from '../../../utils/environment.js';
|
|
5
|
+
import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
|
|
6
|
+
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
7
|
+
import consola from 'consola';
|
|
8
|
+
import fs from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
export default defineCommand({
|
|
12
|
+
description: 'Create a new app certificate.',
|
|
13
|
+
options: defineOptions(z.object({
|
|
14
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
15
|
+
file: z.string().optional().describe('Path to the certificate file.'),
|
|
16
|
+
keyAlias: z.string().optional().describe('Key alias for the certificate.'),
|
|
17
|
+
keyPassword: z.string().optional().describe('Key password for the certificate.'),
|
|
18
|
+
name: z.string().optional().describe('Name of the certificate.'),
|
|
19
|
+
password: z.string().optional().describe('Password for the certificate.'),
|
|
20
|
+
platform: z
|
|
21
|
+
.enum(['android', 'ios', 'web'])
|
|
22
|
+
.optional()
|
|
23
|
+
.describe('Platform of the certificate (android, ios, web).'),
|
|
24
|
+
provisioningProfile: z
|
|
25
|
+
.array(z.string())
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('Paths to provisioning profile files to upload and link.'),
|
|
28
|
+
type: z
|
|
29
|
+
.enum(['development', 'production'])
|
|
30
|
+
.optional()
|
|
31
|
+
.describe('Type of the certificate (development, production).'),
|
|
32
|
+
yes: z.boolean().optional().describe('Skip confirmation prompts.'),
|
|
33
|
+
}), { y: 'yes' }),
|
|
34
|
+
action: withAuth(async (options, args) => {
|
|
35
|
+
let { appId, file, keyAlias, keyPassword, name, password, platform, provisioningProfile, type } = options;
|
|
36
|
+
// 1. Select organization and app
|
|
37
|
+
if (!appId) {
|
|
38
|
+
if (!isInteractive()) {
|
|
39
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const organizationId = await promptOrganizationSelection();
|
|
43
|
+
appId = await promptAppSelection(organizationId);
|
|
44
|
+
}
|
|
45
|
+
// 2. Enter certificate name
|
|
46
|
+
if (!name) {
|
|
47
|
+
if (!isInteractive()) {
|
|
48
|
+
consola.error('You must provide the certificate name when running in non-interactive environment.');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
name = await prompt('Enter the name of the certificate:', { type: 'text' });
|
|
52
|
+
if (!name) {
|
|
53
|
+
consola.error('You must provide a certificate name.');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// 3. Select platform
|
|
58
|
+
if (!platform) {
|
|
59
|
+
if (!isInteractive()) {
|
|
60
|
+
consola.error('You must provide the platform 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
|
+
platform = await prompt('Select the platform:', {
|
|
65
|
+
type: 'select',
|
|
66
|
+
options: [
|
|
67
|
+
{ label: 'Android', value: 'android' },
|
|
68
|
+
{ label: 'iOS', value: 'ios' },
|
|
69
|
+
{ label: 'Web', value: 'web' },
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
if (!platform) {
|
|
73
|
+
consola.error('You must select a platform.');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// 4. Warn if deprecated --type option is used
|
|
78
|
+
if (type) {
|
|
79
|
+
consola.warn('The --type option is deprecated and will be removed in a future version. The certificate type is now detected automatically.');
|
|
80
|
+
}
|
|
81
|
+
// 5. Enter certificate file path
|
|
82
|
+
if (!file) {
|
|
83
|
+
if (!isInteractive()) {
|
|
84
|
+
consola.error('You must provide a certificate file path when running in non-interactive environment.');
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
file = await prompt('Enter the path to the certificate file:', { type: 'text' });
|
|
88
|
+
if (!file) {
|
|
89
|
+
consola.error('You must provide a certificate file path.');
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// 6. Enter certificate password (required for android/ios)
|
|
94
|
+
if (!password && (platform === 'android' || platform === 'ios')) {
|
|
95
|
+
if (!isInteractive()) {
|
|
96
|
+
consola.error('You must provide the certificate password when running in non-interactive environment.');
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
password = await prompt('Enter the certificate password:', { type: 'text' });
|
|
100
|
+
if (!password) {
|
|
101
|
+
consola.error('You must provide a certificate password.');
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// 7. If Android, ask for key alias (required)
|
|
106
|
+
if (!keyAlias && platform === 'android') {
|
|
107
|
+
if (!isInteractive()) {
|
|
108
|
+
consola.error('You must provide the key alias when running in non-interactive environment.');
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
keyAlias = await prompt('Enter the key alias:', { type: 'text' });
|
|
112
|
+
if (!keyAlias) {
|
|
113
|
+
consola.error('You must provide a key alias.');
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// 8. If Android, ask for key password (optional, skip with --yes)
|
|
118
|
+
if (!keyPassword && platform === 'android' && !options.yes && isInteractive()) {
|
|
119
|
+
keyPassword = await prompt('Enter the key password (leave empty if none):', { type: 'text' });
|
|
120
|
+
if (!keyPassword) {
|
|
121
|
+
keyPassword = undefined;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// 9. If iOS, ask for provisioning profile file path (optional, skip with --yes)
|
|
125
|
+
if (!provisioningProfile && platform === 'ios' && !options.yes && isInteractive()) {
|
|
126
|
+
const profilePath = await prompt('Enter the path to the provisioning profile file (leave empty to skip):', {
|
|
127
|
+
type: 'text',
|
|
128
|
+
});
|
|
129
|
+
if (profilePath) {
|
|
130
|
+
provisioningProfile = [profilePath];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const buffer = fs.readFileSync(file);
|
|
134
|
+
const fileName = path.basename(file);
|
|
135
|
+
// Upload provisioning profiles first
|
|
136
|
+
const provisioningProfileIds = [];
|
|
137
|
+
if (provisioningProfile && provisioningProfile.length > 0) {
|
|
138
|
+
for (const profilePath of provisioningProfile) {
|
|
139
|
+
const profileBuffer = fs.readFileSync(profilePath);
|
|
140
|
+
const profileFileName = path.basename(profilePath);
|
|
141
|
+
const profile = await appProvisioningProfilesService.create({
|
|
142
|
+
appId,
|
|
143
|
+
buffer: profileBuffer,
|
|
144
|
+
fileName: profileFileName,
|
|
145
|
+
});
|
|
146
|
+
provisioningProfileIds.push(profile.id);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const certificate = await appCertificatesService.create({
|
|
150
|
+
appId,
|
|
151
|
+
buffer,
|
|
152
|
+
fileName,
|
|
153
|
+
name,
|
|
154
|
+
platform: platform,
|
|
155
|
+
password,
|
|
156
|
+
keyAlias,
|
|
157
|
+
keyPassword,
|
|
158
|
+
});
|
|
159
|
+
// Link provisioning profiles to the certificate
|
|
160
|
+
if (provisioningProfileIds.length > 0) {
|
|
161
|
+
await appProvisioningProfilesService.updateMany({
|
|
162
|
+
appId,
|
|
163
|
+
ids: provisioningProfileIds,
|
|
164
|
+
appCertificateId: certificate.id,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
consola.info(`Certificate ID: ${certificate.id}`);
|
|
168
|
+
consola.success('Certificate created successfully.');
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import appCertificatesService from '../../../services/app-certificates.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 certificate.',
|
|
10
|
+
options: defineOptions(z.object({
|
|
11
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
12
|
+
certificateId: z.string().optional().describe('ID of the certificate.'),
|
|
13
|
+
name: z.string().optional().describe('Name of the certificate.'),
|
|
14
|
+
platform: z
|
|
15
|
+
.enum(['android', 'ios', 'web'])
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('Platform of the certificate (android, ios, web).'),
|
|
18
|
+
yes: z.boolean().optional().describe('Skip confirmation prompt.'),
|
|
19
|
+
}), { y: 'yes' }),
|
|
20
|
+
action: withAuth(async (options, args) => {
|
|
21
|
+
let { appId, certificateId, name, platform } = options;
|
|
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 organizationId = await promptOrganizationSelection();
|
|
28
|
+
appId = await promptAppSelection(organizationId);
|
|
29
|
+
}
|
|
30
|
+
if (!certificateId) {
|
|
31
|
+
if (name && platform) {
|
|
32
|
+
const certificates = await appCertificatesService.findAll({ appId, name, platform });
|
|
33
|
+
const firstCertificate = certificates[0];
|
|
34
|
+
if (!firstCertificate) {
|
|
35
|
+
consola.error(`No certificate found with name '${name}' and platform '${platform}'.`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
certificateId = firstCertificate.id;
|
|
39
|
+
}
|
|
40
|
+
else if (isInteractive()) {
|
|
41
|
+
if (!platform) {
|
|
42
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
43
|
+
platform = await prompt('Select the platform:', {
|
|
44
|
+
type: 'select',
|
|
45
|
+
options: [
|
|
46
|
+
{ label: 'Android', value: 'android' },
|
|
47
|
+
{ label: 'iOS', value: 'ios' },
|
|
48
|
+
{ label: 'Web', value: 'web' },
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const certificates = await appCertificatesService.findAll({ appId, platform });
|
|
53
|
+
if (!certificates.length) {
|
|
54
|
+
consola.error('No certificates found for this app. Create one first.');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
58
|
+
certificateId = await prompt('Select the certificate to delete:', {
|
|
59
|
+
type: 'select',
|
|
60
|
+
options: certificates.map((cert) => ({ label: cert.name, value: cert.id })),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
consola.error('You must provide the certificate ID or --name and --platform when running in non-interactive environment.');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!options.yes && isInteractive()) {
|
|
69
|
+
const confirmed = await prompt('Are you sure you want to delete this certificate?', {
|
|
70
|
+
type: 'confirm',
|
|
71
|
+
});
|
|
72
|
+
if (!confirmed) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
await appCertificatesService.delete({ appId, certificateId });
|
|
77
|
+
consola.success('Certificate deleted successfully.');
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import appCertificatesService from '../../../services/app-certificates.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 certificate.',
|
|
10
|
+
options: defineOptions(z.object({
|
|
11
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
12
|
+
certificateId: z.string().optional().describe('ID of the certificate.'),
|
|
13
|
+
json: z.boolean().optional().describe('Output in JSON format.'),
|
|
14
|
+
name: z.string().optional().describe('Name of the certificate.'),
|
|
15
|
+
platform: z
|
|
16
|
+
.enum(['android', 'ios', 'web'])
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Platform of the certificate (android, ios, web).'),
|
|
19
|
+
})),
|
|
20
|
+
action: withAuth(async (options, args) => {
|
|
21
|
+
let { appId, certificateId, name, platform } = options;
|
|
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 organizationId = await promptOrganizationSelection();
|
|
28
|
+
appId = await promptAppSelection(organizationId);
|
|
29
|
+
}
|
|
30
|
+
if (!certificateId) {
|
|
31
|
+
if (name && platform) {
|
|
32
|
+
const certificates = await appCertificatesService.findAll({ appId, name, platform });
|
|
33
|
+
const firstCertificate = certificates[0];
|
|
34
|
+
if (!firstCertificate) {
|
|
35
|
+
consola.error(`No certificate found with name '${name}' and platform '${platform}'.`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
certificateId = firstCertificate.id;
|
|
39
|
+
}
|
|
40
|
+
else if (isInteractive()) {
|
|
41
|
+
if (!platform) {
|
|
42
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
43
|
+
platform = await prompt('Select the platform:', {
|
|
44
|
+
type: 'select',
|
|
45
|
+
options: [
|
|
46
|
+
{ label: 'Android', value: 'android' },
|
|
47
|
+
{ label: 'iOS', value: 'ios' },
|
|
48
|
+
{ label: 'Web', value: 'web' },
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const certificates = await appCertificatesService.findAll({ appId, platform });
|
|
53
|
+
if (!certificates.length) {
|
|
54
|
+
consola.error('No certificates found for this app. Create one first.');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
58
|
+
certificateId = await prompt('Select the certificate:', {
|
|
59
|
+
type: 'select',
|
|
60
|
+
options: certificates.map((cert) => ({ label: cert.name, value: cert.id })),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
consola.error('You must provide the certificate ID or --name and --platform when running in non-interactive environment.');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const certificate = await appCertificatesService.findOneById({
|
|
69
|
+
appId,
|
|
70
|
+
certificateId,
|
|
71
|
+
});
|
|
72
|
+
if (options.json) {
|
|
73
|
+
console.log(JSON.stringify(certificate, null, 2));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.table(certificate);
|
|
77
|
+
consola.success('Certificate retrieved successfully.');
|
|
78
|
+
}
|
|
79
|
+
}),
|
|
80
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import appCertificatesService from '../../../services/app-certificates.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 certificates.',
|
|
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', 'web']).optional().describe('Filter by platform.'),
|
|
16
|
+
type: z.enum(['development', 'production']).optional().describe('Filter by type.'),
|
|
17
|
+
})),
|
|
18
|
+
action: withAuth(async (options, args) => {
|
|
19
|
+
let { appId, json, limit, offset, platform, type } = options;
|
|
20
|
+
if (!appId) {
|
|
21
|
+
if (!isInteractive()) {
|
|
22
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
const organizationId = await promptOrganizationSelection();
|
|
26
|
+
appId = await promptAppSelection(organizationId);
|
|
27
|
+
}
|
|
28
|
+
const certificates = await appCertificatesService.findAll({
|
|
29
|
+
appId,
|
|
30
|
+
limit,
|
|
31
|
+
offset,
|
|
32
|
+
platform,
|
|
33
|
+
type,
|
|
34
|
+
});
|
|
35
|
+
if (json) {
|
|
36
|
+
console.log(JSON.stringify(certificates, null, 2));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.table(certificates);
|
|
40
|
+
consola.success('Certificates retrieved successfully.');
|
|
41
|
+
}
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import appCertificatesService from '../../../services/app-certificates.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 certificate.',
|
|
10
|
+
options: defineOptions(z.object({
|
|
11
|
+
appId: z.string().optional().describe('ID of the app.'),
|
|
12
|
+
certificateId: z.string().optional().describe('ID of the certificate.'),
|
|
13
|
+
keyAlias: z.string().optional().describe('Key alias for the certificate.'),
|
|
14
|
+
keyPassword: z.string().optional().describe('Key password for the certificate.'),
|
|
15
|
+
name: z.string().optional().describe('Name of the certificate.'),
|
|
16
|
+
password: z.string().optional().describe('Password for the certificate.'),
|
|
17
|
+
type: z
|
|
18
|
+
.enum(['development', 'production'])
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('Type of the certificate (development, production).'),
|
|
21
|
+
})),
|
|
22
|
+
action: withAuth(async (options, args) => {
|
|
23
|
+
let { appId, certificateId, keyAlias, keyPassword, name, password, type } = options;
|
|
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 organizationId = await promptOrganizationSelection();
|
|
30
|
+
appId = await promptAppSelection(organizationId);
|
|
31
|
+
}
|
|
32
|
+
if (!certificateId) {
|
|
33
|
+
if (!isInteractive()) {
|
|
34
|
+
consola.error('You must provide the certificate ID when running in non-interactive environment.');
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
certificateId = await prompt('Enter the certificate ID:', { type: 'text' });
|
|
38
|
+
}
|
|
39
|
+
if (type) {
|
|
40
|
+
consola.warn('The --type option is deprecated and will be removed in a future version. The certificate type is now detected automatically.');
|
|
41
|
+
}
|
|
42
|
+
await appCertificatesService.update({
|
|
43
|
+
appId,
|
|
44
|
+
certificateId,
|
|
45
|
+
name,
|
|
46
|
+
password,
|
|
47
|
+
keyAlias,
|
|
48
|
+
keyPassword,
|
|
49
|
+
});
|
|
50
|
+
consola.success('Certificate updated successfully.');
|
|
51
|
+
}),
|
|
52
|
+
});
|