@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 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.0",
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
- await distribution_default.put(url, {
95077
- timeout: options.timeout || UPLOAD_TIMEOUT,
95078
- retry: 5,
95079
- body: zipped,
95080
- headers: !localS3 ? {
95081
- "Content-Type": "application/octet-stream",
95082
- "Cache-Control": "public, max-age=456789, immutable",
95083
- "x-amz-meta-crc32": checksum
95084
- } : void 0
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/cli",
3
- "version": "4.8.0",
3
+ "version": "4.8.1",
4
4
  "description": "A CLI to upload to capgo servers",
5
5
  "author": "github.com/riderx",
6
6
  "license": "Apache 2.0",
@@ -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
- await ky.put(url, {
393
- timeout: options.timeout || UPLOAD_TIMEOUT,
394
- retry: 5,
395
- body: zipped,
396
- headers: (!localS3
397
- ? {
398
- 'Content-Type': 'application/octet-stream',
399
- 'Cache-Control': 'public, max-age=456789, immutable',
400
- 'x-amz-meta-crc32': checksum,
401
- }
402
- : undefined),
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) {