@capawesome/cli 4.7.0 → 4.8.0-dev.efa0850.1775645973
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 +11 -0
- package/dist/commands/apps/builds/create.js +25 -10
- package/dist/commands/apps/create.js +23 -4
- package/dist/index.js +9 -5
- package/dist/utils/error.js +6 -0
- package/dist/utils/git.js +6 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
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.8.0](https://github.com/capawesome-team/cli/compare/v4.7.0...v4.8.0) (2026-04-08)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **apps:** add `--json` option to `apps:create` ([#147](https://github.com/capawesome-team/cli/issues/147)) ([3573d60](https://github.com/capawesome-team/cli/commit/3573d6021574111e12a6758d212b41002a357ae8))
|
|
11
|
+
* **apps:** add `--link` option to `apps:create` ([#145](https://github.com/capawesome-team/cli/issues/145)) ([c9e38b8](https://github.com/capawesome-team/cli/commit/c9e38b8e3e360bc856cbff4bf66b68fadedd3b04))
|
|
12
|
+
* **apps:** add prompt to connect git repository after app creation ([#143](https://github.com/capawesome-team/cli/issues/143)) ([3427e6d](https://github.com/capawesome-team/cli/commit/3427e6da277fa61902dac5b9045c433535188142))
|
|
13
|
+
* **apps:** print app URL after `apps:create` ([#146](https://github.com/capawesome-team/cli/issues/146)) ([ed7e04a](https://github.com/capawesome-team/cli/commit/ed7e04a5b379187d8c69856d180256f246931571))
|
|
14
|
+
* **error:** add `UserError` class to skip user errors in Sentry ([#144](https://github.com/capawesome-team/cli/issues/144)) ([f7728d0](https://github.com/capawesome-team/cli/commit/f7728d0849cf4883c761de5bfd673eb5c3c37409))
|
|
15
|
+
|
|
5
16
|
## [4.7.0](https://github.com/capawesome-team/cli/compare/v4.6.0...v4.7.0) (2026-04-04)
|
|
6
17
|
|
|
7
18
|
|
|
@@ -5,7 +5,9 @@ import appCertificatesService from '../../../services/app-certificates.js';
|
|
|
5
5
|
import appEnvironmentsService from '../../../services/app-environments.js';
|
|
6
6
|
import { parseKeyValuePairs } from '../../../utils/app-environments.js';
|
|
7
7
|
import { withAuth } from '../../../utils/auth.js';
|
|
8
|
+
import { createBufferFromPath } from '../../../utils/buffer.js';
|
|
8
9
|
import { isInteractive } from '../../../utils/environment.js';
|
|
10
|
+
import { fileExistsAtPath, isDirectory } from '../../../utils/file.js';
|
|
9
11
|
import { waitForJobCompletion } from '../../../utils/job.js';
|
|
10
12
|
import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
|
|
11
13
|
import zip from '../../../utils/zip.js';
|
|
@@ -47,7 +49,7 @@ export default defineCommand({
|
|
|
47
49
|
.optional()
|
|
48
50
|
.describe('Download the generated IPA file (iOS only). Optionally provide a file path.'),
|
|
49
51
|
json: z.boolean().optional().describe('Output in JSON format.'),
|
|
50
|
-
path: z.string().optional().describe('Path to local source files to upload.'),
|
|
52
|
+
path: z.string().optional().describe('Path to local source files to upload. Must be a folder or a zip file.'),
|
|
51
53
|
platform: z
|
|
52
54
|
.enum(['ios', 'android', 'web'], {
|
|
53
55
|
message: 'Platform must be either `ios`, `android`, or `web`.',
|
|
@@ -117,15 +119,22 @@ export default defineCommand({
|
|
|
117
119
|
if (sourcePath) {
|
|
118
120
|
consola.warn('The --path option is experimental and may change in the future.');
|
|
119
121
|
const resolvedPath = path.resolve(sourcePath);
|
|
120
|
-
const
|
|
121
|
-
if (!
|
|
122
|
-
consola.error('The --path
|
|
122
|
+
const exists = await fileExistsAtPath(resolvedPath);
|
|
123
|
+
if (!exists) {
|
|
124
|
+
consola.error('The --path does not exist.');
|
|
123
125
|
process.exit(1);
|
|
124
126
|
}
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
const pathIsDirectory = await isDirectory(resolvedPath);
|
|
128
|
+
if (pathIsDirectory) {
|
|
129
|
+
const packageJsonPath = path.join(resolvedPath, 'package.json');
|
|
130
|
+
const packageJsonExists = await fileExistsAtPath(packageJsonPath);
|
|
131
|
+
if (!packageJsonExists) {
|
|
132
|
+
consola.error('The directory specified by --path must contain a package.json file.');
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else if (!zip.isZipped(resolvedPath)) {
|
|
137
|
+
consola.error('The --path must be a folder or a zip file.');
|
|
129
138
|
process.exit(1);
|
|
130
139
|
}
|
|
131
140
|
}
|
|
@@ -265,8 +274,14 @@ export default defineCommand({
|
|
|
265
274
|
// Upload source files if path is provided
|
|
266
275
|
if (sourcePath) {
|
|
267
276
|
const resolvedPath = path.resolve(sourcePath);
|
|
268
|
-
|
|
269
|
-
|
|
277
|
+
let buffer;
|
|
278
|
+
if (zip.isZipped(resolvedPath)) {
|
|
279
|
+
buffer = await createBufferFromPath(resolvedPath);
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
consola.start('Zipping source files...');
|
|
283
|
+
buffer = await zip.zipFolderWithGitignore(resolvedPath);
|
|
284
|
+
}
|
|
270
285
|
consola.start('Uploading source files...');
|
|
271
286
|
const appBuildSource = await appBuildSourcesService.createFromFile({
|
|
272
287
|
appId,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_CONSOLE_BASE_URL } from '../../config/consts.js';
|
|
1
2
|
import appsService from '../../services/apps.js';
|
|
2
3
|
import { withAuth } from '../../utils/auth.js';
|
|
3
4
|
import { isInteractive } from '../../utils/environment.js';
|
|
@@ -8,11 +9,14 @@ import { z } from 'zod';
|
|
|
8
9
|
export default defineCommand({
|
|
9
10
|
description: 'Create a new app.',
|
|
10
11
|
options: defineOptions(z.object({
|
|
12
|
+
json: z.boolean().optional().describe('Output in JSON format.'),
|
|
13
|
+
link: z.boolean().optional().describe('Connect the created app to the local git repository.'),
|
|
11
14
|
name: z.string().optional().describe('Name of the app.'),
|
|
12
15
|
organizationId: z.string().optional().describe('ID of the organization to create the app in.'),
|
|
13
|
-
|
|
16
|
+
yes: z.boolean().optional().describe('Skip all confirmation prompts.'),
|
|
17
|
+
}), { y: 'yes' }),
|
|
14
18
|
action: withAuth(async (options, args) => {
|
|
15
|
-
let { name, organizationId } = options;
|
|
19
|
+
let { json, name, organizationId } = options;
|
|
16
20
|
if (!organizationId) {
|
|
17
21
|
if (!isInteractive()) {
|
|
18
22
|
consola.error('You must provide the organization ID when running in non-interactive environment.');
|
|
@@ -28,7 +32,22 @@ export default defineCommand({
|
|
|
28
32
|
name = await prompt('Enter the name of the app:', { type: 'text' });
|
|
29
33
|
}
|
|
30
34
|
const response = await appsService.create({ name, organizationId });
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
if (!json) {
|
|
36
|
+
consola.info(`App ID: ${response.id}`);
|
|
37
|
+
consola.info(`App URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${response.id}`);
|
|
38
|
+
consola.success('App created successfully.');
|
|
39
|
+
}
|
|
40
|
+
let shouldLink = options.link ?? false;
|
|
41
|
+
if (!shouldLink && !options.yes && !json && isInteractive()) {
|
|
42
|
+
shouldLink = await prompt('Do you want to connect a git repository?', {
|
|
43
|
+
type: 'confirm',
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
if (shouldLink) {
|
|
47
|
+
await (await import('../../commands/apps/link.js').then((mod) => mod.default)).action({ appId: response.id }, undefined);
|
|
48
|
+
}
|
|
49
|
+
if (json) {
|
|
50
|
+
console.log(JSON.stringify({ id: response.id }, null, 2));
|
|
51
|
+
}
|
|
33
52
|
}),
|
|
34
53
|
});
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import configService from './services/config.js';
|
|
3
3
|
import updateService from './services/update.js';
|
|
4
|
-
import { getMessageFromUnknownError } from './utils/error.js';
|
|
4
|
+
import { getMessageFromUnknownError, UserError } from './utils/error.js';
|
|
5
5
|
import { defineConfig, processConfig, ZliError } from '@robingenz/zli';
|
|
6
6
|
import * as Sentry from '@sentry/node';
|
|
7
7
|
import { AxiosError } from 'axios';
|
|
@@ -76,6 +76,14 @@ const config = defineConfig({
|
|
|
76
76
|
},
|
|
77
77
|
});
|
|
78
78
|
const captureException = async (error) => {
|
|
79
|
+
// Ignore failed HTTP requests
|
|
80
|
+
if (error instanceof AxiosError) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Ignore expected user errors
|
|
84
|
+
if (error instanceof UserError) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
79
87
|
// Ignore errors from the CLI itself (e.g. "No command found.")
|
|
80
88
|
if (error instanceof ZliError) {
|
|
81
89
|
return;
|
|
@@ -84,10 +92,6 @@ const captureException = async (error) => {
|
|
|
84
92
|
if (error instanceof ZodError) {
|
|
85
93
|
return;
|
|
86
94
|
}
|
|
87
|
-
// Ignore failed HTTP requests
|
|
88
|
-
if (error instanceof AxiosError) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
95
|
const environment = await configService.getValueForKey('ENVIRONMENT');
|
|
92
96
|
if (environment !== 'production') {
|
|
93
97
|
return;
|
package/dist/utils/error.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { AxiosError } from 'axios';
|
|
2
2
|
import { ZodError } from 'zod';
|
|
3
|
+
export class UserError extends Error {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = 'UserError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
3
9
|
export const getMessageFromUnknownError = (error) => {
|
|
4
10
|
let message = 'An unknown error has occurred.';
|
|
5
11
|
if (error instanceof AxiosError) {
|
package/dist/utils/git.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
|
+
import { UserError } from '../utils/error.js';
|
|
2
3
|
const HOSTNAME_TO_PROVIDER = {
|
|
3
4
|
'github.com': 'github',
|
|
4
5
|
'gitlab.com': 'gitlab',
|
|
@@ -15,7 +16,7 @@ const getGitRemoteUrl = () => {
|
|
|
15
16
|
return execSync('git remote get-url origin', { encoding: 'utf-8' }).trim();
|
|
16
17
|
}
|
|
17
18
|
catch {
|
|
18
|
-
throw new
|
|
19
|
+
throw new UserError('Could not read the git remote URL. Make sure you are inside a git repository with an origin remote.');
|
|
19
20
|
}
|
|
20
21
|
};
|
|
21
22
|
export const parseGitRemoteUrl = (remoteUrl) => {
|
|
@@ -55,7 +56,7 @@ export const parseGitRemoteUrl = (remoteUrl) => {
|
|
|
55
56
|
const hostname = sshMatch[1];
|
|
56
57
|
const provider = HOSTNAME_TO_PROVIDER[hostname];
|
|
57
58
|
if (!provider) {
|
|
58
|
-
throw new
|
|
59
|
+
throw new UserError(`Unsupported git provider for hostname "${hostname}".`);
|
|
59
60
|
}
|
|
60
61
|
return {
|
|
61
62
|
ownerSlug: sshMatch[2],
|
|
@@ -71,7 +72,7 @@ export const parseGitRemoteUrl = (remoteUrl) => {
|
|
|
71
72
|
const hostname = url.hostname;
|
|
72
73
|
const provider = HOSTNAME_TO_PROVIDER[hostname];
|
|
73
74
|
if (!provider) {
|
|
74
|
-
throw new
|
|
75
|
+
throw new UserError(`Unsupported git provider for hostname "${hostname}".`);
|
|
75
76
|
}
|
|
76
77
|
const pathSegments = url.pathname.split('/').filter(Boolean);
|
|
77
78
|
const repositorySlug = pathSegments.pop()?.replace(/\.git$/, '');
|
|
@@ -83,9 +84,9 @@ export const parseGitRemoteUrl = (remoteUrl) => {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
catch (error) {
|
|
86
|
-
if (error instanceof
|
|
87
|
+
if (error instanceof UserError) {
|
|
87
88
|
throw error;
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
|
-
throw new
|
|
91
|
+
throw new UserError('Could not parse git remote URL.');
|
|
91
92
|
};
|