@capawesome/cli 4.16.1 → 4.17.1
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
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
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.17.1](https://github.com/capawesome-team/cli/compare/v4.17.0...v4.17.1) (2026-07-16)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **deps:** update `systeminformation` to `5.31.17` ([#187](https://github.com/capawesome-team/cli/issues/187)) ([2aca30c](https://github.com/capawesome-team/cli/commit/2aca30cd0c48f27ffc8cd44d400efec9763a58ac))
|
|
11
|
+
|
|
12
|
+
## [4.17.0](https://github.com/capawesome-team/cli/compare/v4.16.1...v4.17.0) (2026-07-12)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* add `apps:builds:unshare` command ([#184](https://github.com/capawesome-team/cli/issues/184)) ([55ecd8e](https://github.com/capawesome-team/cli/commit/55ecd8e7892da4a70a6438b90b6e9db3470c315f))
|
|
18
|
+
|
|
5
19
|
## [4.16.1](https://github.com/capawesome-team/cli/compare/v4.16.0...v4.16.1) (2026-07-12)
|
|
6
20
|
|
|
7
21
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import appBuildsService from '../../../services/app-builds.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: 'Revoke the public share link of an app build.',
|
|
10
|
+
options: defineOptions(z.object({
|
|
11
|
+
appId: z
|
|
12
|
+
.uuid({
|
|
13
|
+
message: 'App ID must be a UUID.',
|
|
14
|
+
})
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('App ID the build belongs to.'),
|
|
17
|
+
buildId: z
|
|
18
|
+
.uuid({
|
|
19
|
+
message: 'Build ID must be a UUID.',
|
|
20
|
+
})
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Build ID to unshare.'),
|
|
23
|
+
buildNumber: z.string().optional().describe('Build number to unshare (e.g., "1", "42").'),
|
|
24
|
+
yes: z.boolean().optional().describe('Skip confirmation prompt.'),
|
|
25
|
+
}), { y: 'yes' }),
|
|
26
|
+
action: withAuth(async (options) => {
|
|
27
|
+
let { appId, buildId } = options;
|
|
28
|
+
const { buildNumber } = options;
|
|
29
|
+
// Prompt for app ID if not provided
|
|
30
|
+
if (!appId) {
|
|
31
|
+
if (!isInteractive()) {
|
|
32
|
+
consola.error('You must provide an app ID when running in non-interactive environment.');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
const organizationId = await promptOrganizationSelection();
|
|
36
|
+
appId = await promptAppSelection(organizationId);
|
|
37
|
+
}
|
|
38
|
+
// Convert build number to build ID if provided
|
|
39
|
+
if (!buildId && buildNumber) {
|
|
40
|
+
const builds = await appBuildsService.findAll({ appId, numberAsString: buildNumber });
|
|
41
|
+
if (builds.length === 0) {
|
|
42
|
+
consola.error(`Build #${buildNumber} not found.`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
buildId = builds[0]?.id;
|
|
46
|
+
}
|
|
47
|
+
// Prompt for build ID if not provided
|
|
48
|
+
if (!buildId) {
|
|
49
|
+
if (!isInteractive()) {
|
|
50
|
+
consola.error('You must provide a build ID when running in non-interactive environment.');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const builds = await appBuildsService.findAll({ appId });
|
|
54
|
+
if (builds.length === 0) {
|
|
55
|
+
consola.error('No builds found for this app.');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
59
|
+
buildId = await prompt('Select the build you want to unshare:', {
|
|
60
|
+
type: 'select',
|
|
61
|
+
options: builds.map((build) => ({
|
|
62
|
+
label: `Build #${build.numberAsString || build.id} (${build.platform} - ${build.type})`,
|
|
63
|
+
value: build.id,
|
|
64
|
+
})),
|
|
65
|
+
});
|
|
66
|
+
if (!buildId) {
|
|
67
|
+
consola.error('You must select a build to unshare.');
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Find the active share
|
|
72
|
+
const shares = await appBuildsService.findAllShares({ appId, appBuildId: buildId });
|
|
73
|
+
const share = shares[0];
|
|
74
|
+
if (!share) {
|
|
75
|
+
consola.error('This build is not shared.');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
// Confirm revocation
|
|
79
|
+
if (!options.yes && isInteractive()) {
|
|
80
|
+
const confirmed = await prompt('Are you sure you want to revoke the share link for this build?', {
|
|
81
|
+
type: 'confirm',
|
|
82
|
+
});
|
|
83
|
+
if (!confirmed) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Revoke the share
|
|
88
|
+
await appBuildsService.deleteShare({ appId, appBuildId: buildId, id: share.id });
|
|
89
|
+
consola.success('Share revoked successfully.');
|
|
90
|
+
}),
|
|
91
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { DEFAULT_API_BASE_URL } from '../../../config/consts.js';
|
|
2
|
+
import authorizationService from '../../../services/authorization-service.js';
|
|
3
|
+
import userConfig from '../../../utils/user-config.js';
|
|
4
|
+
import consola from 'consola';
|
|
5
|
+
import nock from 'nock';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
|
+
import unshareCommand from './unshare.js';
|
|
8
|
+
// Mock dependencies
|
|
9
|
+
vi.mock('@/utils/user-config.js');
|
|
10
|
+
vi.mock('@/utils/prompt.js');
|
|
11
|
+
vi.mock('@/services/authorization-service.js');
|
|
12
|
+
vi.mock('consola');
|
|
13
|
+
vi.mock('@/utils/environment.js', () => ({
|
|
14
|
+
isInteractive: () => false,
|
|
15
|
+
}));
|
|
16
|
+
describe('apps-builds-unshare', () => {
|
|
17
|
+
const mockUserConfig = vi.mocked(userConfig);
|
|
18
|
+
const mockAuthorizationService = vi.mocked(authorizationService);
|
|
19
|
+
const mockConsola = vi.mocked(consola);
|
|
20
|
+
const testToken = 'test-token';
|
|
21
|
+
const appId = '00000000-0000-0000-0000-000000000001';
|
|
22
|
+
const buildId = '00000000-0000-0000-0000-000000000002';
|
|
23
|
+
const shareId = 'share-abc';
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks();
|
|
26
|
+
mockUserConfig.read.mockReturnValue({ token: testToken });
|
|
27
|
+
mockAuthorizationService.hasAuthorizationToken.mockReturnValue(true);
|
|
28
|
+
mockAuthorizationService.getCurrentAuthorizationToken.mockReturnValue(testToken);
|
|
29
|
+
vi.spyOn(process, 'exit').mockImplementation((code) => {
|
|
30
|
+
throw new Error(`Process exited with code ${code}`);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
nock.cleanAll();
|
|
35
|
+
vi.restoreAllMocks();
|
|
36
|
+
});
|
|
37
|
+
it('should revoke the active share', async () => {
|
|
38
|
+
const options = { appId, buildId, yes: true };
|
|
39
|
+
const sharesScope = nock(DEFAULT_API_BASE_URL)
|
|
40
|
+
.get(`/v1/apps/${appId}/builds/${buildId}/shares`)
|
|
41
|
+
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
42
|
+
.reply(200, [
|
|
43
|
+
{
|
|
44
|
+
id: shareId,
|
|
45
|
+
appBuildId: buildId,
|
|
46
|
+
description: null,
|
|
47
|
+
expiresAt: null,
|
|
48
|
+
createdAt: '2024-01-01T00:00:00Z',
|
|
49
|
+
updatedAt: '2024-01-01T00:00:00Z',
|
|
50
|
+
},
|
|
51
|
+
]);
|
|
52
|
+
const deleteScope = nock(DEFAULT_API_BASE_URL)
|
|
53
|
+
.delete(`/v1/apps/${appId}/builds/${buildId}/shares/${shareId}`)
|
|
54
|
+
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
55
|
+
.reply(204);
|
|
56
|
+
await unshareCommand.action(options, undefined);
|
|
57
|
+
expect(sharesScope.isDone()).toBe(true);
|
|
58
|
+
expect(deleteScope.isDone()).toBe(true);
|
|
59
|
+
expect(mockConsola.success).toHaveBeenCalledWith('Share revoked successfully.');
|
|
60
|
+
});
|
|
61
|
+
it('should error when the build is not shared', async () => {
|
|
62
|
+
const options = { appId, buildId, yes: true };
|
|
63
|
+
nock(DEFAULT_API_BASE_URL)
|
|
64
|
+
.get(`/v1/apps/${appId}/builds/${buildId}/shares`)
|
|
65
|
+
.matchHeader('Authorization', `Bearer ${testToken}`)
|
|
66
|
+
.reply(200, []);
|
|
67
|
+
await expect(unshareCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
68
|
+
expect(mockConsola.error).toHaveBeenCalledWith('This build is not shared.');
|
|
69
|
+
});
|
|
70
|
+
it('should error when no app ID is provided in non-interactive environment', async () => {
|
|
71
|
+
const options = {};
|
|
72
|
+
await expect(unshareCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
|
|
73
|
+
expect(mockConsola.error).toHaveBeenCalledWith('You must provide an app ID when running in non-interactive environment.');
|
|
74
|
+
});
|
|
75
|
+
});
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ const config = defineConfig({
|
|
|
37
37
|
'apps:builds:list': await import('./commands/apps/builds/list.js').then((mod) => mod.default),
|
|
38
38
|
'apps:builds:logs': await import('./commands/apps/builds/logs.js').then((mod) => mod.default),
|
|
39
39
|
'apps:builds:share': await import('./commands/apps/builds/share.js').then((mod) => mod.default),
|
|
40
|
+
'apps:builds:unshare': await import('./commands/apps/builds/unshare.js').then((mod) => mod.default),
|
|
40
41
|
'apps:builds:download': await import('./commands/apps/builds/download.js').then((mod) => mod.default),
|
|
41
42
|
'apps:bundles:create': await import('./commands/apps/bundles/create.js').then((mod) => mod.default),
|
|
42
43
|
'apps:bundles:delete': await import('./commands/apps/bundles/delete.js').then((mod) => mod.default),
|
|
@@ -23,6 +23,13 @@ class AppBuildsServiceImpl {
|
|
|
23
23
|
});
|
|
24
24
|
return response.data;
|
|
25
25
|
}
|
|
26
|
+
async deleteShare(dto) {
|
|
27
|
+
await this.httpClient.delete(`/v1/apps/${dto.appId}/builds/${dto.appBuildId}/shares/${dto.id}`, {
|
|
28
|
+
headers: {
|
|
29
|
+
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
26
33
|
async findAll(dto) {
|
|
27
34
|
const params = {};
|
|
28
35
|
if (dto.limit !== undefined) {
|
|
@@ -48,6 +55,14 @@ class AppBuildsServiceImpl {
|
|
|
48
55
|
});
|
|
49
56
|
return response.data;
|
|
50
57
|
}
|
|
58
|
+
async findAllShares(dto) {
|
|
59
|
+
const response = await this.httpClient.get(`/v1/apps/${dto.appId}/builds/${dto.appBuildId}/shares`, {
|
|
60
|
+
headers: {
|
|
61
|
+
Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
return response.data;
|
|
65
|
+
}
|
|
51
66
|
async findOne(dto) {
|
|
52
67
|
const { appId, appBuildId, relations } = dto;
|
|
53
68
|
const params = {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capawesome/cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.17.1",
|
|
4
4
|
"description": "The Capawesome Cloud Command Line Interface (CLI) to manage Live Updates and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -16,10 +16,12 @@
|
|
|
16
16
|
"sentry:releases:new": "sentry-cli releases new capawesome-team-cli@$npm_package_version --org genz-it-solutions-gmbh --project capawesome-team-cli",
|
|
17
17
|
"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",
|
|
18
18
|
"sentry:releases:finalize": "sentry-cli releases finalize capawesome-team-cli@$npm_package_version --org genz-it-solutions-gmbh --project capawesome-team-cli",
|
|
19
|
-
"release": "commit-and-tag-version",
|
|
20
19
|
"prepublishOnly": "npm run build && npm run sentry:releases:new && npm run sentry:releases:set-commits",
|
|
21
20
|
"postpublish": "npm run sentry:releases:finalize"
|
|
22
21
|
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
23
25
|
"engines": {
|
|
24
26
|
"npm": ">=10.0.0",
|
|
25
27
|
"node": ">=20.0.0"
|
|
@@ -70,7 +72,7 @@
|
|
|
70
72
|
"open": "10.2.0",
|
|
71
73
|
"rc9": "2.1.2",
|
|
72
74
|
"semver": "7.6.3",
|
|
73
|
-
"systeminformation": "5.31.
|
|
75
|
+
"systeminformation": "5.31.17",
|
|
74
76
|
"zod": "4.0.17"
|
|
75
77
|
},
|
|
76
78
|
"devDependencies": {
|
|
@@ -82,7 +84,6 @@
|
|
|
82
84
|
"@types/semver": "7.5.8",
|
|
83
85
|
"@vitest/coverage-v8": "4.1.7",
|
|
84
86
|
"@vitest/ui": "4.1.7",
|
|
85
|
-
"commit-and-tag-version": "12.6.1",
|
|
86
87
|
"nock": "14.0.10",
|
|
87
88
|
"prettier": "3.3.3",
|
|
88
89
|
"rimraf": "6.0.1",
|