@capawesome/cli 3.7.0 → 3.8.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 +13 -0
- package/dist/commands/apps/builds/cancel.js +1 -1
- package/dist/commands/apps/builds/create.js +1 -1
- package/dist/commands/apps/builds/download.js +1 -1
- package/dist/commands/apps/builds/logs.js +1 -1
- package/dist/commands/apps/bundles/create.js +152 -46
- package/dist/commands/apps/bundles/create.test.js +37 -9
- package/dist/commands/apps/bundles/delete.js +1 -1
- package/dist/commands/apps/bundles/update.js +1 -1
- package/dist/commands/apps/bundles/update.test.js +1 -1
- package/dist/commands/apps/channels/create.js +1 -1
- package/dist/commands/apps/channels/delete.js +1 -1
- package/dist/commands/apps/channels/get.js +1 -1
- package/dist/commands/apps/channels/list.js +1 -1
- package/dist/commands/apps/channels/list.test.js +1 -1
- package/dist/commands/apps/channels/update.js +1 -1
- package/dist/commands/apps/channels/update.test.js +1 -1
- package/dist/commands/apps/create.js +1 -1
- package/dist/commands/apps/delete.js +1 -1
- package/dist/commands/apps/deployments/cancel.js +1 -1
- package/dist/commands/apps/deployments/create.js +1 -1
- package/dist/commands/apps/deployments/logs.js +2 -2
- package/dist/commands/apps/devices/delete.js +1 -1
- package/dist/commands/organizations/create.js +1 -1
- package/dist/commands/organizations/create.test.js +1 -1
- package/dist/services/apps.js +8 -0
- package/dist/utils/capacitor-config.js +96 -0
- package/dist/utils/package-json.js +58 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
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
|
+
## [3.8.0](https://github.com/capawesome-team/cli/compare/v3.7.0...v3.8.0) (2025-12-04)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **apps:bundles:create:** auto-detect app ID and web assets directory ([#102](https://github.com/capawesome-team/cli/issues/102)) ([0e60df6](https://github.com/capawesome-team/cli/commit/0e60df66fcbe1effcb406b24e5a751a21badfa9d))
|
|
11
|
+
* **apps:bundles:create:** print warning if private key is missing ([ae29ad9](https://github.com/capawesome-team/cli/commit/ae29ad96ba85db353c81b85be2bee4c6ac928dc0))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* update error message to guide users to login first ([e5ea710](https://github.com/capawesome-team/cli/commit/e5ea710208df82d918e6f191d256d377569350f8))
|
|
17
|
+
|
|
5
18
|
## [3.7.0](https://github.com/capawesome-team/cli/compare/v3.6.0...v3.7.0) (2025-12-02)
|
|
6
19
|
|
|
7
20
|
|
|
@@ -28,7 +28,7 @@ export default defineCommand({
|
|
|
28
28
|
let { appId, buildId } = options;
|
|
29
29
|
// Check if the user is logged in
|
|
30
30
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
31
|
-
consola.error('You must be logged in to run this command.');
|
|
31
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
32
32
|
process.exit(1);
|
|
33
33
|
}
|
|
34
34
|
// Prompt for app ID if not provided
|
|
@@ -60,7 +60,7 @@ export default defineCommand({
|
|
|
60
60
|
let { appId, platform, type, gitRef, environment, certificate, json } = options;
|
|
61
61
|
// Check if the user is logged in
|
|
62
62
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
63
|
-
consola.error('You must be logged in to run this command.');
|
|
63
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
64
64
|
process.exit(1);
|
|
65
65
|
}
|
|
66
66
|
// Validate that detached flag cannot be used with artifact flags
|
|
@@ -41,7 +41,7 @@ export default defineCommand({
|
|
|
41
41
|
let { appId, buildId } = options;
|
|
42
42
|
// Check if the user is logged in
|
|
43
43
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
44
|
-
consola.error('You must be logged in to run this command.');
|
|
44
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
45
45
|
process.exit(1);
|
|
46
46
|
}
|
|
47
47
|
// Prompt for app ID if not provided
|
|
@@ -29,7 +29,7 @@ export default defineCommand({
|
|
|
29
29
|
let { appId, buildId } = options;
|
|
30
30
|
// Check if the user is logged in
|
|
31
31
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
32
|
-
consola.error('You must be logged in to run this command.');
|
|
32
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
33
33
|
process.exit(1);
|
|
34
34
|
}
|
|
35
35
|
// Prompt for app ID if not provided
|
|
@@ -5,18 +5,25 @@ import appsService from '../../../services/apps.js';
|
|
|
5
5
|
import authorizationService from '../../../services/authorization-service.js';
|
|
6
6
|
import organizationsService from '../../../services/organizations.js';
|
|
7
7
|
import { createBufferFromPath, createBufferFromReadStream, createBufferFromString, isPrivateKeyContent, } from '../../../utils/buffer.js';
|
|
8
|
+
import { findCapacitorConfigPath, getLiveUpdatePluginAppIdFromConfig, getLiveUpdatePluginPublicKeyFromConfig, getWebDirFromConfig, } from '../../../utils/capacitor-config.js';
|
|
8
9
|
import { fileExistsAtPath, getFilesInDirectoryAndSubdirectories, isDirectory } from '../../../utils/file.js';
|
|
9
10
|
import { createHash } from '../../../utils/hash.js';
|
|
10
11
|
import { generateManifestJson } from '../../../utils/manifest.js';
|
|
12
|
+
import { findPackageJsonPath, getBuildScript } from '../../../utils/package-json.js';
|
|
11
13
|
import { formatPrivateKey } from '../../../utils/private-key.js';
|
|
12
14
|
import { prompt } from '../../../utils/prompt.js';
|
|
13
15
|
import { createSignature } from '../../../utils/signature.js';
|
|
14
16
|
import zip from '../../../utils/zip.js';
|
|
15
17
|
import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
18
|
+
import { exec } from 'child_process';
|
|
16
19
|
import consola from 'consola';
|
|
17
20
|
import { createReadStream } from 'fs';
|
|
21
|
+
import pathModule from 'path';
|
|
18
22
|
import { hasTTY } from 'std-env';
|
|
23
|
+
import { promisify } from 'util';
|
|
19
24
|
import { z } from 'zod';
|
|
25
|
+
// Promisified exec for running build scripts
|
|
26
|
+
const execAsync = promisify(exec);
|
|
20
27
|
export default defineCommand({
|
|
21
28
|
description: 'Create a new app bundle.',
|
|
22
29
|
options: defineOptions(z.object({
|
|
@@ -100,7 +107,7 @@ export default defineCommand({
|
|
|
100
107
|
let { androidEq, androidMax, androidMin, appId, artifactType, channel, commitMessage, commitRef, commitSha, customProperty, expiresInDays, iosEq, iosMax, iosMin, path, privateKey, rollout, url, } = options;
|
|
101
108
|
// Check if the user is logged in
|
|
102
109
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
103
|
-
consola.error('You must be logged in to run this command.');
|
|
110
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
104
111
|
process.exit(1);
|
|
105
112
|
}
|
|
106
113
|
// Calculate the expiration date
|
|
@@ -110,22 +117,85 @@ export default defineCommand({
|
|
|
110
117
|
expiresAtDate.setDate(expiresAtDate.getDate() + expiresInDays);
|
|
111
118
|
expiresAt = expiresAtDate.toISOString();
|
|
112
119
|
}
|
|
120
|
+
// Try to auto-detect webDir from Capacitor configuration
|
|
121
|
+
const capacitorConfigPath = await findCapacitorConfigPath();
|
|
122
|
+
if (!capacitorConfigPath) {
|
|
123
|
+
consola.warn('No Capacitor configuration found to auto-detect web asset directory or app ID.');
|
|
124
|
+
}
|
|
113
125
|
// Check that either a path or a url is provided
|
|
114
126
|
if (!path && !url) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
127
|
+
// Try to auto-detect webDir from Capacitor configuration
|
|
128
|
+
if (capacitorConfigPath) {
|
|
129
|
+
const webDirPath = await getWebDirFromConfig(capacitorConfigPath);
|
|
130
|
+
if (webDirPath) {
|
|
131
|
+
const relativeWebDirPath = pathModule.relative(process.cwd(), webDirPath);
|
|
132
|
+
consola.success(`Auto-detected web asset directory "${relativeWebDirPath}" from Capacitor configuration.`);
|
|
133
|
+
path = webDirPath;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
consola.warn('No web asset directory found in Capacitor configuration (`webDir`).');
|
|
137
|
+
}
|
|
118
138
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (!path) {
|
|
124
|
-
consola.error('You must provide a path to the app bundle.');
|
|
139
|
+
// If still no path, prompt the user
|
|
140
|
+
if (!path) {
|
|
141
|
+
if (!hasTTY) {
|
|
142
|
+
consola.error('You must provide either a path or a url when running in non-interactive environment.');
|
|
125
143
|
process.exit(1);
|
|
126
144
|
}
|
|
145
|
+
else {
|
|
146
|
+
path = await prompt('Enter the path to the app bundle:', {
|
|
147
|
+
type: 'text',
|
|
148
|
+
});
|
|
149
|
+
if (!path) {
|
|
150
|
+
consola.error('You must provide a path to the app bundle.');
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Check for build scripts if a path is provided or detected
|
|
157
|
+
if (path && !url) {
|
|
158
|
+
const packageJsonPath = await findPackageJsonPath();
|
|
159
|
+
if (!packageJsonPath) {
|
|
160
|
+
consola.warn('No package.json file found.');
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
const buildScript = await getBuildScript(packageJsonPath);
|
|
164
|
+
if (!buildScript) {
|
|
165
|
+
consola.warn('No build script (`capawesome:build` or `build`) found in package.json.');
|
|
166
|
+
}
|
|
167
|
+
else if (hasTTY) {
|
|
168
|
+
const shouldBuild = await prompt('Do you want to run the build script before creating the bundle to ensure the latest assets are included?', {
|
|
169
|
+
type: 'confirm',
|
|
170
|
+
initial: true,
|
|
171
|
+
});
|
|
172
|
+
if (shouldBuild) {
|
|
173
|
+
try {
|
|
174
|
+
consola.start(`Running \`${buildScript.name}\` script...`);
|
|
175
|
+
const { stdout, stderr } = await execAsync(`npm run ${buildScript.name}`);
|
|
176
|
+
if (stdout) {
|
|
177
|
+
console.log(stdout);
|
|
178
|
+
}
|
|
179
|
+
if (stderr) {
|
|
180
|
+
console.error(stderr);
|
|
181
|
+
}
|
|
182
|
+
consola.success('Build completed successfully.');
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
consola.error('Build failed.');
|
|
186
|
+
if (error.stdout) {
|
|
187
|
+
console.log(error.stdout);
|
|
188
|
+
}
|
|
189
|
+
if (error.stderr) {
|
|
190
|
+
console.error(error.stderr);
|
|
191
|
+
}
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
127
196
|
}
|
|
128
197
|
}
|
|
198
|
+
// Validate the provided path
|
|
129
199
|
if (path) {
|
|
130
200
|
// Check if the path exists when a path is provided
|
|
131
201
|
const pathExists = await fileExistsAtPath(path);
|
|
@@ -164,48 +234,63 @@ export default defineCommand({
|
|
|
164
234
|
consola.error('It is not yet possible to provide a URL when creating a bundle with an artifact type of `manifest`.');
|
|
165
235
|
process.exit(1);
|
|
166
236
|
}
|
|
237
|
+
// Track if we found a Capacitor configuration but no app ID (for showing setup hint later)
|
|
167
238
|
if (!appId) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const organizationId = await prompt('Select the organization of the app for which you want to create a bundle.', {
|
|
179
|
-
type: 'select',
|
|
180
|
-
options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
|
|
181
|
-
});
|
|
182
|
-
if (!organizationId) {
|
|
183
|
-
consola.error('You must select the organization of an app for which you want to create a bundle.');
|
|
184
|
-
process.exit(1);
|
|
185
|
-
}
|
|
186
|
-
const apps = await appsService.findAll({
|
|
187
|
-
organizationId,
|
|
188
|
-
});
|
|
189
|
-
if (apps.length === 0) {
|
|
190
|
-
consola.error('You must create an app before creating a bundle.');
|
|
191
|
-
process.exit(1);
|
|
239
|
+
// Try to auto-detect appId from Capacitor configuration
|
|
240
|
+
if (capacitorConfigPath) {
|
|
241
|
+
const configAppId = await getLiveUpdatePluginAppIdFromConfig(capacitorConfigPath);
|
|
242
|
+
if (configAppId) {
|
|
243
|
+
consola.success(`Auto-detected Capawesome Cloud app ID "${configAppId}" from Capacitor configuration.`);
|
|
244
|
+
appId = configAppId;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
consola.warn('No Capawesome Cloud app ID found in Capacitor configuration (`plugins.LiveUpdate.appId`).');
|
|
248
|
+
}
|
|
192
249
|
}
|
|
193
|
-
//
|
|
194
|
-
appId = await prompt('Which app do you want to deploy to:', {
|
|
195
|
-
type: 'select',
|
|
196
|
-
options: apps.map((app) => ({ label: app.name, value: app.id })),
|
|
197
|
-
});
|
|
250
|
+
// If still no appId, prompt the user
|
|
198
251
|
if (!appId) {
|
|
199
|
-
|
|
200
|
-
|
|
252
|
+
if (!hasTTY) {
|
|
253
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
const organizations = await organizationsService.findAll();
|
|
257
|
+
if (organizations.length === 0) {
|
|
258
|
+
consola.error('You must create an organization before creating a bundle.');
|
|
259
|
+
process.exit(1);
|
|
260
|
+
}
|
|
261
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
262
|
+
const organizationId = await prompt('Select the organization of the app for which you want to create a bundle.', {
|
|
263
|
+
type: 'select',
|
|
264
|
+
options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
|
|
265
|
+
});
|
|
266
|
+
if (!organizationId) {
|
|
267
|
+
consola.error('You must select the organization of an app for which you want to create a bundle.');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
const apps = await appsService.findAll({
|
|
271
|
+
organizationId,
|
|
272
|
+
});
|
|
273
|
+
if (apps.length === 0) {
|
|
274
|
+
consola.error('You must create an app before creating a bundle.');
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
278
|
+
appId = await prompt('Which app do you want to deploy to:', {
|
|
279
|
+
type: 'select',
|
|
280
|
+
options: apps.map((app) => ({ label: app.name, value: app.id })),
|
|
281
|
+
});
|
|
282
|
+
if (!appId) {
|
|
283
|
+
consola.error('You must select an app to deploy to.');
|
|
284
|
+
process.exit(1);
|
|
285
|
+
}
|
|
201
286
|
}
|
|
202
287
|
}
|
|
203
288
|
if (!channel && hasTTY) {
|
|
204
|
-
const
|
|
205
|
-
type: '
|
|
206
|
-
|
|
289
|
+
const shouldDeployToChannel = await prompt('Do you want to deploy to a specific channel?', {
|
|
290
|
+
type: 'confirm',
|
|
291
|
+
initial: false,
|
|
207
292
|
});
|
|
208
|
-
if (
|
|
293
|
+
if (shouldDeployToChannel) {
|
|
209
294
|
channel = await prompt('Enter the channel name:', {
|
|
210
295
|
type: 'text',
|
|
211
296
|
});
|
|
@@ -215,6 +300,13 @@ export default defineCommand({
|
|
|
215
300
|
}
|
|
216
301
|
}
|
|
217
302
|
}
|
|
303
|
+
// Check if public key is configured but no private key was provided
|
|
304
|
+
if (!privateKey && capacitorConfigPath) {
|
|
305
|
+
const publicKey = await getLiveUpdatePluginPublicKeyFromConfig(capacitorConfigPath);
|
|
306
|
+
if (publicKey) {
|
|
307
|
+
consola.warn('A public key for verifying the integrity of the bundles is configured in your Capacitor configuration, but no private key has been provided for signing this bundle.');
|
|
308
|
+
}
|
|
309
|
+
}
|
|
218
310
|
// Create the private key buffer
|
|
219
311
|
let privateKeyBuffer;
|
|
220
312
|
if (privateKey) {
|
|
@@ -242,9 +334,23 @@ export default defineCommand({
|
|
|
242
334
|
process.exit(1);
|
|
243
335
|
}
|
|
244
336
|
}
|
|
337
|
+
// Get app details for confirmation
|
|
338
|
+
const app = await appsService.findOne({ appId });
|
|
339
|
+
const appName = app.name;
|
|
340
|
+
// Final confirmation before creating bundle
|
|
341
|
+
if (path && hasTTY) {
|
|
342
|
+
const relativePath = pathModule.relative(process.cwd(), path);
|
|
343
|
+
const confirmed = await prompt(`Are you sure you want to create a bundle from path "${relativePath}" for app "${appName}" (${appId})?`, {
|
|
344
|
+
type: 'confirm',
|
|
345
|
+
});
|
|
346
|
+
if (!confirmed) {
|
|
347
|
+
consola.info('Bundle creation cancelled.');
|
|
348
|
+
process.exit(0);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// Create the app bundle
|
|
245
352
|
let appBundleId;
|
|
246
353
|
try {
|
|
247
|
-
// Create the app bundle
|
|
248
354
|
consola.start('Creating bundle...');
|
|
249
355
|
let checksum;
|
|
250
356
|
let signature;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { DEFAULT_API_BASE_URL } from '../../../config/consts.js';
|
|
2
2
|
import authorizationService from '../../../services/authorization-service.js';
|
|
3
|
+
import { findCapacitorConfigPath } from '../../../utils/capacitor-config.js';
|
|
3
4
|
import { fileExistsAtPath, getFilesInDirectoryAndSubdirectories, isDirectory } from '../../../utils/file.js';
|
|
5
|
+
import { findPackageJsonPath } from '../../../utils/package-json.js';
|
|
4
6
|
import userConfig from '../../../utils/user-config.js';
|
|
5
7
|
import consola from 'consola';
|
|
6
8
|
import nock from 'nock';
|
|
@@ -15,6 +17,8 @@ vi.mock('@/utils/buffer.js');
|
|
|
15
17
|
vi.mock('@/utils/private-key.js');
|
|
16
18
|
vi.mock('@/utils/hash.js');
|
|
17
19
|
vi.mock('@/utils/signature.js');
|
|
20
|
+
vi.mock('@/utils/capacitor-config.js');
|
|
21
|
+
vi.mock('@/utils/package-json.js');
|
|
18
22
|
vi.mock('consola');
|
|
19
23
|
describe('apps-bundles-create', () => {
|
|
20
24
|
const mockUserConfig = vi.mocked(userConfig);
|
|
@@ -22,12 +26,16 @@ describe('apps-bundles-create', () => {
|
|
|
22
26
|
const mockFileExistsAtPath = vi.mocked(fileExistsAtPath);
|
|
23
27
|
const mockGetFilesInDirectoryAndSubdirectories = vi.mocked(getFilesInDirectoryAndSubdirectories);
|
|
24
28
|
const mockIsDirectory = vi.mocked(isDirectory);
|
|
29
|
+
const mockFindCapacitorConfigPath = vi.mocked(findCapacitorConfigPath);
|
|
30
|
+
const mockFindPackageJsonPath = vi.mocked(findPackageJsonPath);
|
|
25
31
|
const mockConsola = vi.mocked(consola);
|
|
26
32
|
beforeEach(() => {
|
|
27
33
|
vi.clearAllMocks();
|
|
28
34
|
mockUserConfig.read.mockReturnValue({ token: 'test-token' });
|
|
29
35
|
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(true);
|
|
30
36
|
mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue('test-token');
|
|
37
|
+
mockFindCapacitorConfigPath.mockResolvedValue(undefined);
|
|
38
|
+
mockFindPackageJsonPath.mockResolvedValue(undefined);
|
|
31
39
|
vi.spyOn(process, 'exit').mockImplementation((code) => {
|
|
32
40
|
throw new Error(`Process exited with code ${code}`);
|
|
33
41
|
});
|
|
@@ -41,7 +49,7 @@ describe('apps-bundles-create', () => {
|
|
|
41
49
|
const options = { appId, path: './dist', artifactType: 'zip', rollout: 1 };
|
|
42
50
|
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(false);
|
|
43
51
|
await expect(createBundleCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
44
|
-
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command.');
|
|
52
|
+
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command. Please run the `login` command first.');
|
|
45
53
|
});
|
|
46
54
|
it('should create bundle with self-hosted URL', async () => {
|
|
47
55
|
const appId = 'app-123';
|
|
@@ -67,7 +75,11 @@ describe('apps-bundles-create', () => {
|
|
|
67
75
|
vi.mocked(mockZip.default.isZipped).mockReturnValue(true);
|
|
68
76
|
vi.mocked(mockBuffer.createBufferFromPath).mockResolvedValue(testBuffer);
|
|
69
77
|
vi.mocked(mockHash.createHash).mockResolvedValue(testHash);
|
|
70
|
-
const
|
|
78
|
+
const appScope = nock(DEFAULT_API_BASE_URL)
|
|
79
|
+
.get(`/v1/apps/${appId}`)
|
|
80
|
+
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
81
|
+
.reply(200, { id: appId, name: 'Test App' });
|
|
82
|
+
const bundleScope = nock(DEFAULT_API_BASE_URL)
|
|
71
83
|
.post(`/v1/apps/${appId}/bundles`, {
|
|
72
84
|
appId,
|
|
73
85
|
url: bundleUrl,
|
|
@@ -78,7 +90,8 @@ describe('apps-bundles-create', () => {
|
|
|
78
90
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
79
91
|
.reply(201, { id: bundleId });
|
|
80
92
|
await createBundleCommand.action(options, undefined);
|
|
81
|
-
expect(
|
|
93
|
+
expect(appScope.isDone()).toBe(true);
|
|
94
|
+
expect(bundleScope.isDone()).toBe(true);
|
|
82
95
|
expect(mockConsola.success).toHaveBeenCalledWith('Bundle successfully created.');
|
|
83
96
|
expect(mockConsola.info).toHaveBeenCalledWith(`Bundle ID: ${bundleId}`);
|
|
84
97
|
});
|
|
@@ -129,12 +142,17 @@ describe('apps-bundles-create', () => {
|
|
|
129
142
|
artifactType: 'zip',
|
|
130
143
|
rollout: 1,
|
|
131
144
|
};
|
|
132
|
-
const
|
|
145
|
+
const appScope = nock(DEFAULT_API_BASE_URL)
|
|
146
|
+
.get(`/v1/apps/${appId}`)
|
|
147
|
+
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
148
|
+
.reply(200, { id: appId, name: 'Test App' });
|
|
149
|
+
const bundleScope = nock(DEFAULT_API_BASE_URL)
|
|
133
150
|
.post(`/v1/apps/${appId}/bundles`)
|
|
134
151
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
135
152
|
.reply(400, { message: 'Invalid bundle data' });
|
|
136
153
|
await expect(createBundleCommand.action(options, undefined)).rejects.toThrow();
|
|
137
|
-
expect(
|
|
154
|
+
expect(appScope.isDone()).toBe(true);
|
|
155
|
+
expect(bundleScope.isDone()).toBe(true);
|
|
138
156
|
});
|
|
139
157
|
it('should handle private key file path', async () => {
|
|
140
158
|
const appId = 'app-123';
|
|
@@ -175,7 +193,11 @@ describe('apps-bundles-create', () => {
|
|
|
175
193
|
vi.mocked(mockBuffer.createBufferFromString).mockReturnValue(testBuffer);
|
|
176
194
|
vi.mocked(mockHash.createHash).mockResolvedValue(testHash);
|
|
177
195
|
vi.mocked(mockSignature.createSignature).mockResolvedValue(testSignature);
|
|
178
|
-
const
|
|
196
|
+
const appScope = nock(DEFAULT_API_BASE_URL)
|
|
197
|
+
.get(`/v1/apps/${appId}`)
|
|
198
|
+
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
199
|
+
.reply(200, { id: appId, name: 'Test App' });
|
|
200
|
+
const bundleScope = nock(DEFAULT_API_BASE_URL)
|
|
179
201
|
.post(`/v1/apps/${appId}/bundles`, {
|
|
180
202
|
appId,
|
|
181
203
|
url: bundleUrl,
|
|
@@ -187,7 +209,8 @@ describe('apps-bundles-create', () => {
|
|
|
187
209
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
188
210
|
.reply(201, { id: bundleId });
|
|
189
211
|
await createBundleCommand.action(options, undefined);
|
|
190
|
-
expect(
|
|
212
|
+
expect(appScope.isDone()).toBe(true);
|
|
213
|
+
expect(bundleScope.isDone()).toBe(true);
|
|
191
214
|
expect(mockConsola.success).toHaveBeenCalledWith('Bundle successfully created.');
|
|
192
215
|
});
|
|
193
216
|
it('should handle private key plain text content', async () => {
|
|
@@ -223,7 +246,11 @@ describe('apps-bundles-create', () => {
|
|
|
223
246
|
vi.mocked(mockPrivateKey.formatPrivateKey).mockReturnValue('formatted-private-key');
|
|
224
247
|
vi.mocked(mockHash.createHash).mockResolvedValue(testHash);
|
|
225
248
|
vi.mocked(mockSignature.createSignature).mockResolvedValue(testSignature);
|
|
226
|
-
const
|
|
249
|
+
const appScope = nock(DEFAULT_API_BASE_URL)
|
|
250
|
+
.get(`/v1/apps/${appId}`)
|
|
251
|
+
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
252
|
+
.reply(200, { id: appId, name: 'Test App' });
|
|
253
|
+
const bundleScope = nock(DEFAULT_API_BASE_URL)
|
|
227
254
|
.post(`/v1/apps/${appId}/bundles`, {
|
|
228
255
|
appId,
|
|
229
256
|
url: bundleUrl,
|
|
@@ -235,7 +262,8 @@ describe('apps-bundles-create', () => {
|
|
|
235
262
|
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
236
263
|
.reply(201, { id: bundleId });
|
|
237
264
|
await createBundleCommand.action(options, undefined);
|
|
238
|
-
expect(
|
|
265
|
+
expect(appScope.isDone()).toBe(true);
|
|
266
|
+
expect(bundleScope.isDone()).toBe(true);
|
|
239
267
|
expect(mockConsola.success).toHaveBeenCalledWith('Bundle successfully created.');
|
|
240
268
|
});
|
|
241
269
|
it('should handle private key file not found', async () => {
|
|
@@ -16,7 +16,7 @@ export default defineCommand({
|
|
|
16
16
|
action: async (options, args) => {
|
|
17
17
|
let { appId, bundleId } = options;
|
|
18
18
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
19
|
-
consola.error('You must be logged in to run this command.');
|
|
19
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
20
20
|
process.exit(1);
|
|
21
21
|
}
|
|
22
22
|
// Prompt for missing arguments
|
|
@@ -48,7 +48,7 @@ export default defineCommand({
|
|
|
48
48
|
action: async (options, args) => {
|
|
49
49
|
let { androidMax, androidMin, androidEq, appId, bundleId, rollout, iosMax, iosMin, iosEq } = options;
|
|
50
50
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
51
|
-
consola.error('You must be logged in to run this command.');
|
|
51
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
52
52
|
process.exit(1);
|
|
53
53
|
}
|
|
54
54
|
// Prompt for missing arguments
|
|
@@ -38,7 +38,7 @@ describe('apps-bundles-update', () => {
|
|
|
38
38
|
const options = { appId, bundleId };
|
|
39
39
|
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(false);
|
|
40
40
|
await expect(updateBundleCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
41
|
-
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command.');
|
|
41
|
+
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command. Please run the `login` command first.');
|
|
42
42
|
});
|
|
43
43
|
it('should update bundle with provided options', async () => {
|
|
44
44
|
const appId = 'app-123';
|
|
@@ -31,7 +31,7 @@ export default defineCommand({
|
|
|
31
31
|
action: async (options, args) => {
|
|
32
32
|
let { appId, bundleLimit, expiresInDays, ignoreErrors, name } = options;
|
|
33
33
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
34
|
-
consola.error('You must be logged in to run this command.');
|
|
34
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
35
35
|
process.exit(1);
|
|
36
36
|
}
|
|
37
37
|
// Calculate the expiration date
|
|
@@ -23,7 +23,7 @@ export default defineCommand({
|
|
|
23
23
|
action: async (options, args) => {
|
|
24
24
|
let { appId, channelId, name } = options;
|
|
25
25
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
26
|
-
consola.error('You must be logged in to run this command.');
|
|
26
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
27
27
|
process.exit(1);
|
|
28
28
|
}
|
|
29
29
|
if (!appId) {
|
|
@@ -14,7 +14,7 @@ export default defineCommand({
|
|
|
14
14
|
action: async (options, args) => {
|
|
15
15
|
let { appId, channelId, json, name } = options;
|
|
16
16
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
17
|
-
consola.error('You must be logged in to run this command.');
|
|
17
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
18
18
|
process.exit(1);
|
|
19
19
|
}
|
|
20
20
|
if (!appId) {
|
|
@@ -14,7 +14,7 @@ export default defineCommand({
|
|
|
14
14
|
action: async (options, args) => {
|
|
15
15
|
let { appId, json, limit, offset } = options;
|
|
16
16
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
17
|
-
consola.error('You must be logged in to run this command.');
|
|
17
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
18
18
|
process.exit(1);
|
|
19
19
|
}
|
|
20
20
|
if (!appId) {
|
|
@@ -33,7 +33,7 @@ describe('apps-channels-list', () => {
|
|
|
33
33
|
const options = { appId };
|
|
34
34
|
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(false);
|
|
35
35
|
await expect(listChannelsCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
36
|
-
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command.');
|
|
36
|
+
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command. Please run the `login` command first.');
|
|
37
37
|
});
|
|
38
38
|
it('should require appId', async () => {
|
|
39
39
|
const options = { appId: undefined };
|
|
@@ -21,7 +21,7 @@ export default defineCommand({
|
|
|
21
21
|
action: async (options, args) => {
|
|
22
22
|
let { appId, channelId, bundleLimit, name } = options;
|
|
23
23
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
24
|
-
consola.error('You must be logged in to run this command.');
|
|
24
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
25
25
|
process.exit(1);
|
|
26
26
|
}
|
|
27
27
|
// Prompt app ID if not provided
|
|
@@ -38,7 +38,7 @@ describe('apps-channels-update', () => {
|
|
|
38
38
|
const options = { appId, channelId };
|
|
39
39
|
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(false);
|
|
40
40
|
await expect(updateChannelCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
41
|
-
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command.');
|
|
41
|
+
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command. Please run the `login` command first.');
|
|
42
42
|
});
|
|
43
43
|
it('should update channel with provided options', async () => {
|
|
44
44
|
const appId = 'app-123';
|
|
@@ -15,7 +15,7 @@ export default defineCommand({
|
|
|
15
15
|
action: async (options, args) => {
|
|
16
16
|
let { name, organizationId } = options;
|
|
17
17
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
18
|
-
consola.error('You must be logged in to run this command.');
|
|
18
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
19
19
|
process.exit(1);
|
|
20
20
|
}
|
|
21
21
|
if (!organizationId) {
|
|
@@ -14,7 +14,7 @@ export default defineCommand({
|
|
|
14
14
|
action: async (options, args) => {
|
|
15
15
|
let { appId } = options;
|
|
16
16
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
17
|
-
consola.error('You must be logged in to run this command.');
|
|
17
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
18
18
|
process.exit(1);
|
|
19
19
|
}
|
|
20
20
|
if (!appId) {
|
|
@@ -28,7 +28,7 @@ export default defineCommand({
|
|
|
28
28
|
let { appId, deploymentId } = options;
|
|
29
29
|
// Check if the user is logged in
|
|
30
30
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
31
|
-
consola.error('You must be logged in to run this command.');
|
|
31
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
32
32
|
process.exit(1);
|
|
33
33
|
}
|
|
34
34
|
// Prompt for app ID if not provided
|
|
@@ -37,7 +37,7 @@ export default defineCommand({
|
|
|
37
37
|
let { appId, buildId, destination } = options;
|
|
38
38
|
// Check if the user is logged in
|
|
39
39
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
40
|
-
consola.error('You must be logged in to run this command.');
|
|
40
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
41
41
|
process.exit(1);
|
|
42
42
|
}
|
|
43
43
|
// Prompt for app ID if not provided
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import appDeploymentsService from '../../../services/app-deployments.js';
|
|
1
2
|
import appsService from '../../../services/apps.js';
|
|
2
3
|
import authorizationService from '../../../services/authorization-service.js';
|
|
3
4
|
import organizationsService from '../../../services/organizations.js';
|
|
@@ -8,7 +9,6 @@ import { defineCommand, defineOptions } from '@robingenz/zli';
|
|
|
8
9
|
import consola from 'consola';
|
|
9
10
|
import { hasTTY } from 'std-env';
|
|
10
11
|
import { z } from 'zod';
|
|
11
|
-
import appDeploymentsService from '../../../services/app-deployments.js';
|
|
12
12
|
export default defineCommand({
|
|
13
13
|
description: 'View the deployment logs of an app.',
|
|
14
14
|
options: defineOptions(z.object({
|
|
@@ -29,7 +29,7 @@ export default defineCommand({
|
|
|
29
29
|
let { appId, deploymentId } = options;
|
|
30
30
|
// Check if the user is logged in
|
|
31
31
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
32
|
-
consola.error('You must be logged in to run this command.');
|
|
32
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
33
33
|
process.exit(1);
|
|
34
34
|
}
|
|
35
35
|
// Prompt for app ID if not provided
|
|
@@ -16,7 +16,7 @@ export default defineCommand({
|
|
|
16
16
|
action: async (options, args) => {
|
|
17
17
|
let { appId, deviceId } = options;
|
|
18
18
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
19
|
-
consola.error('You must be logged in to run this command.');
|
|
19
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
20
20
|
process.exit(1);
|
|
21
21
|
}
|
|
22
22
|
// Prompt for app ID if not provided
|
|
@@ -13,7 +13,7 @@ export default defineCommand({
|
|
|
13
13
|
action: async (options, args) => {
|
|
14
14
|
let { name } = options;
|
|
15
15
|
if (!authorizationService.hasAuthorizationToken()) {
|
|
16
|
-
consola.error('You must be logged in to run this command.');
|
|
16
|
+
consola.error('You must be logged in to run this command. Please run the `login` command first.');
|
|
17
17
|
process.exit(1);
|
|
18
18
|
}
|
|
19
19
|
if (!name) {
|
|
@@ -67,7 +67,7 @@ describe('organizations-create', () => {
|
|
|
67
67
|
const options = { name: organizationName };
|
|
68
68
|
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(false);
|
|
69
69
|
await expect(createOrganizationCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
70
|
-
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command.');
|
|
70
|
+
expect(mockConsola.error).toHaveBeenCalledWith('You must be logged in to run this command. Please run the `login` command first.');
|
|
71
71
|
});
|
|
72
72
|
it('should handle API error during creation', async () => {
|
|
73
73
|
const organizationName = 'Test Organization';
|
package/dist/services/apps.js
CHANGED
|
@@ -31,6 +31,14 @@ class AppsServiceImpl {
|
|
|
31
31
|
});
|
|
32
32
|
return response.data;
|
|
33
33
|
}
|
|
34
|
+
async findOne(dto) {
|
|
35
|
+
const response = await this.httpClient.get(`/v1/apps/${dto.appId}`, {
|
|
36
|
+
headers: {
|
|
37
|
+
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
return response.data;
|
|
41
|
+
}
|
|
34
42
|
}
|
|
35
43
|
const appsService = new AppsServiceImpl(httpClient);
|
|
36
44
|
export default appsService;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import pathModule from 'path';
|
|
3
|
+
import { fileExistsAtPath } from './file.js';
|
|
4
|
+
/**
|
|
5
|
+
* Find the Capacitor config file in the current working directory.
|
|
6
|
+
* Looks for capacitor.config.json or capacitor.config.ts.
|
|
7
|
+
*
|
|
8
|
+
* @returns The path to the config file, or undefined if not found.
|
|
9
|
+
*/
|
|
10
|
+
export const findCapacitorConfigPath = async () => {
|
|
11
|
+
const cwd = process.cwd();
|
|
12
|
+
const jsonPath = pathModule.join(cwd, 'capacitor.config.json');
|
|
13
|
+
const tsPath = pathModule.join(cwd, 'capacitor.config.ts');
|
|
14
|
+
if (await fileExistsAtPath(jsonPath)) {
|
|
15
|
+
return jsonPath;
|
|
16
|
+
}
|
|
17
|
+
if (await fileExistsAtPath(tsPath)) {
|
|
18
|
+
return tsPath;
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Read and parse the Capacitor config file.
|
|
24
|
+
*
|
|
25
|
+
* @param configPath The path to the config file.
|
|
26
|
+
* @returns The parsed config object, or undefined if parsing fails.
|
|
27
|
+
*/
|
|
28
|
+
export const readCapacitorConfig = async (configPath) => {
|
|
29
|
+
try {
|
|
30
|
+
if (configPath.endsWith('.json')) {
|
|
31
|
+
const content = await fs.promises.readFile(configPath, 'utf-8');
|
|
32
|
+
return JSON.parse(content);
|
|
33
|
+
}
|
|
34
|
+
else if (configPath.endsWith('.ts')) {
|
|
35
|
+
// For TypeScript config, parse as text and extract values
|
|
36
|
+
const content = await fs.promises.readFile(configPath, 'utf-8');
|
|
37
|
+
// Extract webDir using regex
|
|
38
|
+
const webDirMatch = content.match(/webDir:\s*['"]([^'"]+)['"]/);
|
|
39
|
+
const appIdMatch = content.match(/LiveUpdate:\s*{[^}]*appId:\s*['"]([^'"]+)['"]/s);
|
|
40
|
+
const publicKeyMatch = content.match(/LiveUpdate:\s*{[^}]*publicKey:\s*['"]([^'"]+)['"]/s);
|
|
41
|
+
const config = {};
|
|
42
|
+
if (webDirMatch) {
|
|
43
|
+
config.webDir = webDirMatch[1];
|
|
44
|
+
}
|
|
45
|
+
if (appIdMatch || publicKeyMatch) {
|
|
46
|
+
config.plugins = {
|
|
47
|
+
LiveUpdate: {
|
|
48
|
+
...(appIdMatch && { appId: appIdMatch[1] }),
|
|
49
|
+
...(publicKeyMatch && { publicKey: publicKeyMatch[1] }),
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return Object.keys(config).length > 0 ? config : undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
// Return undefined if parsing fails
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Get the webDir from the Capacitor config.
|
|
63
|
+
* Returns the absolute path to the webDir.
|
|
64
|
+
*
|
|
65
|
+
* @param configPath The path to the config file.
|
|
66
|
+
* @returns The absolute path to the webDir, or undefined if not found.
|
|
67
|
+
*/
|
|
68
|
+
export const getWebDirFromConfig = async (configPath) => {
|
|
69
|
+
const config = await readCapacitorConfig(configPath);
|
|
70
|
+
if (config?.webDir) {
|
|
71
|
+
// Resolve the webDir relative to the config file location
|
|
72
|
+
const configDir = pathModule.dirname(configPath);
|
|
73
|
+
return pathModule.resolve(configDir, config.webDir);
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Get the LiveUpdate appId from the Capacitor config.
|
|
79
|
+
*
|
|
80
|
+
* @param configPath The path to the config file.
|
|
81
|
+
* @returns The appId, or undefined if not found.
|
|
82
|
+
*/
|
|
83
|
+
export const getLiveUpdatePluginAppIdFromConfig = async (configPath) => {
|
|
84
|
+
const config = await readCapacitorConfig(configPath);
|
|
85
|
+
return config?.plugins?.LiveUpdate?.appId;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Get the LiveUpdate publicKey from the Capacitor config.
|
|
89
|
+
*
|
|
90
|
+
* @param configPath The path to the config file.
|
|
91
|
+
* @returns The publicKey, or undefined if not found.
|
|
92
|
+
*/
|
|
93
|
+
export const getLiveUpdatePluginPublicKeyFromConfig = async (configPath) => {
|
|
94
|
+
const config = await readCapacitorConfig(configPath);
|
|
95
|
+
return config?.plugins?.LiveUpdate?.publicKey;
|
|
96
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { fileExistsAtPath } from './file.js';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import pathModule from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Find the package.json file in the current working directory.
|
|
6
|
+
*
|
|
7
|
+
* @returns The path to the package.json file, or undefined if not found.
|
|
8
|
+
*/
|
|
9
|
+
export const findPackageJsonPath = async () => {
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
const packageJsonPath = pathModule.join(cwd, 'package.json');
|
|
12
|
+
if (await fileExistsAtPath(packageJsonPath)) {
|
|
13
|
+
return packageJsonPath;
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Read and parse the package.json file.
|
|
19
|
+
*
|
|
20
|
+
* @param packageJsonPath The path to the package.json file.
|
|
21
|
+
* @returns The parsed package.json object, or undefined if parsing fails.
|
|
22
|
+
*/
|
|
23
|
+
export const readPackageJson = async (packageJsonPath) => {
|
|
24
|
+
try {
|
|
25
|
+
const content = await fs.promises.readFile(packageJsonPath, 'utf-8');
|
|
26
|
+
return JSON.parse(content);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Get the build script from package.json.
|
|
34
|
+
* Prefers 'capawesome:build' over 'build' if both exist.
|
|
35
|
+
*
|
|
36
|
+
* @param packageJsonPath The path to the package.json file.
|
|
37
|
+
* @returns An object with the script name and command, or undefined if not found.
|
|
38
|
+
*/
|
|
39
|
+
export const getBuildScript = async (packageJsonPath) => {
|
|
40
|
+
const packageJson = await readPackageJson(packageJsonPath);
|
|
41
|
+
if (!packageJson?.scripts) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
// Prefer 'capawesome:build' over 'build'
|
|
45
|
+
if (packageJson.scripts['capawesome:build']) {
|
|
46
|
+
return {
|
|
47
|
+
name: 'capawesome:build',
|
|
48
|
+
command: packageJson.scripts['capawesome:build'],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (packageJson.scripts['build']) {
|
|
52
|
+
return {
|
|
53
|
+
name: 'build',
|
|
54
|
+
command: packageJson.scripts['build'],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capawesome/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.0",
|
|
4
4
|
"description": "The Capawesome Cloud Command Line Interface (CLI) to manage Live Updates and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"fmt": "npm run prettier -- --write",
|
|
14
14
|
"prettier": "prettier \"**/*.{css,html,ts,js}\"",
|
|
15
15
|
"sentry:releases:new": "sentry-cli releases new capawesome-team-cli@$npm_package_version --org genz-it-solutions-gmbh --project capawesome-team-cli",
|
|
16
|
-
"sentry:releases:set-commits": "sentry-cli releases set-commits capawesome-team-cli@$npm_package_version --auto --org genz-it-solutions-gmbh --project capawesome-team-cli",
|
|
16
|
+
"sentry:releases:set-commits": "sentry-cli releases set-commits capawesome-team-cli@$npm_package_version --auto --org genz-it-solutions-gmbh --project capawesome-team-cli --ignore-missing",
|
|
17
17
|
"sentry:releases:finalize": "sentry-cli releases finalize capawesome-team-cli@$npm_package_version --org genz-it-solutions-gmbh --project capawesome-team-cli",
|
|
18
18
|
"release": "commit-and-tag-version",
|
|
19
19
|
"prepublishOnly": "npm run build && npm run sentry:releases:new && npm run sentry:releases:set-commits",
|