@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.
Files changed (40) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/commands/apps/builds/create.js +98 -12
  3. package/dist/commands/apps/bundles/create.js +4 -2
  4. package/dist/commands/apps/bundles/delete.js +2 -3
  5. package/dist/commands/apps/bundles/update.js +2 -3
  6. package/dist/commands/apps/certificates/delete.js +28 -5
  7. package/dist/commands/apps/certificates/get.js +28 -5
  8. package/dist/commands/apps/deployments/create.js +5 -77
  9. package/dist/commands/apps/devices/forcechannel.js +9 -7
  10. package/dist/commands/apps/devices/unforcechannel.js +9 -7
  11. package/dist/commands/apps/link.js +34 -0
  12. package/dist/commands/apps/link.test.js +94 -0
  13. package/dist/commands/apps/liveupdates/bundle.js +7 -2
  14. package/dist/commands/apps/liveupdates/create.js +148 -44
  15. package/dist/commands/apps/liveupdates/create.test.js +300 -0
  16. package/dist/commands/apps/liveupdates/generate-manifest.js +12 -1
  17. package/dist/commands/apps/liveupdates/generate-manifest.test.js +21 -1
  18. package/dist/commands/apps/liveupdates/register.js +10 -15
  19. package/dist/commands/apps/liveupdates/upload.js +18 -16
  20. package/dist/commands/apps/transfer.js +47 -0
  21. package/dist/commands/apps/transfer.test.js +123 -0
  22. package/dist/commands/apps/unlink.js +35 -0
  23. package/dist/commands/apps/unlink.test.js +99 -0
  24. package/dist/commands/manifests/generate.js +1 -1
  25. package/dist/index.js +3 -0
  26. package/dist/services/app-build-sources.js +120 -0
  27. package/dist/services/app-devices.js +8 -0
  28. package/dist/services/apps.js +25 -0
  29. package/dist/services/authorization-service.js +5 -1
  30. package/dist/services/jobs.js +13 -0
  31. package/dist/types/app-build-source.js +1 -0
  32. package/dist/types/index.js +1 -0
  33. package/dist/utils/custom-properties.js +22 -0
  34. package/dist/utils/file.js +8 -1
  35. package/dist/utils/git.js +91 -0
  36. package/dist/utils/git.test.js +130 -0
  37. package/dist/utils/{build.js → job.js} +26 -23
  38. package/dist/utils/prompt.js +1 -1
  39. package/dist/utils/zip.js +19 -2
  40. 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 appBuildsService from '../services/app-builds.js';
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
- export const waitForBuildCompletion = async (options) => {
6
- const { appId, appBuildId, relations = 'job,job.jobLogs' } = options;
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 build = await appBuildsService.findOne({
12
- appId,
13
- appBuildId,
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 build to start (status: ${jobStatus})...`);
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('Build started...');
33
+ consola.success(`${capitalize(label)} started...`);
31
34
  }
32
- if (build.job.jobLogs && build.job.jobLogs.length > 0) {
33
- const newLogs = build.job.jobLogs
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 build;
51
+ return job;
49
52
  }
50
53
  else if (jobStatus === 'failed') {
51
- consola.error('Build failed.');
54
+ consola.error(`${capitalize(label)} failed.`);
52
55
  process.exit(1);
53
56
  }
54
57
  else if (jobStatus === 'canceled') {
55
- consola.warn('Build was canceled.');
58
+ consola.error(`${capitalize(label)} was canceled.`);
56
59
  process.exit(1);
57
60
  }
58
61
  else if (jobStatus === 'rejected') {
59
- consola.error('Build was rejected.');
62
+ consola.error(`${capitalize(label)} was rejected.`);
60
63
  process.exit(1);
61
64
  }
62
65
  else if (jobStatus === 'timed_out') {
63
- consola.error('Build timed out.');
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 build status:', error);
73
+ consola.error('Error polling job status:', error);
71
74
  process.exit(1);
72
75
  }
73
76
  }
@@ -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
- isZipped(path) {
9
- return path.endsWith('.zip');
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.6.0-dev.bda9e9d.1774345600",
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",