@capgo/cli 7.18.23 → 7.19.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 (95) hide show
  1. package/dist/index.js +214 -214
  2. package/dist/src/sdk.js +1 -1
  3. package/package.json +1 -1
  4. package/dist/package.json +0 -86
  5. package/dist/src/api/app.js +0 -45
  6. package/dist/src/api/app.js.map +0 -1
  7. package/dist/src/api/channels.js +0 -167
  8. package/dist/src/api/channels.js.map +0 -1
  9. package/dist/src/api/cryptoV2.js +0 -102
  10. package/dist/src/api/cryptoV2.js.map +0 -1
  11. package/dist/src/api/update.js +0 -13
  12. package/dist/src/api/update.js.map +0 -1
  13. package/dist/src/api/versions.js +0 -92
  14. package/dist/src/api/versions.js.map +0 -1
  15. package/dist/src/app/add.js +0 -150
  16. package/dist/src/app/add.js.map +0 -1
  17. package/dist/src/app/debug.js +0 -222
  18. package/dist/src/app/debug.js.map +0 -1
  19. package/dist/src/app/delete.js +0 -89
  20. package/dist/src/app/delete.js.map +0 -1
  21. package/dist/src/app/info.js +0 -84
  22. package/dist/src/app/info.js.map +0 -1
  23. package/dist/src/app/list.js +0 -48
  24. package/dist/src/app/list.js.map +0 -1
  25. package/dist/src/app/set.js +0 -96
  26. package/dist/src/app/set.js.map +0 -1
  27. package/dist/src/app/setting.js +0 -50
  28. package/dist/src/app/setting.js.map +0 -1
  29. package/dist/src/bundle/check.js +0 -30
  30. package/dist/src/bundle/check.js.map +0 -1
  31. package/dist/src/bundle/cleanup.js +0 -105
  32. package/dist/src/bundle/cleanup.js.map +0 -1
  33. package/dist/src/bundle/compatibility.js +0 -62
  34. package/dist/src/bundle/compatibility.js.map +0 -1
  35. package/dist/src/bundle/decryptV2.js +0 -76
  36. package/dist/src/bundle/decryptV2.js.map +0 -1
  37. package/dist/src/bundle/delete.js +0 -40
  38. package/dist/src/bundle/delete.js.map +0 -1
  39. package/dist/src/bundle/encryptV2.js +0 -108
  40. package/dist/src/bundle/encryptV2.js.map +0 -1
  41. package/dist/src/bundle/list.js +0 -36
  42. package/dist/src/bundle/list.js.map +0 -1
  43. package/dist/src/bundle/partial.js +0 -277
  44. package/dist/src/bundle/partial.js.map +0 -1
  45. package/dist/src/bundle/unlink.js +0 -70
  46. package/dist/src/bundle/unlink.js.map +0 -1
  47. package/dist/src/bundle/upload.js +0 -940
  48. package/dist/src/bundle/upload.js.map +0 -1
  49. package/dist/src/bundle/upload_interface.js +0 -2
  50. package/dist/src/bundle/upload_interface.js.map +0 -1
  51. package/dist/src/bundle/zip.js +0 -156
  52. package/dist/src/bundle/zip.js.map +0 -1
  53. package/dist/src/channel/add.js +0 -68
  54. package/dist/src/channel/add.js.map +0 -1
  55. package/dist/src/channel/currentBundle.js +0 -54
  56. package/dist/src/channel/currentBundle.js.map +0 -1
  57. package/dist/src/channel/delete.js +0 -77
  58. package/dist/src/channel/delete.js.map +0 -1
  59. package/dist/src/channel/list.js +0 -45
  60. package/dist/src/channel/list.js.map +0 -1
  61. package/dist/src/channel/set.js +0 -220
  62. package/dist/src/channel/set.js.map +0 -1
  63. package/dist/src/checksum.js +0 -46
  64. package/dist/src/checksum.js.map +0 -1
  65. package/dist/src/config/index.js +0 -31
  66. package/dist/src/config/index.js.map +0 -1
  67. package/dist/src/docs.js +0 -280
  68. package/dist/src/docs.js.map +0 -1
  69. package/dist/src/index.js +0 -522
  70. package/dist/src/index.js.map +0 -1
  71. package/dist/src/init.js +0 -803
  72. package/dist/src/init.js.map +0 -1
  73. package/dist/src/keyV2.js +0 -163
  74. package/dist/src/keyV2.js.map +0 -1
  75. package/dist/src/login.js +0 -51
  76. package/dist/src/login.js.map +0 -1
  77. package/dist/src/organisation/add.js +0 -82
  78. package/dist/src/organisation/add.js.map +0 -1
  79. package/dist/src/organisation/delete.js +0 -91
  80. package/dist/src/organisation/delete.js.map +0 -1
  81. package/dist/src/organisation/index.js +0 -5
  82. package/dist/src/organisation/index.js.map +0 -1
  83. package/dist/src/organisation/list.js +0 -60
  84. package/dist/src/organisation/list.js.map +0 -1
  85. package/dist/src/organisation/set.js +0 -95
  86. package/dist/src/organisation/set.js.map +0 -1
  87. package/dist/src/sdk.js.map +0 -1
  88. package/dist/src/types/supabase.types.js +0 -86
  89. package/dist/src/types/supabase.types.js.map +0 -1
  90. package/dist/src/user/account.js +0 -31
  91. package/dist/src/user/account.js.map +0 -1
  92. package/dist/src/utils/latest-version.js +0 -26
  93. package/dist/src/utils/latest-version.js.map +0 -1
  94. package/dist/src/utils.js +0 -1222
  95. package/dist/src/utils.js.map +0 -1
