@capawesome/cli 4.6.0-dev.bda9e9d.1774345600 → 4.7.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 +24 -0
- package/dist/commands/apps/builds/create.js +98 -12
- package/dist/commands/apps/bundles/create.js +4 -2
- package/dist/commands/apps/bundles/delete.js +2 -3
- package/dist/commands/apps/bundles/update.js +2 -3
- package/dist/commands/apps/certificates/delete.js +28 -5
- package/dist/commands/apps/certificates/get.js +28 -5
- package/dist/commands/apps/deployments/create.js +5 -77
- package/dist/commands/apps/devices/forcechannel.js +9 -7
- package/dist/commands/apps/devices/unforcechannel.js +9 -7
- package/dist/commands/apps/link.js +34 -0
- package/dist/commands/apps/link.test.js +94 -0
- package/dist/commands/apps/liveupdates/bundle.js +7 -2
- package/dist/commands/apps/liveupdates/create.js +148 -44
- package/dist/commands/apps/liveupdates/create.test.js +300 -0
- package/dist/commands/apps/liveupdates/generate-manifest.js +12 -1
- package/dist/commands/apps/liveupdates/generate-manifest.test.js +21 -1
- package/dist/commands/apps/liveupdates/register.js +10 -15
- package/dist/commands/apps/liveupdates/upload.js +18 -16
- package/dist/commands/apps/transfer.js +47 -0
- package/dist/commands/apps/transfer.test.js +123 -0
- package/dist/commands/apps/unlink.js +35 -0
- package/dist/commands/apps/unlink.test.js +99 -0
- package/dist/commands/manifests/generate.js +1 -1
- package/dist/index.js +3 -0
- package/dist/services/app-build-sources.js +120 -0
- package/dist/services/app-devices.js +8 -0
- package/dist/services/apps.js +25 -0
- package/dist/services/authorization-service.js +5 -1
- package/dist/services/jobs.js +13 -0
- package/dist/types/app-build-source.js +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/custom-properties.js +22 -0
- package/dist/utils/file.js +8 -1
- package/dist/utils/git.js +91 -0
- package/dist/utils/git.test.js +130 -0
- package/dist/utils/{build.js → job.js} +26 -23
- package/dist/utils/prompt.js +1 -1
- package/dist/utils/zip.js +19 -2
- package/package.json +2 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { parseGitRemoteUrl } from './git.js';
|
|
3
|
+
describe('parseGitRemoteUrl', () => {
|
|
4
|
+
it('should parse GitHub HTTPS URL', () => {
|
|
5
|
+
const result = parseGitRemoteUrl('https://github.com/capawesome-team/cli.git');
|
|
6
|
+
expect(result).toEqual({
|
|
7
|
+
ownerSlug: 'capawesome-team',
|
|
8
|
+
provider: 'github',
|
|
9
|
+
repositorySlug: 'cli',
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
it('should parse GitHub HTTPS URL without .git suffix', () => {
|
|
13
|
+
const result = parseGitRemoteUrl('https://github.com/capawesome-team/cli');
|
|
14
|
+
expect(result).toEqual({
|
|
15
|
+
ownerSlug: 'capawesome-team',
|
|
16
|
+
provider: 'github',
|
|
17
|
+
repositorySlug: 'cli',
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
it('should parse GitHub SSH URL', () => {
|
|
21
|
+
const result = parseGitRemoteUrl('git@github.com:capawesome-team/cli.git');
|
|
22
|
+
expect(result).toEqual({
|
|
23
|
+
ownerSlug: 'capawesome-team',
|
|
24
|
+
provider: 'github',
|
|
25
|
+
repositorySlug: 'cli',
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
it('should parse GitHub SSH URL without .git suffix', () => {
|
|
29
|
+
const result = parseGitRemoteUrl('git@github.com:capawesome-team/cli');
|
|
30
|
+
expect(result).toEqual({
|
|
31
|
+
ownerSlug: 'capawesome-team',
|
|
32
|
+
provider: 'github',
|
|
33
|
+
repositorySlug: 'cli',
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
it('should parse GitLab HTTPS URL', () => {
|
|
37
|
+
const result = parseGitRemoteUrl('https://gitlab.com/my-group/my-repo.git');
|
|
38
|
+
expect(result).toEqual({
|
|
39
|
+
ownerSlug: 'my-group',
|
|
40
|
+
provider: 'gitlab',
|
|
41
|
+
repositorySlug: 'my-repo',
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
it('should parse GitLab SSH URL', () => {
|
|
45
|
+
const result = parseGitRemoteUrl('git@gitlab.com:my-group/my-repo.git');
|
|
46
|
+
expect(result).toEqual({
|
|
47
|
+
ownerSlug: 'my-group',
|
|
48
|
+
provider: 'gitlab',
|
|
49
|
+
repositorySlug: 'my-repo',
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
it('should parse GitLab HTTPS URL with subgroup', () => {
|
|
53
|
+
const result = parseGitRemoteUrl('https://gitlab.com/my-group/my-subgroup/my-repo.git');
|
|
54
|
+
expect(result).toEqual({
|
|
55
|
+
ownerSlug: 'my-group',
|
|
56
|
+
provider: 'gitlab',
|
|
57
|
+
repositorySlug: 'my-repo',
|
|
58
|
+
projectSlug: 'my-subgroup',
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
it('should parse GitLab SSH URL with subgroup', () => {
|
|
62
|
+
const result = parseGitRemoteUrl('git@gitlab.com:my-group/my-subgroup/my-repo.git');
|
|
63
|
+
expect(result).toEqual({
|
|
64
|
+
ownerSlug: 'my-group',
|
|
65
|
+
provider: 'gitlab',
|
|
66
|
+
repositorySlug: 'my-repo',
|
|
67
|
+
projectSlug: 'my-subgroup',
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
it('should parse Bitbucket HTTPS URL', () => {
|
|
71
|
+
const result = parseGitRemoteUrl('https://bitbucket.org/my-team/my-repo.git');
|
|
72
|
+
expect(result).toEqual({
|
|
73
|
+
ownerSlug: 'my-team',
|
|
74
|
+
provider: 'bitbucket',
|
|
75
|
+
repositorySlug: 'my-repo',
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
it('should parse Bitbucket SSH URL', () => {
|
|
79
|
+
const result = parseGitRemoteUrl('git@bitbucket.org:my-team/my-repo.git');
|
|
80
|
+
expect(result).toEqual({
|
|
81
|
+
ownerSlug: 'my-team',
|
|
82
|
+
provider: 'bitbucket',
|
|
83
|
+
repositorySlug: 'my-repo',
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
it('should parse Azure DevOps HTTPS URL', () => {
|
|
87
|
+
const result = parseGitRemoteUrl('https://dev.azure.com/my-org/my-project/_git/my-repo');
|
|
88
|
+
expect(result).toEqual({
|
|
89
|
+
ownerSlug: 'my-org',
|
|
90
|
+
provider: 'azure',
|
|
91
|
+
repositorySlug: 'my-repo',
|
|
92
|
+
projectSlug: 'my-project',
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
it('should parse Azure DevOps SSH URL', () => {
|
|
96
|
+
const result = parseGitRemoteUrl('git@ssh.dev.azure.com:v3/my-org/my-project/my-repo');
|
|
97
|
+
expect(result).toEqual({
|
|
98
|
+
ownerSlug: 'my-org',
|
|
99
|
+
provider: 'azure',
|
|
100
|
+
repositorySlug: 'my-repo',
|
|
101
|
+
projectSlug: 'my-project',
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
it('should parse Visual Studio HTTPS URL', () => {
|
|
105
|
+
const result = parseGitRemoteUrl('https://my-org.visualstudio.com/my-project/_git/my-repo');
|
|
106
|
+
expect(result).toEqual({
|
|
107
|
+
ownerSlug: 'my-org',
|
|
108
|
+
provider: 'azure',
|
|
109
|
+
repositorySlug: 'my-repo',
|
|
110
|
+
projectSlug: 'my-project',
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
it('should parse GitHub HTTPS URL with credentials', () => {
|
|
114
|
+
const result = parseGitRemoteUrl('https://x-access-token:ghp_secret123@github.com/capawesome-team/cli.git');
|
|
115
|
+
expect(result).toEqual({
|
|
116
|
+
ownerSlug: 'capawesome-team',
|
|
117
|
+
provider: 'github',
|
|
118
|
+
repositorySlug: 'cli',
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
it('should throw for unsupported hostname', () => {
|
|
122
|
+
expect(() => parseGitRemoteUrl('https://example.com/owner/repo.git')).toThrow('Unsupported git provider for hostname "example.com".');
|
|
123
|
+
});
|
|
124
|
+
it('should not leak credentials in error messages', () => {
|
|
125
|
+
expect(() => parseGitRemoteUrl('https://token@example.com/owner/repo.git')).toThrow('Unsupported git provider for hostname "example.com".');
|
|
126
|
+
});
|
|
127
|
+
it('should throw for unparseable URL', () => {
|
|
128
|
+
expect(() => parseGitRemoteUrl('not-a-url')).toThrow('Could not parse git remote URL.');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -1,36 +1,39 @@
|
|
|
1
|
-
import
|
|
1
|
+
import jobsService from '../services/jobs.js';
|
|
2
2
|
import { unescapeAnsi } from '../utils/ansi.js';
|
|
3
3
|
import { wait } from '../utils/wait.js';
|
|
4
4
|
import consola from 'consola';
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const getLabel = (job) => {
|
|
6
|
+
if (job.appBuildId) {
|
|
7
|
+
return 'build';
|
|
8
|
+
}
|
|
9
|
+
if (job.appDeploymentId) {
|
|
10
|
+
return 'deployment';
|
|
11
|
+
}
|
|
12
|
+
return 'job';
|
|
13
|
+
};
|
|
14
|
+
const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
15
|
+
export const waitForJobCompletion = async (options) => {
|
|
16
|
+
const { jobId } = options;
|
|
7
17
|
let lastPrintedLogNumber = 0;
|
|
8
18
|
let isWaitingForStart = true;
|
|
9
19
|
while (true) {
|
|
10
20
|
try {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
relations,
|
|
15
|
-
});
|
|
16
|
-
if (!build.job) {
|
|
17
|
-
await wait(3000);
|
|
18
|
-
continue;
|
|
19
|
-
}
|
|
20
|
-
const jobStatus = build.job.status;
|
|
21
|
+
const job = await jobsService.findOne({ jobId, relations: 'jobLogs' });
|
|
22
|
+
const label = getLabel(job);
|
|
23
|
+
const jobStatus = job.status;
|
|
21
24
|
if (jobStatus === 'queued' || jobStatus === 'pending') {
|
|
22
25
|
if (isWaitingForStart) {
|
|
23
|
-
consola.start(`Waiting for
|
|
26
|
+
consola.start(`Waiting for ${label} to start (status: ${jobStatus})...`);
|
|
24
27
|
}
|
|
25
28
|
await wait(3000);
|
|
26
29
|
continue;
|
|
27
30
|
}
|
|
28
31
|
if (isWaitingForStart && jobStatus === 'in_progress') {
|
|
29
32
|
isWaitingForStart = false;
|
|
30
|
-
consola.success(
|
|
33
|
+
consola.success(`${capitalize(label)} started...`);
|
|
31
34
|
}
|
|
32
|
-
if (
|
|
33
|
-
const newLogs =
|
|
35
|
+
if (job.jobLogs && job.jobLogs.length > 0) {
|
|
36
|
+
const newLogs = job.jobLogs
|
|
34
37
|
.filter((log) => log.number > lastPrintedLogNumber)
|
|
35
38
|
.sort((a, b) => a.number - b.number);
|
|
36
39
|
for (const log of newLogs) {
|
|
@@ -45,29 +48,29 @@ export const waitForBuildCompletion = async (options) => {
|
|
|
45
48
|
jobStatus === 'timed_out') {
|
|
46
49
|
console.log();
|
|
47
50
|
if (jobStatus === 'succeeded') {
|
|
48
|
-
return
|
|
51
|
+
return job;
|
|
49
52
|
}
|
|
50
53
|
else if (jobStatus === 'failed') {
|
|
51
|
-
consola.error(
|
|
54
|
+
consola.error(`${capitalize(label)} failed.`);
|
|
52
55
|
process.exit(1);
|
|
53
56
|
}
|
|
54
57
|
else if (jobStatus === 'canceled') {
|
|
55
|
-
consola.
|
|
58
|
+
consola.error(`${capitalize(label)} was canceled.`);
|
|
56
59
|
process.exit(1);
|
|
57
60
|
}
|
|
58
61
|
else if (jobStatus === 'rejected') {
|
|
59
|
-
consola.error(
|
|
62
|
+
consola.error(`${capitalize(label)} was rejected.`);
|
|
60
63
|
process.exit(1);
|
|
61
64
|
}
|
|
62
65
|
else if (jobStatus === 'timed_out') {
|
|
63
|
-
consola.error(
|
|
66
|
+
consola.error(`${capitalize(label)} timed out.`);
|
|
64
67
|
process.exit(1);
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
70
|
await wait(3000);
|
|
68
71
|
}
|
|
69
72
|
catch (error) {
|
|
70
|
-
consola.error('Error polling
|
|
73
|
+
consola.error('Error polling job status:', error);
|
|
71
74
|
process.exit(1);
|
|
72
75
|
}
|
|
73
76
|
}
|
package/dist/utils/prompt.js
CHANGED
|
@@ -31,7 +31,7 @@ export const promptOrganizationSelection = async (options) => {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
// @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
|
|
34
|
-
const organizationId = await prompt('Which organization do you want to use?', {
|
|
34
|
+
const organizationId = await prompt(options?.message ?? 'Which organization do you want to use?', {
|
|
35
35
|
type: 'select',
|
|
36
36
|
options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
|
|
37
37
|
});
|
package/dist/utils/zip.js
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
import AdmZip from 'adm-zip';
|
|
2
|
+
import { globby } from 'globby';
|
|
3
|
+
import path from 'path';
|
|
2
4
|
class ZipImpl {
|
|
3
5
|
async zipFolder(sourceFolder) {
|
|
4
6
|
const zip = new AdmZip();
|
|
5
7
|
zip.addLocalFolder(sourceFolder);
|
|
6
8
|
return zip.toBuffer();
|
|
7
9
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
async zipFolderWithGitignore(sourceFolder) {
|
|
11
|
+
const files = await globby(['**/*'], {
|
|
12
|
+
cwd: sourceFolder,
|
|
13
|
+
gitignore: true,
|
|
14
|
+
ignore: ['.git/**'],
|
|
15
|
+
dot: true,
|
|
16
|
+
});
|
|
17
|
+
const zip = new AdmZip();
|
|
18
|
+
for (const file of files) {
|
|
19
|
+
const filePath = path.join(sourceFolder, file);
|
|
20
|
+
const dirName = path.dirname(file);
|
|
21
|
+
zip.addLocalFile(filePath, dirName === '.' ? '' : dirName);
|
|
22
|
+
}
|
|
23
|
+
return zip.toBuffer();
|
|
24
|
+
}
|
|
25
|
+
isZipped(filePath) {
|
|
26
|
+
return filePath.endsWith('.zip');
|
|
10
27
|
}
|
|
11
28
|
}
|
|
12
29
|
const zip = new ZipImpl();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capawesome/cli",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "The Capawesome Cloud Command Line Interface (CLI) to manage Live Updates and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"c12": "3.3.3",
|
|
62
62
|
"consola": "3.3.0",
|
|
63
63
|
"form-data": "4.0.4",
|
|
64
|
+
"globby": "16.1.1",
|
|
64
65
|
"http-proxy-agent": "7.0.2",
|
|
65
66
|
"https-proxy-agent": "7.0.6",
|
|
66
67
|
"mime": "4.0.7",
|