@capgo/cli 4.8.0 → 4.8.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 +2 -0
- package/dist/index.js +86 -18
- package/package.json +1 -1
- package/src/bundle/upload.ts +27 -18
- package/src/index.ts +1 -0
- package/src/utils.ts +91 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [4.8.1](https://github.com/Cap-go/CLI/compare/v4.8.0...v4.8.1) (2024-05-11)
|
|
6
|
+
|
|
5
7
|
## [4.8.0](https://github.com/Cap-go/CLI/compare/v4.7.0...v4.8.0) (2024-05-10)
|
|
6
8
|
|
|
7
9
|
|
package/dist/index.js
CHANGED
|
@@ -92373,7 +92373,7 @@ var {
|
|
|
92373
92373
|
// package.json
|
|
92374
92374
|
var package_default = {
|
|
92375
92375
|
name: "@capgo/cli",
|
|
92376
|
-
version: "4.8.
|
|
92376
|
+
version: "4.8.1",
|
|
92377
92377
|
description: "A CLI to upload to capgo servers",
|
|
92378
92378
|
author: "github.com/riderx",
|
|
92379
92379
|
license: "Apache 2.0",
|
|
@@ -93916,7 +93916,8 @@ async function updateOrCreateVersion(supabase, update) {
|
|
|
93916
93916
|
async function uploadUrl(supabase, appId, name) {
|
|
93917
93917
|
const data = {
|
|
93918
93918
|
app_id: appId,
|
|
93919
|
-
name
|
|
93919
|
+
name,
|
|
93920
|
+
version: 0
|
|
93920
93921
|
};
|
|
93921
93922
|
try {
|
|
93922
93923
|
const pathUploadLink = "private/upload_link";
|
|
@@ -93927,6 +93928,52 @@ async function uploadUrl(supabase, appId, name) {
|
|
|
93927
93928
|
}
|
|
93928
93929
|
return "";
|
|
93929
93930
|
}
|
|
93931
|
+
async function prepareMultipart(supabase, appId, name) {
|
|
93932
|
+
const data = {
|
|
93933
|
+
app_id: appId,
|
|
93934
|
+
name,
|
|
93935
|
+
version: 1
|
|
93936
|
+
};
|
|
93937
|
+
try {
|
|
93938
|
+
const pathUploadLink = "private/upload_link";
|
|
93939
|
+
const res = await supabase.functions.invoke(pathUploadLink, { body: JSON.stringify(data) });
|
|
93940
|
+
return res.data;
|
|
93941
|
+
} catch (error) {
|
|
93942
|
+
f2.error(`Cannot get upload url ${formatError(error)}`);
|
|
93943
|
+
return null;
|
|
93944
|
+
}
|
|
93945
|
+
}
|
|
93946
|
+
async function finishMultipartDownload(key2, uploadId, url, parts) {
|
|
93947
|
+
const metadata = {
|
|
93948
|
+
action: "mpu-complete",
|
|
93949
|
+
uploadId,
|
|
93950
|
+
key: key2
|
|
93951
|
+
};
|
|
93952
|
+
await distribution_default.post(url, {
|
|
93953
|
+
json: {
|
|
93954
|
+
parts
|
|
93955
|
+
},
|
|
93956
|
+
searchParams: new URLSearchParams({ body: btoa(JSON.stringify(metadata)) })
|
|
93957
|
+
});
|
|
93958
|
+
}
|
|
93959
|
+
var PART_SIZE = 10 * 1024 * 1024;
|
|
93960
|
+
async function uploadMultipart(supabase, appId, name, data) {
|
|
93961
|
+
try {
|
|
93962
|
+
const multipartPrep = await prepareMultipart(supabase, appId, name);
|
|
93963
|
+
if (!multipartPrep) {
|
|
93964
|
+
return false;
|
|
93965
|
+
}
|
|
93966
|
+
const fileSize = data.length;
|
|
93967
|
+
const partCount = Math.ceil(fileSize / PART_SIZE);
|
|
93968
|
+
const uploadPromises = Array.from({ length: partCount }, (_3, index) => uploadPart(data, PART_SIZE, multipartPrep.url, multipartPrep.key, multipartPrep.uploadId, index));
|
|
93969
|
+
const parts = await Promise.all(uploadPromises);
|
|
93970
|
+
await finishMultipartDownload(multipartPrep.key, multipartPrep.uploadId, multipartPrep.url, parts);
|
|
93971
|
+
return true;
|
|
93972
|
+
} catch (e2) {
|
|
93973
|
+
f2.error(`Could not upload via multipart ${formatError(e2)}`);
|
|
93974
|
+
return false;
|
|
93975
|
+
}
|
|
93976
|
+
}
|
|
93930
93977
|
async function deletedFailedVersion(supabase, appId, name) {
|
|
93931
93978
|
const data = {
|
|
93932
93979
|
app_id: appId,
|
|
@@ -93941,6 +93988,23 @@ async function deletedFailedVersion(supabase, appId, name) {
|
|
|
93941
93988
|
return Promise.reject(new Error("Cannot delete failed version"));
|
|
93942
93989
|
}
|
|
93943
93990
|
}
|
|
93991
|
+
async function uploadPart(data, partsize, url, key2, uploadId, index) {
|
|
93992
|
+
const dataToUpload = data.subarray(
|
|
93993
|
+
partsize * index,
|
|
93994
|
+
partsize * (index + 1)
|
|
93995
|
+
);
|
|
93996
|
+
const metadata = {
|
|
93997
|
+
action: "mpu-uploadpart",
|
|
93998
|
+
uploadId,
|
|
93999
|
+
partNumber: index + 1,
|
|
94000
|
+
key: key2
|
|
94001
|
+
};
|
|
94002
|
+
const response = await distribution_default.put(url, {
|
|
94003
|
+
body: dataToUpload,
|
|
94004
|
+
searchParams: new URLSearchParams({ body: btoa(JSON.stringify(metadata)) })
|
|
94005
|
+
});
|
|
94006
|
+
return await response.json();
|
|
94007
|
+
}
|
|
93944
94008
|
async function updateOrCreateChannel(supabase, update) {
|
|
93945
94009
|
if (!update.app_id || !update.name || !update.created_by) {
|
|
93946
94010
|
f2.error("missing app_id, name, or created_by");
|
|
@@ -95066,23 +95130,27 @@ The app size is ${mbSize} Mb, this may take a while to download for users
|
|
|
95066
95130
|
if (!external && zipped) {
|
|
95067
95131
|
const spinner = de();
|
|
95068
95132
|
spinner.start(`Uploading Bundle`);
|
|
95069
|
-
const url = await uploadUrl(supabase, appid, bundle2);
|
|
95070
|
-
if (!url) {
|
|
95071
|
-
f2.error(`Cannot get upload url`);
|
|
95072
|
-
program.error("");
|
|
95073
|
-
}
|
|
95074
95133
|
const startTime = import_node_perf_hooks.performance.now();
|
|
95075
95134
|
try {
|
|
95076
|
-
|
|
95077
|
-
|
|
95078
|
-
|
|
95079
|
-
|
|
95080
|
-
|
|
95081
|
-
|
|
95082
|
-
"
|
|
95083
|
-
|
|
95084
|
-
|
|
95085
|
-
|
|
95135
|
+
if (options.multipart !== void 0 && options.multipart) {
|
|
95136
|
+
await uploadMultipart(supabase, appid, bundle2, zipped);
|
|
95137
|
+
} else {
|
|
95138
|
+
const url = await uploadUrl(supabase, appid, bundle2);
|
|
95139
|
+
if (!url) {
|
|
95140
|
+
f2.error(`Cannot get upload url`);
|
|
95141
|
+
program.error("");
|
|
95142
|
+
}
|
|
95143
|
+
await distribution_default.put(url, {
|
|
95144
|
+
timeout: options.timeout || UPLOAD_TIMEOUT,
|
|
95145
|
+
retry: 5,
|
|
95146
|
+
body: zipped,
|
|
95147
|
+
headers: !localS3 ? {
|
|
95148
|
+
"Content-Type": "application/octet-stream",
|
|
95149
|
+
"Cache-Control": "public, max-age=456789, immutable",
|
|
95150
|
+
"x-amz-meta-crc32": checksum
|
|
95151
|
+
} : void 0
|
|
95152
|
+
});
|
|
95153
|
+
}
|
|
95086
95154
|
} catch (errorUpload) {
|
|
95087
95155
|
const endTime2 = import_node_perf_hooks.performance.now();
|
|
95088
95156
|
const uploadTime2 = ((endTime2 - startTime) / 1e3).toFixed(2);
|
|
@@ -96641,7 +96709,7 @@ var bundle = program.command("bundle").description("Manage bundle");
|
|
|
96641
96709
|
bundle.command("upload [appId]").alias("u").description("Upload a new bundle in Capgo Cloud").action(uploadCommand).option("-a, --apikey <apikey>", "apikey to link to your account").option("-p, --path <path>", "path of the folder to upload").option("-c, --channel <channel>", "channel to link to").option("-e, --external <url>", "link to external url intead of upload to Capgo Cloud").option("--iv-session-key <key>", "Set the iv and session key for bundle url external").option("--s3-region <region>", "Region for your AWS S3 bucket").option("--s3-apikey <apikey>", "apikey for your AWS S3 account").option("--s3-apisecret <apisecret>", "api secret for your AWS S3 account").option("--s3-bucket-name <bucketName>", "Name for your AWS S3 bucket").option("--key <key>", "custom path for public signing key").option("--key-data <keyData>", "base64 public signing key").option("--bundle-url", "prints bundle url into stdout").option("--no-key", "ignore signing key and send clear update").option("--no-code-check", "Ignore checking if notifyAppReady() is called in soure code and index present in root folder").option("--display-iv-session", "Show in the console the iv and session key used to encrypt the update").option("-b, --bundle <bundle>", "bundle version number of the bundle to upload").option(
|
|
96642
96710
|
"--min-update-version <minUpdateVersion>",
|
|
96643
96711
|
"Minimal version required to update to this version. Used only if the disable auto update is set to metadata in channel"
|
|
96644
|
-
).option("--auto-min-update-version", "Set the min update version based on native packages").option("--ignore-metadata-check", "Ignores the metadata (node_modules) check when uploading").option("--timeout <timeout>", "Timeout for the upload process in seconds");
|
|
96712
|
+
).option("--auto-min-update-version", "Set the min update version based on native packages").option("--ignore-metadata-check", "Ignores the metadata (node_modules) check when uploading").option("--timeout <timeout>", "Timeout for the upload process in seconds").option("--multipart", "Uses multipart protocol to upload data to S3");
|
|
96645
96713
|
bundle.command("compatibility [appId]").action(checkCompatibilityCommand).option("-a, --apikey <apikey>", "apikey to link to your account").option("-c, --channel <channel>", "channel to check the compatibility with").option("--text", "output text instead of emojis");
|
|
96646
96714
|
bundle.command("delete [bundleId] [appId]").alias("d").description("Delete a bundle in Capgo Cloud").action(deleteBundle).option("-a, --apikey <apikey>", "apikey to link to your account");
|
|
96647
96715
|
bundle.command("list [appId]").alias("l").description("List bundle in Capgo Cloud").action(listBundle).option("-a, --apikey <apikey>", "apikey to link to your account");
|
package/package.json
CHANGED
package/src/bundle/upload.ts
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
requireUpdateMetadata,
|
|
40
40
|
updateOrCreateChannel,
|
|
41
41
|
updateOrCreateVersion,
|
|
42
|
+
uploadMultipart,
|
|
42
43
|
uploadUrl,
|
|
43
44
|
useLogSnag,
|
|
44
45
|
verifyUser,
|
|
@@ -66,6 +67,7 @@ interface Options extends OptionsBase {
|
|
|
66
67
|
autoMinUpdateVersion?: boolean
|
|
67
68
|
ignoreMetadataCheck?: boolean
|
|
68
69
|
timeout?: number
|
|
70
|
+
multipart?: boolean
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
const UPLOAD_TIMEOUT = 120000
|
|
@@ -381,26 +383,32 @@ It will be also visible in your dashboard\n`)
|
|
|
381
383
|
if (!external && zipped) {
|
|
382
384
|
const spinner = p.spinner()
|
|
383
385
|
spinner.start(`Uploading Bundle`)
|
|
384
|
-
|
|
385
|
-
const url = await uploadUrl(supabase, appid, bundle)
|
|
386
|
-
if (!url) {
|
|
387
|
-
p.log.error(`Cannot get upload url`)
|
|
388
|
-
program.error('')
|
|
389
|
-
}
|
|
390
386
|
const startTime = performance.now()
|
|
387
|
+
|
|
391
388
|
try {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
389
|
+
if (options.multipart !== undefined && options.multipart) {
|
|
390
|
+
await uploadMultipart(supabase, appid, bundle, zipped)
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
const url = await uploadUrl(supabase, appid, bundle)
|
|
394
|
+
if (!url) {
|
|
395
|
+
p.log.error(`Cannot get upload url`)
|
|
396
|
+
program.error('')
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
await ky.put(url, {
|
|
400
|
+
timeout: options.timeout || UPLOAD_TIMEOUT,
|
|
401
|
+
retry: 5,
|
|
402
|
+
body: zipped,
|
|
403
|
+
headers: (!localS3
|
|
404
|
+
? {
|
|
405
|
+
'Content-Type': 'application/octet-stream',
|
|
406
|
+
'Cache-Control': 'public, max-age=456789, immutable',
|
|
407
|
+
'x-amz-meta-crc32': checksum,
|
|
408
|
+
}
|
|
409
|
+
: undefined),
|
|
410
|
+
})
|
|
411
|
+
}
|
|
404
412
|
}
|
|
405
413
|
catch (errorUpload) {
|
|
406
414
|
const endTime = performance.now()
|
|
@@ -411,6 +419,7 @@ It will be also visible in your dashboard\n`)
|
|
|
411
419
|
await deletedFailedVersion(supabase, appid, bundle)
|
|
412
420
|
program.error('')
|
|
413
421
|
}
|
|
422
|
+
|
|
414
423
|
versionData.storage_provider = 'r2'
|
|
415
424
|
const { error: dbError2 } = await updateOrCreateVersion(supabase, versionData)
|
|
416
425
|
if (dbError2) {
|
package/src/index.ts
CHANGED
|
@@ -131,6 +131,7 @@ bundle
|
|
|
131
131
|
.option('--auto-min-update-version', 'Set the min update version based on native packages')
|
|
132
132
|
.option('--ignore-metadata-check', 'Ignores the metadata (node_modules) check when uploading')
|
|
133
133
|
.option('--timeout <timeout>', 'Timeout for the upload process in seconds')
|
|
134
|
+
.option('--multipart', 'Uses multipart protocol to upload data to S3')
|
|
134
135
|
|
|
135
136
|
bundle
|
|
136
137
|
.command('compatibility [appId]')
|
package/src/utils.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
|
2
2
|
import { homedir } from 'node:os'
|
|
3
3
|
import { resolve } from 'node:path'
|
|
4
4
|
import process from 'node:process'
|
|
5
|
+
import type { Buffer } from 'node:buffer'
|
|
5
6
|
import { loadConfig } from '@capacitor/cli/dist/config'
|
|
6
7
|
import { program } from 'commander'
|
|
7
8
|
import type { SupabaseClient } from '@supabase/supabase-js'
|
|
@@ -483,6 +484,7 @@ export async function uploadUrl(supabase: SupabaseClient<Database>, appId: strin
|
|
|
483
484
|
const data = {
|
|
484
485
|
app_id: appId,
|
|
485
486
|
name,
|
|
487
|
+
version: 0,
|
|
486
488
|
}
|
|
487
489
|
try {
|
|
488
490
|
const pathUploadLink = 'private/upload_link'
|
|
@@ -495,6 +497,67 @@ export async function uploadUrl(supabase: SupabaseClient<Database>, appId: strin
|
|
|
495
497
|
return ''
|
|
496
498
|
}
|
|
497
499
|
|
|
500
|
+
async function prepareMultipart(supabase: SupabaseClient<Database>, appId: string, name: string): Promise<{ key: string, uploadId: string, url: string } | null> {
|
|
501
|
+
const data = {
|
|
502
|
+
app_id: appId,
|
|
503
|
+
name,
|
|
504
|
+
version: 1,
|
|
505
|
+
}
|
|
506
|
+
try {
|
|
507
|
+
const pathUploadLink = 'private/upload_link'
|
|
508
|
+
const res = await supabase.functions.invoke(pathUploadLink, { body: JSON.stringify(data) })
|
|
509
|
+
return res.data as any
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
p.log.error(`Cannot get upload url ${formatError(error)}`)
|
|
513
|
+
return null
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
async function finishMultipartDownload(key: string, uploadId: string, url: string, parts: any[]) {
|
|
518
|
+
const metadata = {
|
|
519
|
+
action: 'mpu-complete',
|
|
520
|
+
uploadId,
|
|
521
|
+
key,
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
await ky.post(url, {
|
|
525
|
+
json: {
|
|
526
|
+
parts,
|
|
527
|
+
},
|
|
528
|
+
searchParams: new URLSearchParams({ body: btoa(JSON.stringify(metadata)) }),
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
// console.log(await response.json())
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const PART_SIZE = 10 * 1024 * 1024
|
|
535
|
+
export async function uploadMultipart(supabase: SupabaseClient<Database>, appId: string, name: string, data: Buffer): Promise<boolean> {
|
|
536
|
+
try {
|
|
537
|
+
const multipartPrep = await prepareMultipart(supabase, appId, name)
|
|
538
|
+
if (!multipartPrep) {
|
|
539
|
+
// Just pass the error
|
|
540
|
+
return false
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const fileSize = data.length
|
|
544
|
+
const partCount = Math.ceil(fileSize / PART_SIZE)
|
|
545
|
+
|
|
546
|
+
const uploadPromises = Array.from({ length: partCount }, (_, index) =>
|
|
547
|
+
uploadPart(data, PART_SIZE, multipartPrep.url, multipartPrep.key, multipartPrep.uploadId, index))
|
|
548
|
+
|
|
549
|
+
const parts = await Promise.all(uploadPromises)
|
|
550
|
+
|
|
551
|
+
await finishMultipartDownload(multipartPrep.key, multipartPrep.uploadId, multipartPrep.url, parts)
|
|
552
|
+
|
|
553
|
+
return true
|
|
554
|
+
}
|
|
555
|
+
catch (e) {
|
|
556
|
+
p.log.error(`Could not upload via multipart ${formatError(e)}`)
|
|
557
|
+
return false
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
498
561
|
export async function deletedFailedVersion(supabase: SupabaseClient<Database>, appId: string, name: string): Promise<void> {
|
|
499
562
|
const data = {
|
|
500
563
|
app_id: appId,
|
|
@@ -511,6 +574,34 @@ export async function deletedFailedVersion(supabase: SupabaseClient<Database>, a
|
|
|
511
574
|
}
|
|
512
575
|
}
|
|
513
576
|
|
|
577
|
+
async function uploadPart(
|
|
578
|
+
data: Buffer,
|
|
579
|
+
partsize: number,
|
|
580
|
+
url: string,
|
|
581
|
+
key: string,
|
|
582
|
+
uploadId: string,
|
|
583
|
+
index: number,
|
|
584
|
+
) {
|
|
585
|
+
const dataToUpload = data.subarray(
|
|
586
|
+
partsize * index,
|
|
587
|
+
partsize * (index + 1),
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
const metadata = {
|
|
591
|
+
action: 'mpu-uploadpart',
|
|
592
|
+
uploadId,
|
|
593
|
+
partNumber: index + 1,
|
|
594
|
+
key,
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
const response = await ky.put(url, {
|
|
598
|
+
body: dataToUpload,
|
|
599
|
+
searchParams: new URLSearchParams({ body: btoa(JSON.stringify(metadata)) }),
|
|
600
|
+
})
|
|
601
|
+
|
|
602
|
+
return await response.json()
|
|
603
|
+
}
|
|
604
|
+
|
|
514
605
|
export async function updateOrCreateChannel(supabase: SupabaseClient<Database>, update: Database['public']['Tables']['channels']['Insert']) {
|
|
515
606
|
// console.log('updateOrCreateChannel', update)
|
|
516
607
|
if (!update.app_id || !update.name || !update.created_by) {
|