@@ -1,940 +0,0 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { existsSync, readFileSync } from 'node:fs';
3
- import { join } from 'node:path/posix';
4
- import { cwd } from 'node:process';
5
- import { S3Client } from '@bradenmacdonald/s3-lite-client';
6
- import { intro, log, outro, spinner as spinnerC } from '@clack/prompts';
7
- import { greaterOrEqual, parse } from '@std/semver';
8
- // Native fetch is available in Node.js >= 18
9
- import pack from '../../package.json';
10
- import { checkAppExistsAndHasPermissionOrgErr } from '../api/app';
11
- import { encryptChecksumV2, encryptSourceV2, generateSessionKey } from '../api/cryptoV2';
12
- import { checkAlerts } from '../api/update';
13
- import { checksum as getChecksum } from '../checksum';
14
- import { baseKeyV2, checkChecksum, checkCompatibility, checkPlanValidUpload, checkRemoteCliMessages, createSupabaseClient, deletedFailedVersion, findRoot, findSavedKey, formatError, getAllPackagesDependencies, getAppId, getBundleVersion, getConfig, getLocalConfig, getLocalDepenencies, getOrganizationId, getPMAndCommand, getRemoteFileConfig, hasOrganizationPerm, isCompatible, OrganizationPerm, PACKNAME, regexSemver, sendEvent, updateConfigUpdater, updateOrCreateChannel, updateOrCreateVersion, UPLOAD_TIMEOUT, uploadTUS, uploadUrl, verifyUser, zipFile } from '../utils';
15
- import { checkIndexPosition, searchInDirectory } from './check';
16
- import { prepareBundlePartialFiles, uploadPartial } from './partial';
17
- function uploadFail(message) {
18
- log.error(message);
19
- throw new Error(message);
20
- }
21
- async function getBundle(config, options) {
22
- const pkgVersion = getBundleVersion('', options.packageJson);
23
- // create bundle name format : 1.0.0-beta.x where x is a uuid
24
- const bundle = options.bundle
25
- || config?.plugins?.CapacitorUpdater?.version
26
- || pkgVersion
27
- || `0.0.1-beta.${randomUUID().split('-')[0]}`;
28
- if (!regexSemver.test(bundle)) {
29
- uploadFail(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`);
30
- }
31
- return bundle;
32
- }
33
- function getApikey(options) {
34
- const apikey = options.apikey || findSavedKey();
35
- if (!apikey) {
36
- uploadFail('Missing API key, you need to provide an API key to upload your bundle');
37
- }
38
- return apikey;
39
- }
40
- function getAppIdAndPath(appId, options, config) {
41
- const finalAppId = getAppId(appId, config);
42
- const path = options.path || config?.webDir;
43
- if (!finalAppId) {
44
- uploadFail('Missing argument, you need to provide a appid or be in a capacitor project');
45
- }
46
- if (!path) {
47
- uploadFail('Missing argument, you need to provide a path (--path), or be in a capacitor project');
48
- }
49
- if (!existsSync(path)) {
50
- uploadFail(`Path ${path} does not exist, build your app first, or provide a valid path`);
51
- }
52
- return { appid: finalAppId, path };
53
- }
54
- function checkNotifyAppReady(options, path) {
55
- const checkNotifyAppReady = options.codeCheck;
56
- if (typeof checkNotifyAppReady === 'undefined' || checkNotifyAppReady) {
57
- const isPluginConfigured = searchInDirectory(path, 'notifyAppReady');
58
- if (!isPluginConfigured) {
59
- uploadFail(`notifyAppReady() is missing in the build folder of your app. see: https://capgo.app/docs/plugin/api/#notifyappready
60
- If you are sure your app has this code, you can use the --no-code-check option`);
61
- }
62
- const foundIndex = checkIndexPosition(path);
63
- if (!foundIndex) {
64
- uploadFail(`index.html is missing in the root folder of ${path}`);
65
- }
66
- }
67
- }
68
- async function verifyCompatibility(supabase, pm, options, channel, appid, bundle) {
69
- // Check compatibility here
70
- const ignoreMetadataCheck = options.ignoreMetadataCheck;
71
- const autoMinUpdateVersion = options.autoMinUpdateVersion;
72
- let minUpdateVersion = options.minUpdateVersion;
73
- const { data: channelData, error: channelError } = await supabase
74
- .from('channels')
75
- .select('disable_auto_update, version ( min_update_version, native_packages )')
76
- .eq('name', channel)
77
- .eq('app_id', appid)
78
- .single();
79
- const updateMetadataRequired = !!channelData && channelData.disable_auto_update === 'version_number';
80
- let localDependencies;
81
- let finalCompatibility;
82
- // We only check compatibility IF the channel exists
83
- if (!channelError && channelData && channelData.version && channelData.version.native_packages && !ignoreMetadataCheck) {
84
- const spinner = spinnerC();
85
- spinner.start(`Checking bundle compatibility with channel ${channel}`);
86
- const { finalCompatibility: finalCompatibilityWithChannel, localDependencies: localDependenciesWithChannel, } = await checkCompatibility(supabase, appid, channel, options.packageJson, options.nodeModules);
87
- finalCompatibility = finalCompatibilityWithChannel;
88
- localDependencies = localDependenciesWithChannel;
89
- // Check if any package is incompatible
90
- if (finalCompatibility.find(x => !isCompatible(x))) {
91
- spinner.stop(`Bundle NOT compatible with ${channel} channel`);
92
- log.warn(`You can check compatibility with "${pm.runner} @capgo/cli bundle compatibility"`);
93
- if (autoMinUpdateVersion) {
94
- minUpdateVersion = bundle;
95
- log.info(`Auto set min-update-version to ${minUpdateVersion}`);
96
- }
97
- }
98
- else if (autoMinUpdateVersion) {
99
- try {
100
- const { min_update_version: lastMinUpdateVersion } = channelData.version;
101
- if (!lastMinUpdateVersion || !regexSemver.test(lastMinUpdateVersion))
102
- uploadFail('Invalid remote min update version, skipping auto setting compatibility');
103
- minUpdateVersion = lastMinUpdateVersion;
104
- spinner.stop(`Auto set min-update-version to ${minUpdateVersion}`);
105
- }
106
- catch {
107
- uploadFail(`Cannot auto set compatibility, invalid data ${channelData}`);
108
- }
109
- }
110
- else {
111
- spinner.stop(`Bundle compatible with ${channel} channel`);
112
- }
113
- }
114
- else if (!ignoreMetadataCheck) {
115
- log.warn(`Channel ${channel} is new or it's your first upload with compatibility check, it will be ignored this time`);
116
- localDependencies = await getLocalDepenencies(options.packageJson, options.nodeModules);
117
- if (autoMinUpdateVersion) {
118
- minUpdateVersion = bundle;
119
- log.info(`Auto set min-update-version to ${minUpdateVersion}`);
120
- }
121
- }
122
- if (updateMetadataRequired && !minUpdateVersion && !ignoreMetadataCheck) {
123
- uploadFail('You need to provide a min-update-version to upload a bundle to this channel');
124
- }
125
- if (minUpdateVersion) {
126
- if (!regexSemver.test(minUpdateVersion))
127
- uploadFail(`Your minimal version update ${minUpdateVersion}, is not valid it should follow semver convention : https://semver.org/`);
128
- }
129
- const hashedLocalDependencies = localDependencies
130
- ? new Map(localDependencies
131
- .filter(a => !!a.native && a.native !== undefined)
132
- .map(a => [a.name, a]))
133
- : new Map();
134
- const nativePackages = (hashedLocalDependencies.size > 0 || !options.ignoreMetadataCheck) ? Array.from(hashedLocalDependencies, ([name, value]) => ({ name, version: value.version })) : undefined;
135
- return { nativePackages, minUpdateVersion };
136
- }
137
- async function checkTrial(supabase, orgId, localConfig) {
138
- const { data: isTrial, error: isTrialsError } = await supabase
139
- .rpc('is_trial_org', { orgid: orgId })
140
- .single();
141
- if ((isTrial && isTrial > 0) || isTrialsError) {
142
- // TODO: Come back to this to fix for orgs v3
143
- log.warn(`WARNING !!\nTrial expires in ${isTrial} days`);
144
- log.warn(`Upgrade here: ${localConfig.hostWeb}/dashboard/settings/plans?oid=${orgId}`);
145
- }
146
- }
147
- async function checkVersionExists(supabase, appid, bundle, versionExistsOk = false) {
148
- // check if app already exist
149
- // apikey is sooo legacy code, current prod does not use it
150
- // TODO: remove apikey and create a new function who not need it
151
- const { data: appVersion, error: appVersionError } = await supabase
152
- .rpc('exist_app_versions', { appid, apikey: '', name_version: bundle })
153
- .single();
154
- if (appVersion || appVersionError) {
155
- if (versionExistsOk) {
156
- log.warn(`Version ${bundle} already exists - exiting gracefully due to --silent-fail option`);
157
- outro('Bundle version already exists - exiting gracefully 🎉');
158
- return true;
159
- }
160
- uploadFail(`Version ${bundle} already exists ${formatError(appVersionError)}`);
161
- }
162
- return false;
163
- }
164
- async function prepareBundleFile(path, options, apikey, orgId, appid, maxUploadLength, alertUploadSize) {
165
- let ivSessionKey;
166
- let sessionKey;
167
- let checksum = '';
168
- let zipped = null;
169
- let encryptionMethod = 'none';
170
- let finalKeyData = '';
171
- const keyV2 = options.keyV2;
172
- const noKey = options.key === false;
173
- zipped = await zipFile(path);
174
- const s = spinnerC();
175
- s.start(`Calculating checksum`);
176
- const root = join(findRoot(cwd()), PACKNAME);
177
- // options.packageJson
178
- const dependencies = await getAllPackagesDependencies(undefined, options.packageJson || root);
179
- const updaterVersion = dependencies.get('@capgo/capacitor-updater');
180
- let useSha256 = false;
181
- let coerced;
182
- try {
183
- coerced = updaterVersion ? parse(updaterVersion) : undefined;
184
- }
185
- catch {
186
- coerced = undefined;
187
- }
188
- if (!updaterVersion) {
189
- uploadFail('Cannot find @capgo/capacitor-updater in ./package.json, provide the package.json path with --package-json it\'s required for v7 CLI to work');
190
- }
191
- else if (coerced) {
192
- // Use SHA256 for v6.25.0+ and v7.0.0+
193
- useSha256 = greaterOrEqual(coerced, parse('6.25.0'));
194
- }
195
- else if (updaterVersion === 'link:@capgo/capacitor-updater') {
196
- log.warn('Using local @capgo/capacitor-updater. Assuming v7');
197
- useSha256 = true;
198
- }
199
- if (((keyV2 || options.keyDataV2 || existsSync(baseKeyV2)) && !noKey) || useSha256) {
200
- checksum = await getChecksum(zipped, 'sha256');
201
- }
202
- else {
203
- checksum = await getChecksum(zipped, 'crc32');
204
- }
205
- s.stop(`Checksum ${useSha256 ? 'SHA256' : 'CRC32'}: ${checksum}`);
206
- // key should be undefined or a string if false it should ingore encryption DO NOT REPLACE key === false With !key it will not work
207
- if (noKey) {
208
- log.info(`Encryption ignored`);
209
- }
210
- else if ((keyV2 || existsSync(baseKeyV2) || options.keyDataV2) && !options.oldEncryption) {
211
- const privateKey = typeof keyV2 === 'string' ? keyV2 : baseKeyV2;
212
- let keyDataV2 = options.keyDataV2 || '';
213
- if (!keyDataV2 && !existsSync(privateKey))
214
- uploadFail(`Cannot find private key ${privateKey}`);
215
- await sendEvent(apikey, {
216
- channel: 'app',
217
- event: 'App encryption v2',
218
- icon: '🔑',
219
- user_id: orgId,
220
- tags: {
221
- 'app-id': appid,
222
- },
223
- notify: false,
224
- }, options.verbose);
225
- if (!keyDataV2) {
226
- const keyFile = readFileSync(privateKey);
227
- keyDataV2 = keyFile.toString();
228
- }
229
- log.info('Encrypting your bundle with V2');
230
- const { sessionKey: sKey, ivSessionKey: ivKey } = generateSessionKey(keyDataV2);
231
- const encryptedData = encryptSourceV2(zipped, sKey, ivKey);
232
- checksum = encryptChecksumV2(checksum, keyDataV2);
233
- ivSessionKey = ivKey;
234
- sessionKey = sKey;
235
- encryptionMethod = 'v2';
236
- finalKeyData = keyDataV2;
237
- if (options.displayIvSession) {
238
- log.info(`Your Iv Session key is ${ivSessionKey},
239
- keep it safe, you will need it to decrypt your bundle.
240
- It will be also visible in your dashboard\n`);
241
- }
242
- zipped = encryptedData;
243
- }
244
- const mbSize = Math.floor((zipped?.byteLength ?? 0) / 1024 / 1024);
245
- const mbSizeMax = Math.floor(maxUploadLength / 1024 / 1024);
246
- if (zipped?.byteLength > maxUploadLength) {
247
- uploadFail(`The bundle size is ${mbSize} Mb, this is greater than the maximum upload length ${mbSizeMax} Mb, please reduce the size of your bundle`);
248
- }
249
- else if (zipped?.byteLength > alertUploadSize) {
250
- log.warn(`WARNING !!\nThe bundle size is ${mbSize} Mb, this may take a while to download for users\n`);
251
- log.info(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`);
252
- if (options.verbose) {
253
- log.info(`[Verbose] Bundle size details:`);
254
- log.info(` - Actual size: ${mbSize} MB (${zipped?.byteLength} bytes)`);
255
- log.info(` - Alert threshold: ${Math.floor(alertUploadSize / 1024 / 1024)} MB`);
256
- log.info(` - Maximum allowed: ${mbSizeMax} MB`);
257
- log.info(`[Verbose] Sending 'App Too Large' event to analytics...`);
258
- }
259
- await sendEvent(apikey, {
260
- channel: 'app-error',
261
- event: 'App Too Large',
262
- icon: '🚛',
263
- user_id: orgId,
264
- tags: {
265
- 'app-id': appid,
266
- },
267
- notify: false,
268
- }, options.verbose);
269
- if (options.verbose)
270
- log.info(`[Verbose] Event sent successfully`);
271
- }
272
- else if (options.verbose) {
273
- log.info(`[Verbose] Bundle size OK: ${mbSize} MB (under ${Math.floor(alertUploadSize / 1024 / 1024)} MB alert threshold)`);
274
- }
275
- if (options.verbose)
276
- log.info(`[Verbose] Bundle preparation complete, returning bundle data`);
277
- return { zipped, ivSessionKey, sessionKey, checksum, encryptionMethod, finalKeyData };
278
- }
279
- async function uploadBundleToCapgoCloud(apikey, supabase, appid, bundle, orgId, zipped, options, tusChunkSize) {
280
- const spinner = spinnerC();
281
- spinner.start(`Uploading Bundle`);
282
- const startTime = performance.now();
283
- let isTus = false;
284
- if (options.verbose) {
285
- log.info(`[Verbose] uploadBundleToCapgoCloud called:`);
286
- log.info(` - Bundle size: ${Math.floor(zipped.byteLength / 1024)} KB`);
287
- log.info(` - App ID: ${appid}`);
288
- log.info(` - Bundle version: ${bundle}`);
289
- log.info(` - Chunk size: ${Math.floor(tusChunkSize / 1024 / 1024)} MB`);
290
- }
291
- if (options.dryUpload) {
292
- spinner.stop(`Dry run, bundle not uploaded\nBundle uploaded 💪 in 0 seconds`);
293
- if (options.verbose)
294
- log.info(`[Verbose] Dry upload mode - skipping actual upload`);
295
- return;
296
- }
297
- try {
298
- const localConfig = await getLocalConfig();
299
- if (options.verbose)
300
- log.info(`[Verbose] Local config retrieved for upload`);
301
- if ((options.multipart !== undefined && options.multipart) || (options.tus !== undefined && options.tus)) {
302
- if (options.multipart) {
303
- log.info(`Uploading bundle with multipart is deprecated, we upload with TUS instead`);
304
- }
305
- else {
306
- log.info(`Uploading bundle with TUS protocol`);
307
- }
308
- if (options.verbose) {
309
- log.info(`[Verbose] Starting TUS resumable upload...`);
310
- log.info(` - Host: ${localConfig.hostWeb}`);
311
- log.info(` - Chunk size: ${Math.floor(tusChunkSize / 1024 / 1024)} MB`);
312
- }
313
- await uploadTUS(apikey, zipped, orgId, appid, bundle, spinner, localConfig, tusChunkSize);
314
- isTus = true;
315
- if (options.verbose)
316
- log.info(`[Verbose] TUS upload completed, updating database with R2 path...`);
317
- const filePath = `orgs/${orgId}/apps/${appid}/${bundle}.zip`;
318
- const { error: changeError } = await supabase
319
- .from('app_versions')
320
- .update({ r2_path: filePath })
321
- .eq('name', bundle)
322
- .eq('app_id', appid);
323
- if (changeError) {
324
- log.error(`Cannot finish TUS upload ${formatError(changeError)}`);
325
- if (options.verbose)
326
- log.info(`[Verbose] Database update failed: ${formatError(changeError)}`);
327
- return Promise.reject(new Error('Cannot finish TUS upload'));
328
- }
329
- if (options.verbose)
330
- log.info(`[Verbose] Database updated with R2 path: ${filePath}`);
331
- }
332
- else {
333
- if (options.verbose)
334
- log.info(`[Verbose] Using standard upload (non-TUS), getting presigned URL...`);
335
- const url = await uploadUrl(supabase, appid, bundle);
336
- if (!url) {
337
- log.error(`Cannot get upload url`);
338
- if (options.verbose)
339
- log.info(`[Verbose] Failed to retrieve presigned upload URL from database`);
340
- return Promise.reject(new Error('Cannot get upload url'));
341
- }
342
- if (options.verbose) {
343
- log.info(`[Verbose] Presigned URL obtained, uploading via HTTP PUT...`);
344
- log.info(` - Timeout: ${options.timeout || UPLOAD_TIMEOUT}ms`);
345
- log.info(` - Retry attempts: 5`);
346
- log.info(` - Content-Type: application/zip`);
347
- }
348
- const controller = new AbortController();
349
- const timeoutId = setTimeout(() => controller.abort(), options.timeout || UPLOAD_TIMEOUT);
350
- try {
351
- const response = await fetch(url, {
352
- method: 'PUT',
353
- body: zipped,
354
- headers: {
355
- 'Content-Type': 'application/zip',
356
- },
357
- signal: controller.signal,
358
- });
359
- if (!response.ok) {
360
- throw new Error(`HTTP error! status: ${response.status}`);
361
- }
362
- }
363
- finally {
364
- clearTimeout(timeoutId);
365
- }
366
- if (options.verbose)
367
- log.info(`[Verbose] HTTP PUT upload completed successfully`);
368
- }
369
- }
370
- catch (errorUpload) {
371
- const endTime = performance.now();
372
- const uploadTime = ((endTime - startTime) / 1000).toFixed(2);
373
- spinner.stop(`Failed to upload bundle ( after ${uploadTime} seconds)`);
374
- if (options.verbose) {
375
- log.info(`[Verbose] Upload failed after ${uploadTime} seconds`);
376
- log.info(`[Verbose] Error type: ${errorUpload instanceof Error ? 'Error' : typeof errorUpload}`);
377
- }
378
- if (errorUpload instanceof Error && errorUpload.message.includes('HTTP error')) {
379
- try {
380
- const statusMatch = errorUpload.message.match(/status: (\d+)/);
381
- const status = statusMatch ? statusMatch[1] : 'unknown';
382
- log.error(`Upload failed with status ${status}: ${errorUpload.message}`);
383
- }
384
- catch {
385
- log.error(`Upload failed: ${errorUpload.message}`);
386
- }
387
- }
388
- else {
389
- if (options.verbose)
390
- log.info(`[Verbose] Non-HTTP error: ${formatError(errorUpload)}`);
391
- if (!options.tus) {
392
- log.error(`Cannot upload bundle ( try again with --tus option) ${formatError(errorUpload)}`);
393
- }
394
- else {
395
- log.error(`Cannot upload bundle please contact support if the issue persists ${formatError(errorUpload)}`);
396
- }
397
- }
398
- if (options.verbose)
399
- log.info(`[Verbose] Cleaning up failed version from database...`);
400
- // call delete version on path /delete_failed_version to delete the version
401
- await deletedFailedVersion(supabase, appid, bundle);
402
- if (options.verbose)
403
- log.info(`[Verbose] Failed version cleaned up`);
404
- throw errorUpload instanceof Error ? errorUpload : new Error(String(errorUpload));
405
- }
406
- const endTime = performance.now();
407
- const uploadTime = ((endTime - startTime) / 1000).toFixed(2);
408
- spinner.stop(`Bundle uploaded 💪 in (${uploadTime} seconds)`);
409
- if (options.verbose) {
410
- log.info(`[Verbose] Upload successful:`);
411
- log.info(` - Upload time: ${uploadTime} seconds`);
412
- log.info(` - Upload method: ${isTus ? 'TUS (resumable)' : 'Standard HTTP PUT'}`);
413
- log.info(` - Bundle size: ${Math.floor(zipped.byteLength / 1024)} KB`);
414
- log.info(`[Verbose] Sending performance event...`);
415
- }
416
- await sendEvent(apikey, {
417
- channel: 'performance',
418
- event: isTus ? 'TUS upload zip performance' : 'Upload zip performance',
419
- icon: '🚄',
420
- user_id: orgId,
421
- tags: {
422
- 'app-id': appid,
423
- 'time': uploadTime,
424
- },
425
- notify: false,
426
- }, options.verbose);
427
- if (options.verbose)
428
- log.info(`[Verbose] Performance event sent successfully`);
429
- }
430
- // It is really important that his function never terminates the program, it should always return, even if it fails
431
- async function deleteLinkedBundleOnUpload(supabase, appid, channel) {
432
- const { data, error } = await supabase
433
- .from('channels')
434
- .select('version ( id, name, deleted )')
435
- .eq('app_id', appid)
436
- .eq('name', channel);
437
- if (error) {
438
- log.error(`Cannot delete linked bundle on upload ${formatError(error)}`);
439
- return;
440
- }
441
- if (data.length === 0) {
442
- log.warn('No linked bundle found in the channel you are trying to upload to');
443
- return;
444
- }
445
- const version = data[0].version;
446
- if (version.deleted) {
447
- log.warn('The linked bundle is already deleted');
448
- return;
449
- }
450
- const { error: deleteError } = await supabase
451
- .from('app_versions')
452
- .update({ deleted: true })
453
- .eq('id', version.id);
454
- if (deleteError) {
455
- log.error(`Cannot delete linked bundle on upload ${formatError(deleteError)}`);
456
- return;
457
- }
458
- log.info('Linked bundle deleted');
459
- }
460
- async function setVersionInChannel(supabase, apikey, displayBundleUrl, bundle, channel, userId, orgId, appid, localConfig, selfAssign) {
461
- const { data: versionId } = await supabase
462
- .rpc('get_app_versions', { apikey, name_version: bundle, appid })
463
- .single();
464
- if (!versionId)
465
- uploadFail('Cannot get version id, cannot set channel');
466
- const { data: apiAccess } = await supabase
467
- .rpc('is_allowed_capgkey', { apikey, keymode: ['write', 'all'] })
468
- .single();
469
- if (apiAccess) {
470
- const { error: dbError3, data } = await updateOrCreateChannel(supabase, {
471
- name: channel,
472
- app_id: appid,
473
- created_by: userId,
474
- version: versionId,
475
- owner_org: orgId,
476
- ...(selfAssign ? { allow_device_self_set: true } : {}),
477
- });
478
- if (dbError3)
479
- uploadFail(`Cannot set channel, the upload key is not allowed to do that, use the "all" for this. ${formatError(dbError3)}`);
480
- const bundleUrl = `${localConfig.hostWeb}/app/p/${appid}/channel/${data.id}`;
481
- if (data?.public)
482
- log.info('Your update is now available in your public channel 🎉');
483
- else if (data?.id)
484
- log.info(`Link device to this bundle to try it: ${bundleUrl}`);
485
- if (displayBundleUrl)
486
- log.info(`Bundle url: ${bundleUrl}`);
487
- }
488
- else {
489
- log.warn('The upload key is not allowed to set the version in the channel');
490
- }
491
- }
492
- export async function getDefaulUploadChannel(appId, supabase, hostWeb) {
493
- const { error, data } = await supabase.from('apps')
494
- .select('default_upload_channel')
495
- .eq('app_id', appId)
496
- .single();
497
- if (error) {
498
- log.warn('Cannot find default upload channel');
499
- log.info(`You can set it here: ${hostWeb}/app/p/${appId}/settings`);
500
- return null;
501
- }
502
- return data.default_upload_channel;
503
- }
504
- export async function uploadBundle(preAppid, options, shouldExit = true) {
505
- if (shouldExit)
506
- intro(`Uploading with CLI version ${pack.version}`);
507
- let sessionKey;
508
- const pm = getPMAndCommand();
509
- await checkAlerts();
510
- const { s3Region, s3Apikey, s3Apisecret, s3BucketName, s3Endpoint, s3Port, s3SSL } = options;
511
- if (options.verbose) {
512
- log.info(`[Verbose] Starting upload process with options:`);
513
- log.info(` - API key: ${options.apikey ? 'provided' : 'from saved key'}`);
514
- log.info(` - Path: ${options.path || 'from capacitor config'}`);
515
- log.info(` - Channel: ${options.channel || 'from default upload channel'}`);
516
- log.info(` - Bundle: ${options.bundle || 'auto-detected'}`);
517
- log.info(` - External: ${options.external || 'false'}`);
518
- log.info(` - Encryption: ${options.keyV2 || options.keyDataV2 ? 'v2' : options.key === false ? 'disabled' : 'auto'}`);
519
- log.info(` - Upload method: ${options.tus ? 'TUS' : options.zip ? 'ZIP' : 'auto'}`);
520
- log.info(` - Delta updates: ${options.delta || options.partial ? 'enabled' : 'disabled'}`);
521
- }
522
- const apikey = getApikey(options);
523
- if (options.verbose)
524
- log.info(`[Verbose] API key retrieved successfully`);
525
- const extConfig = await getConfig();
526
- if (options.verbose)
527
- log.info(`[Verbose] Capacitor config loaded successfully`);
528
- const fileConfig = await getRemoteFileConfig();
529
- if (options.verbose) {
530
- log.info(`[Verbose] Remote file config retrieved:`);
531
- log.info(` - Max upload length: ${Math.floor(fileConfig.maxUploadLength / 1024 / 1024)} MB`);
532
- log.info(` - Alert upload size: ${Math.floor(fileConfig.alertUploadSize / 1024 / 1024)} MB`);
533
- log.info(` - TUS upload: ${fileConfig.TUSUpload ? 'enabled' : 'disabled'}`);
534
- log.info(` - TUS upload forced: ${fileConfig.TUSUploadForced ? 'yes' : 'no'}`);
535
- log.info(` - Partial upload: ${fileConfig.partialUpload ? 'enabled' : 'disabled'}`);
536
- log.info(` - Max chunk size: ${Math.floor(fileConfig.maxChunkSize / 1024 / 1024)} MB`);
537
- }
538
- const { appid, path } = getAppIdAndPath(preAppid, options, extConfig.config);
539
- if (options.verbose)
540
- log.info(`[Verbose] App ID: ${appid}, Build path: ${path}`);
541
- const bundle = await getBundle(extConfig.config, options);
542
- if (options.verbose)
543
- log.info(`[Verbose] Bundle version: ${bundle}`);
544
- const defaultStorageProvider = options.external ? 'external' : 'r2-direct';
545
- let encryptionMethod = 'none';
546
- if (options.autoSetBundle) {
547
- await updateConfigUpdater({ version: bundle });
548
- if (options.verbose)
549
- log.info(`[Verbose] Auto-set bundle version in capacitor.config.json`);
550
- }
551
- checkNotifyAppReady(options, path);
552
- if (options.verbose)
553
- log.info(`[Verbose] Code check passed (notifyAppReady found and index.html present)`);
554
- log.info(`Upload ${appid}@${bundle} started from path "${path}" to Capgo cloud`);
555
- const localConfig = await getLocalConfig();
556
- if (options.verbose)
557
- log.info(`[Verbose] Local config loaded: host=${localConfig.hostWeb}`);
558
- if (options.supaHost && options.supaAnon) {
559
- log.info('Using custom supabase instance from provided options');
560
- localConfig.supaHost = options.supaHost;
561
- localConfig.supaKey = options.supaAnon;
562
- if (options.verbose)
563
- log.info(`[Verbose] Custom Supabase host: ${options.supaHost}`);
564
- }
565
- const supabase = await createSupabaseClient(apikey, options.supaHost, options.supaAnon);
566
- if (options.verbose)
567
- log.info(`[Verbose] Supabase client created successfully`);
568
- const userId = await verifyUser(supabase, apikey, ['write', 'all', 'upload']);
569
- if (options.verbose)
570
- log.info(`[Verbose] User verified successfully, user_id: ${userId}`);
571
- const channel = options.channel || await getDefaulUploadChannel(appid, supabase, localConfig.hostWeb) || 'dev';
572
- if (options.verbose)
573
- log.info(`[Verbose] Target channel: ${channel}`);
574
- // Now if it does exist we will fetch the org id
575
- const orgId = await getOrganizationId(supabase, appid);
576
- if (options.verbose)
577
- log.info(`[Verbose] Organization ID: ${orgId}`);
578
- await checkRemoteCliMessages(supabase, orgId, pack.version);
579
- if (options.verbose)
580
- log.info(`[Verbose] Remote CLI messages checked`);
581
- await checkPlanValidUpload(supabase, orgId, apikey, appid, true);
582
- if (options.verbose)
583
- log.info(`[Verbose] Plan validation passed`);
584
- await checkTrial(supabase, orgId, localConfig);
585
- if (options.verbose)
586
- log.info(`[Verbose] Trial check completed`);
587
- if (options.verbose)
588
- log.info(`[Verbose] Checking compatibility with channel ${channel}...`);
589
- const { nativePackages, minUpdateVersion } = await verifyCompatibility(supabase, pm, options, channel, appid, bundle);
590
- if (options.verbose) {
591
- log.info(`[Verbose] Compatibility check completed:`);
592
- log.info(` - Native packages: ${nativePackages ? nativePackages.length : 0}`);
593
- log.info(` - Min update version: ${minUpdateVersion || 'none'}`);
594
- }
595
- if (options.verbose)
596
- log.info(`[Verbose] Checking if version ${bundle} already exists...`);
597
- const versionAlreadyExists = await checkVersionExists(supabase, appid, bundle, options.versionExistsOk);
598
- if (options.verbose)
599
- log.info(`[Verbose] Version exists check: ${versionAlreadyExists ? 'yes (skipping)' : 'no (continuing)'}`);
600
- if (versionAlreadyExists) {
601
- return {
602
- success: true,
603
- skipped: true,
604
- reason: 'VERSION_EXISTS',
605
- bundle,
606
- checksum: null,
607
- encryptionMethod,
608
- storageProvider: defaultStorageProvider,
609
- };
610
- }
611
- if (options.external && !options.external.startsWith('https://')) {
612
- uploadFail(`External link should should start with "https://" current is "${options.external}"`);
613
- }
614
- if (options.deleteLinkedBundleOnUpload) {
615
- log.warn('Deleting linked bundle on upload is destructive, it will delete the currently linked bundle in the channel you are trying to upload to.');
616
- log.warn('Please make sure you want to do this, if you are not sure, please do not use this option.');
617
- }
618
- const versionData = {
619
- name: bundle,
620
- app_id: appid,
621
- session_key: undefined,
622
- external_url: options.external,
623
- storage_provider: defaultStorageProvider,
624
- min_update_version: minUpdateVersion,
625
- native_packages: nativePackages,
626
- owner_org: orgId,
627
- user_id: userId,
628
- checksum: undefined,
629
- link: options.link || null,
630
- comment: options.comment || null,
631
- };
632
- let zipped = null;
633
- let finalKeyData = '';
634
- if (!options.external) {
635
- if (options.verbose)
636
- log.info(`[Verbose] Preparing bundle file from path: ${path}`);
637
- const { zipped: _zipped, ivSessionKey, checksum, sessionKey: sk, encryptionMethod: em, finalKeyData: fkd } = await prepareBundleFile(path, options, apikey, orgId, appid, fileConfig.maxUploadLength, fileConfig.alertUploadSize);
638
- versionData.session_key = ivSessionKey;
639
- versionData.checksum = checksum;
640
- sessionKey = sk;
641
- zipped = _zipped;
642
- encryptionMethod = em;
643
- finalKeyData = fkd;
644
- if (options.verbose) {
645
- log.info(`[Verbose] Bundle prepared:`);
646
- log.info(` - Size: ${Math.floor((_zipped?.byteLength ?? 0) / 1024)} KB`);
647
- log.info(` - Checksum: ${checksum}`);
648
- log.info(` - Encryption: ${em}`);
649
- log.info(` - IV Session Key: ${ivSessionKey ? 'present' : 'none'}`);
650
- }
651
- if (!options.ignoreChecksumCheck) {
652
- if (options.verbose)
653
- log.info(`[Verbose] Checking for duplicate checksum...`);
654
- await checkChecksum(supabase, appid, channel, checksum);
655
- if (options.verbose)
656
- log.info(`[Verbose] Checksum is unique`);
657
- }
658
- }
659
- else {
660
- if (options.verbose)
661
- log.info(`[Verbose] Using external URL: ${options.external}`);
662
- await sendEvent(apikey, {
663
- channel: 'app',
664
- event: 'App external',
665
- icon: '📤',
666
- user_id: orgId,
667
- tags: {
668
- 'app-id': appid,
669
- },
670
- notify: false,
671
- }, options.verbose);
672
- versionData.session_key = options.ivSessionKey;
673
- versionData.checksum = options.encryptedChecksum;
674
- if (options.verbose) {
675
- log.info(`[Verbose] External bundle configured:`);
676
- log.info(` - URL: ${options.external}`);
677
- log.info(` - IV Session Key: ${options.ivSessionKey ? 'provided' : 'none'}`);
678
- log.info(` - Encrypted Checksum: ${options.encryptedChecksum ? 'provided' : 'none'}`);
679
- }
680
- }
681
- if (options.zip) {
682
- options.tus = false;
683
- if (options.verbose)
684
- log.info(`[Verbose] Upload method: ZIP (explicitly set via --zip)`);
685
- }
686
- // ALLOW TO OVERRIDE THE FILE CONFIG WITH THE OPTIONS IF THE FILE CONFIG IS FORCED
687
- else if (!fileConfig.TUSUpload || options.external) {
688
- options.tus = false;
689
- if (options.verbose)
690
- log.info(`[Verbose] Upload method: Standard (TUS not available or external URL)`);
691
- }
692
- else {
693
- options.tus = options.tus || fileConfig.TUSUploadForced;
694
- if (options.verbose)
695
- log.info(`[Verbose] Upload method: ${options.tus ? 'TUS (resumable)' : 'Standard'}`);
696
- }
697
- if (!fileConfig.partialUpload || options.external) {
698
- options.delta = false;
699
- if (options.verbose && options.external)
700
- log.info(`[Verbose] Delta updates disabled (not available with external URLs)`);
701
- }
702
- else {
703
- options.delta = options.delta || options.partial || options.deltaOnly || options.partialOnly || fileConfig.partialUploadForced;
704
- if (options.verbose)
705
- log.info(`[Verbose] Delta updates: ${options.delta ? 'enabled' : 'disabled'}`);
706
- }
707
- if (options.encryptPartial && encryptionMethod === 'v1')
708
- uploadFail('You cannot encrypt the partial update if you are not using the v2 encryption method');
709
- // Auto-encrypt partial updates for updater versions > 6.14.5 if encryption method is v2
710
- if (options.delta && encryptionMethod === 'v2' && !options.encryptPartial) {
711
- // Check updater version
712
- const root = join(findRoot(cwd()), PACKNAME);
713
- const dependencies = await getAllPackagesDependencies(undefined, options.packageJson || root);
714
- const updaterVersion = dependencies.get('@capgo/capacitor-updater');
715
- let coerced;
716
- try {
717
- coerced = updaterVersion ? parse(updaterVersion) : undefined;
718
- }
719
- catch {
720
- coerced = undefined;
721
- }
722
- if (updaterVersion && coerced && greaterOrEqual(coerced, parse('6.14.4'))) {
723
- log.info(`Auto-enabling partial update encryption for updater version ${coerced} (> 6.14.4)`);
724
- if (options.verbose)
725
- log.info(`[Verbose] Partial encryption auto-enabled for updater >= 6.14.4`);
726
- options.encryptPartial = true;
727
- }
728
- }
729
- if (options.verbose && options.delta)
730
- log.info(`[Verbose] Preparing delta/partial update manifest...`);
731
- const manifest = options.delta ? await prepareBundlePartialFiles(path, apikey, orgId, appid, options.encryptPartial ? encryptionMethod : 'none', finalKeyData) : [];
732
- if (options.verbose && options.delta)
733
- log.info(`[Verbose] Delta manifest prepared with ${manifest.length} files`);
734
- if (options.verbose)
735
- log.info(`[Verbose] Creating version record in database...`);
736
- const { error: dbError } = await updateOrCreateVersion(supabase, versionData);
737
- if (dbError)
738
- uploadFail(`Cannot add bundle ${formatError(dbError)}`);
739
- if (options.verbose)
740
- log.info(`[Verbose] Version record created successfully`);
741
- if (options.tusChunkSize && options.tusChunkSize > fileConfig.maxChunkSize) {
742
- log.error(`Chunk size ${options.tusChunkSize} is greater than the maximum chunk size ${fileConfig.maxChunkSize}, using the maximum chunk size`);
743
- options.tusChunkSize = fileConfig.maxChunkSize;
744
- }
745
- else if (!options.tusChunkSize) {
746
- options.tusChunkSize = fileConfig.maxChunkSize;
747
- }
748
- if (options.verbose)
749
- log.info(`[Verbose] TUS chunk size: ${Math.floor(options.tusChunkSize / 1024 / 1024)} MB`);
750
- if (zipped && (s3BucketName || s3Endpoint || s3Region || s3Apikey || s3Apisecret || s3Port || s3SSL)) {
751
- if (!s3BucketName || !s3Endpoint || !s3Region || !s3Apikey || !s3Apisecret || !s3Port)
752
- uploadFail('Missing argument, for S3 upload you need to provide a bucket name, endpoint, region, port, API key, and API secret');
753
- log.info('Uploading to S3');
754
- if (options.verbose) {
755
- log.info(`[Verbose] S3 configuration:`);
756
- log.info(` - Endpoint: ${s3Endpoint}`);
757
- log.info(` - Region: ${s3Region}`);
758
- log.info(` - Bucket: ${s3BucketName}`);
759
- log.info(` - Port: ${s3Port}`);
760
- log.info(` - SSL: ${s3SSL ? 'enabled' : 'disabled'}`);
761
- }
762
- const endPoint = s3SSL ? `https://${s3Endpoint}` : `http://${s3Endpoint}`;
763
- const s3Client = new S3Client({
764
- endPoint: s3Endpoint,
765
- region: s3Region,
766
- port: s3Port,
767
- pathStyle: true,
768
- bucket: s3BucketName,
769
- accessKey: s3Apikey,
770
- secretKey: s3Apisecret,
771
- });
772
- const fileName = `${appid}-${bundle}`;
773
- const encodeFileName = encodeURIComponent(fileName);
774
- if (options.verbose)
775
- log.info(`[Verbose] Uploading to S3 as: ${fileName}`);
776
- await s3Client.putObject(fileName, Uint8Array.from(zipped));
777
- versionData.external_url = `${endPoint}/${encodeFileName}`;
778
- versionData.storage_provider = 'external';
779
- if (options.verbose)
780
- log.info(`[Verbose] S3 upload complete, external URL: ${versionData.external_url}`);
781
- }
782
- else if (zipped) {
783
- if (!options.partialOnly && !options.deltaOnly) {
784
- if (options.verbose)
785
- log.info(`[Verbose] Starting full bundle upload to Capgo Cloud...`);
786
- await uploadBundleToCapgoCloud(apikey, supabase, appid, bundle, orgId, zipped, options, options.tusChunkSize);
787
- }
788
- else if (options.verbose) {
789
- log.info(`[Verbose] Skipping full bundle upload (delta-only mode)`);
790
- }
791
- let finalManifest = null;
792
- try {
793
- if (options.dryUpload) {
794
- options.delta = false;
795
- if (options.verbose)
796
- log.info(`[Verbose] Dry upload mode: skipping delta upload`);
797
- }
798
- const encryptionData = versionData.session_key && options.encryptPartial && sessionKey
799
- ? {
800
- sessionKey,
801
- ivSessionKey: versionData.session_key,
802
- }
803
- : undefined;
804
- if (options.verbose && options.delta) {
805
- log.info(`[Verbose] Starting delta/partial file upload...`);
806
- log.info(` - Manifest entries: ${manifest.length}`);
807
- log.info(` - Encryption: ${encryptionData ? 'enabled' : 'disabled'}`);
808
- }
809
- finalManifest = options.delta
810
- ? await uploadPartial(apikey, manifest, path, appid, bundle, orgId, encryptionData, options)
811
- : null;
812
- if (options.verbose && finalManifest)
813
- log.info(`[Verbose] Delta upload complete with ${finalManifest.length} files`);
814
- }
815
- catch (err) {
816
- log.info(`Failed to upload partial files to capgo cloud. Error: ${formatError(err)}. This is not a critical error, the bundle has been uploaded without the partial files`);
817
- if (options.verbose)
818
- log.info(`[Verbose] Delta upload error details: ${formatError(err)}`);
819
- }
820
- versionData.storage_provider = 'r2';
821
- versionData.manifest = finalManifest;
822
- if (options.verbose)
823
- log.info(`[Verbose] Updating version record with storage provider and manifest...`);
824
- const { error: dbError2 } = await updateOrCreateVersion(supabase, versionData);
825
- if (dbError2)
826
- uploadFail(`Cannot update bundle ${formatError(dbError2)}`);
827
- if (options.verbose)
828
- log.info(`[Verbose] Version record updated successfully`);
829
- }
830
- // Check we have app access to this appId
831
- if (options.verbose)
832
- log.info(`[Verbose] Checking app permissions...`);
833
- const permissions = await checkAppExistsAndHasPermissionOrgErr(supabase, apikey, appid, OrganizationPerm.upload);
834
- if (options.verbose) {
835
- log.info(`[Verbose] Permissions:`);
836
- log.info(` - Upload: ${hasOrganizationPerm(permissions, OrganizationPerm.upload) ? 'yes' : 'no'}`);
837
- log.info(` - Write: ${hasOrganizationPerm(permissions, OrganizationPerm.write) ? 'yes' : 'no'}`);
838
- log.info(` - Admin: ${hasOrganizationPerm(permissions, OrganizationPerm.admin) ? 'yes' : 'no'}`);
839
- }
840
- if (options.deleteLinkedBundleOnUpload && hasOrganizationPerm(permissions, OrganizationPerm.write)) {
841
- if (options.verbose)
842
- log.info(`[Verbose] Deleting linked bundle in channel ${channel}...`);
843
- await deleteLinkedBundleOnUpload(supabase, appid, channel);
844
- }
845
- else if (options.deleteLinkedBundleOnUpload) {
846
- log.warn('Cannot delete linked bundle on upload as a upload organization member');
847
- }
848
- if (hasOrganizationPerm(permissions, OrganizationPerm.write)) {
849
- if (options.verbose)
850
- log.info(`[Verbose] Setting bundle ${bundle} to channel ${channel}...`);
851
- await setVersionInChannel(supabase, apikey, !!options.bundleUrl, bundle, channel, userId, orgId, appid, localConfig, options.selfAssign);
852
- if (options.verbose)
853
- log.info(`[Verbose] Channel updated successfully`);
854
- }
855
- else {
856
- log.warn('Cannot set channel as a upload organization member');
857
- }
858
- if (options.verbose)
859
- log.info(`[Verbose] Sending upload event...`);
860
- await sendEvent(apikey, {
861
- channel: 'app',
862
- event: 'App Uploaded',
863
- icon: '⏫',
864
- user_id: orgId,
865
- tags: {
866
- 'app-id': appid,
867
- },
868
- notify: false,
869
- }, options.verbose);
870
- const result = {
871
- success: true,
872
- bundle,
873
- checksum: versionData.checksum ?? null,
874
- encryptionMethod,
875
- sessionKey: sessionKey ? sessionKey.toString('base64') : undefined,
876
- ivSessionKey: typeof versionData.session_key === 'string' ? versionData.session_key : undefined,
877
- storageProvider: versionData.storage_provider,
878
- };
879
- if (options.verbose) {
880
- log.info(`[Verbose] Upload completed successfully:`);
881
- log.info(` - Bundle: ${result.bundle}`);
882
- log.info(` - Checksum: ${result.checksum}`);
883
- log.info(` - Encryption: ${result.encryptionMethod}`);
884
- log.info(` - Storage: ${result.storageProvider}`);
885
- }
886
- if (shouldExit && !result.skipped)
887
- outro('Time to share your update to the world 🌍');
888
- return result;
889
- }
890
- function checkValidOptions(options) {
891
- if (options.ivSessionKey && !options.external) {
892
- uploadFail('You need to provide an external url if you want to use the --iv-session-key option');
893
- }
894
- if (options.encryptedChecksum && !options.external) {
895
- uploadFail('You need to provide an external url if you want to use the --encrypted-checksum option');
896
- }
897
- if ((options.partial || options.delta || options.partialOnly || options.deltaOnly) && options.external) {
898
- uploadFail('You cannot use the --partial/--delta/--partial-only/--delta-only option with an external url');
899
- }
900
- if (options.tus && options.external) {
901
- uploadFail('You cannot use the --tus option with an external url');
902
- }
903
- if (options.dryUpload && options.external) {
904
- uploadFail('You cannot use the --dry-upload option with an external url');
905
- }
906
- if (options.multipart && options.external) {
907
- uploadFail('You cannot use the --multipart option with an external url');
908
- }
909
- // cannot set key if external
910
- if (options.external && (options.keyV2 || options.keyDataV2)) {
911
- uploadFail('You cannot set a key if you are uploading to an external url');
912
- }
913
- // cannot set key-v2 and key-data-v2
914
- if (options.keyV2 && options.keyDataV2) {
915
- uploadFail('You cannot set both key-v2 and key-data-v2');
916
- }
917
- // cannot set s3 and external
918
- if (options.external && (options.s3Region || options.s3Apikey || options.s3Apisecret || options.s3Endpoint || options.s3BucketName || options.s3Port || options.s3SSL)) {
919
- uploadFail('You cannot set S3 options if you are uploading to an external url, it\'s automatically handled');
920
- }
921
- // cannot set --encrypted-checksum if not external
922
- if (options.encryptedChecksum && !options.external) {
923
- uploadFail('You cannot set the --encrypted-checksum option if you are not uploading to an external url');
924
- }
925
- // cannot set min-update-version and auto-min-update-version
926
- if (options.minUpdateVersion && options.autoMinUpdateVersion) {
927
- uploadFail('You cannot set both min-update-version and auto-min-update-version, use only one of them');
928
- }
929
- }
930
- export async function uploadCommand(appid, options) {
931
- try {
932
- checkValidOptions(options);
933
- await uploadBundle(appid, options, true);
934
- }
935
- catch (error) {
936
- log.error(`uploadBundle failded: ${formatError(error)}`);
937
- throw error instanceof Error ? error : new Error(String(error));
938
- }
939
- }
940
- //# sourceMappingURL=upload.js.map