@capgo/cli 8.0.0-alpha.1 → 8.0.0-alpha.2

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 (149) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +238 -411
  3. package/dist/package.json +89 -93
  4. package/dist/src/api/channels.d.ts +592 -16
  5. package/dist/src/api/channels.d.ts.map +1 -1
  6. package/dist/src/api/update.d.ts.map +1 -1
  7. package/dist/src/app/add.d.ts +2 -8
  8. package/dist/src/app/add.d.ts.map +1 -1
  9. package/dist/src/app/debug.d.ts +1 -1
  10. package/dist/src/app/debug.d.ts.map +1 -1
  11. package/dist/src/app/delete.d.ts +2 -1
  12. package/dist/src/app/delete.d.ts.map +1 -1
  13. package/dist/src/app/info.d.ts +9 -1
  14. package/dist/src/app/info.d.ts.map +1 -1
  15. package/dist/src/app/list.d.ts +19 -1
  16. package/dist/src/app/list.d.ts.map +1 -1
  17. package/dist/src/app/set.d.ts +2 -1
  18. package/dist/src/app/set.d.ts.map +1 -1
  19. package/dist/src/app/setting.d.ts +2 -1
  20. package/dist/src/app/setting.d.ts.map +1 -1
  21. package/dist/src/bundle/cleanup.d.ts +5 -1
  22. package/dist/src/bundle/cleanup.d.ts.map +1 -1
  23. package/dist/src/bundle/compatibility.d.ts +4 -4
  24. package/dist/src/bundle/compatibility.d.ts.map +1 -1
  25. package/dist/src/bundle/delete.d.ts +2 -1
  26. package/dist/src/bundle/delete.d.ts.map +1 -1
  27. package/dist/src/bundle/partial.d.ts +2 -2
  28. package/dist/src/bundle/partial.d.ts.map +1 -1
  29. package/dist/src/bundle/upload.d.ts +3 -3
  30. package/dist/src/bundle/upload.d.ts.map +1 -1
  31. package/dist/src/bundle/upload_interface.d.ts +1 -0
  32. package/dist/src/bundle/upload_interface.d.ts.map +1 -1
  33. package/dist/src/bundle/zip.d.ts.map +1 -1
  34. package/dist/src/channel/add.d.ts +2 -2
  35. package/dist/src/channel/add.d.ts.map +1 -1
  36. package/dist/src/channel/currentBundle.d.ts +2 -1
  37. package/dist/src/channel/currentBundle.d.ts.map +1 -1
  38. package/dist/src/channel/delete.d.ts +2 -1
  39. package/dist/src/channel/delete.d.ts.map +1 -1
  40. package/dist/src/channel/list.d.ts +2 -1
  41. package/dist/src/channel/list.d.ts.map +1 -1
  42. package/dist/src/channel/set.d.ts +2 -1
  43. package/dist/src/channel/set.d.ts.map +1 -1
  44. package/dist/src/checksum.d.ts +9 -0
  45. package/dist/src/checksum.d.ts.map +1 -0
  46. package/dist/src/init.d.ts.map +1 -1
  47. package/dist/src/keyV2.d.ts +2 -3
  48. package/dist/src/keyV2.d.ts.map +1 -1
  49. package/dist/src/login.d.ts +2 -2
  50. package/dist/src/login.d.ts.map +1 -1
  51. package/dist/src/organisation/list.d.ts +3 -0
  52. package/dist/src/organisation/list.d.ts.map +1 -1
  53. package/dist/src/sdk.d.ts +107 -4
  54. package/dist/src/sdk.d.ts.map +1 -1
  55. package/dist/src/sdk.js +247 -310
  56. package/dist/src/types/supabase.types.d.ts +313 -9
  57. package/dist/src/types/supabase.types.d.ts.map +1 -1
  58. package/dist/src/utils/latest-version.d.ts +7 -0
  59. package/dist/src/utils/latest-version.d.ts.map +1 -0
  60. package/dist/src/utils.d.ts +302 -21
  61. package/dist/src/utils.d.ts.map +1 -1
  62. package/package.json +11 -15
  63. package/dist/src/api/app.js +0 -45
  64. package/dist/src/api/app.js.map +0 -1
  65. package/dist/src/api/channels.js +0 -167
  66. package/dist/src/api/channels.js.map +0 -1
  67. package/dist/src/api/cryptoV2.js +0 -102
  68. package/dist/src/api/cryptoV2.js.map +0 -1
  69. package/dist/src/api/update.js +0 -14
  70. package/dist/src/api/update.js.map +0 -1
  71. package/dist/src/api/versions.js +0 -92
  72. package/dist/src/api/versions.js.map +0 -1
  73. package/dist/src/app/add.js +0 -150
  74. package/dist/src/app/add.js.map +0 -1
  75. package/dist/src/app/debug.js +0 -222
  76. package/dist/src/app/debug.js.map +0 -1
  77. package/dist/src/app/delete.js +0 -89
  78. package/dist/src/app/delete.js.map +0 -1
  79. package/dist/src/app/info.js +0 -84
  80. package/dist/src/app/info.js.map +0 -1
  81. package/dist/src/app/list.js +0 -48
  82. package/dist/src/app/list.js.map +0 -1
  83. package/dist/src/app/set.js +0 -96
  84. package/dist/src/app/set.js.map +0 -1
  85. package/dist/src/app/setting.js +0 -50
  86. package/dist/src/app/setting.js.map +0 -1
  87. package/dist/src/bundle/check.js +0 -30
  88. package/dist/src/bundle/check.js.map +0 -1
  89. package/dist/src/bundle/cleanup.js +0 -105
  90. package/dist/src/bundle/cleanup.js.map +0 -1
  91. package/dist/src/bundle/compatibility.js +0 -62
  92. package/dist/src/bundle/compatibility.js.map +0 -1
  93. package/dist/src/bundle/decryptV2.js +0 -76
  94. package/dist/src/bundle/decryptV2.js.map +0 -1
  95. package/dist/src/bundle/delete.js +0 -40
  96. package/dist/src/bundle/delete.js.map +0 -1
  97. package/dist/src/bundle/encryptV2.js +0 -108
  98. package/dist/src/bundle/encryptV2.js.map +0 -1
  99. package/dist/src/bundle/list.js +0 -36
  100. package/dist/src/bundle/list.js.map +0 -1
  101. package/dist/src/bundle/partial.js +0 -332
  102. package/dist/src/bundle/partial.js.map +0 -1
  103. package/dist/src/bundle/unlink.js +0 -70
  104. package/dist/src/bundle/unlink.js.map +0 -1
  105. package/dist/src/bundle/upload.js +0 -700
  106. package/dist/src/bundle/upload.js.map +0 -1
  107. package/dist/src/bundle/upload_interface.js +0 -2
  108. package/dist/src/bundle/upload_interface.js.map +0 -1
  109. package/dist/src/bundle/zip.js +0 -148
  110. package/dist/src/bundle/zip.js.map +0 -1
  111. package/dist/src/channel/add.js +0 -68
  112. package/dist/src/channel/add.js.map +0 -1
  113. package/dist/src/channel/currentBundle.js +0 -54
  114. package/dist/src/channel/currentBundle.js.map +0 -1
  115. package/dist/src/channel/delete.js +0 -77
  116. package/dist/src/channel/delete.js.map +0 -1
  117. package/dist/src/channel/list.js +0 -45
  118. package/dist/src/channel/list.js.map +0 -1
  119. package/dist/src/channel/set.js +0 -220
  120. package/dist/src/channel/set.js.map +0 -1
  121. package/dist/src/config/index.js +0 -31
  122. package/dist/src/config/index.js.map +0 -1
  123. package/dist/src/docs.js +0 -280
  124. package/dist/src/docs.js.map +0 -1
  125. package/dist/src/index.js +0 -504
  126. package/dist/src/index.js.map +0 -1
  127. package/dist/src/init.js +0 -797
  128. package/dist/src/init.js.map +0 -1
  129. package/dist/src/keyV2.js +0 -163
  130. package/dist/src/keyV2.js.map +0 -1
  131. package/dist/src/login.js +0 -51
  132. package/dist/src/login.js.map +0 -1
  133. package/dist/src/organisation/add.js +0 -82
  134. package/dist/src/organisation/add.js.map +0 -1
  135. package/dist/src/organisation/delete.js +0 -91
  136. package/dist/src/organisation/delete.js.map +0 -1
  137. package/dist/src/organisation/index.js +0 -5
  138. package/dist/src/organisation/index.js.map +0 -1
  139. package/dist/src/organisation/list.js +0 -60
  140. package/dist/src/organisation/list.js.map +0 -1
  141. package/dist/src/organisation/set.js +0 -95
  142. package/dist/src/organisation/set.js.map +0 -1
  143. package/dist/src/sdk.js.map +0 -1
  144. package/dist/src/types/supabase.types.js +0 -88
  145. package/dist/src/types/supabase.types.js.map +0 -1
  146. package/dist/src/user/account.js +0 -31
  147. package/dist/src/user/account.js.map +0 -1
  148. package/dist/src/utils.js +0 -1184
  149. package/dist/src/utils.js.map +0 -1
@@ -1,700 +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 { checksum as getChecksum } from '@tomasklaen/checksum';
8
- import ky, { HTTPError } from 'ky';
9
- import coerceVersion from 'semver/functions/coerce';
10
- // We only use semver from std for Capgo semver, others connected to package.json need npm one as it's not following the semver spec
11
- import semverGte from 'semver/functions/gte';
12
- import pack from '../../package.json';
13
- import { checkAppExistsAndHasPermissionOrgErr } from '../api/app';
14
- import { encryptChecksumV2, encryptSourceV2, generateSessionKey } from '../api/cryptoV2';
15
- import { checkAlerts } from '../api/update';
16
- 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';
17
- import { checkIndexPosition, searchInDirectory } from './check';
18
- import { prepareBundlePartialFiles, uploadPartial } from './partial';
19
- function uploadFail(message) {
20
- log.error(message);
21
- throw new Error(message);
22
- }
23
- async function getBundle(config, options) {
24
- const pkgVersion = getBundleVersion('', options.packageJson);
25
- // create bundle name format : 1.0.0-beta.x where x is a uuid
26
- const bundle = options.bundle
27
- || config?.plugins?.CapacitorUpdater?.version
28
- || pkgVersion
29
- || `0.0.1-beta.${randomUUID().split('-')[0]}`;
30
- if (!regexSemver.test(bundle)) {
31
- uploadFail(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`);
32
- }
33
- return bundle;
34
- }
35
- function getApikey(options) {
36
- const apikey = options.apikey || findSavedKey();
37
- if (!apikey) {
38
- uploadFail('Missing API key, you need to provide an API key to upload your bundle');
39
- }
40
- return apikey;
41
- }
42
- function getAppIdAndPath(appId, options, config) {
43
- const finalAppId = getAppId(appId, config);
44
- const path = options.path || config?.webDir;
45
- if (!finalAppId) {
46
- uploadFail('Missing argument, you need to provide a appid or be in a capacitor project');
47
- }
48
- if (!path) {
49
- uploadFail('Missing argument, you need to provide a path (--path), or be in a capacitor project');
50
- }
51
- if (!existsSync(path)) {
52
- uploadFail(`Path ${path} does not exist, build your app first, or provide a valid path`);
53
- }
54
- return { appid: finalAppId, path };
55
- }
56
- function checkNotifyAppReady(options, path) {
57
- const checkNotifyAppReady = options.codeCheck;
58
- if (typeof checkNotifyAppReady === 'undefined' || checkNotifyAppReady) {
59
- const isPluginConfigured = searchInDirectory(path, 'notifyAppReady');
60
- if (!isPluginConfigured) {
61
- uploadFail(`notifyAppReady() is missing in the build folder of your app. see: https://capgo.app/docs/plugin/api/#notifyappready
62
- If you are sure your app has this code, you can use the --no-code-check option`);
63
- }
64
- const foundIndex = checkIndexPosition(path);
65
- if (!foundIndex) {
66
- uploadFail(`index.html is missing in the root folder of ${path}`);
67
- }
68
- }
69
- }
70
- async function verifyCompatibility(supabase, pm, options, channel, appid, bundle) {
71
- // Check compatibility here
72
- const ignoreMetadataCheck = options.ignoreMetadataCheck;
73
- const autoMinUpdateVersion = options.autoMinUpdateVersion;
74
- let minUpdateVersion = options.minUpdateVersion;
75
- const { data: channelData, error: channelError } = await supabase
76
- .from('channels')
77
- .select('disable_auto_update, version ( min_update_version, native_packages )')
78
- .eq('name', channel)
79
- .eq('app_id', appid)
80
- .single();
81
- const updateMetadataRequired = !!channelData && channelData.disable_auto_update === 'version_number';
82
- let localDependencies;
83
- let finalCompatibility;
84
- // We only check compatibility IF the channel exists
85
- if (!channelError && channelData && channelData.version && channelData.version.native_packages && !ignoreMetadataCheck) {
86
- const spinner = spinnerC();
87
- spinner.start(`Checking bundle compatibility with channel ${channel}`);
88
- const { finalCompatibility: finalCompatibilityWithChannel, localDependencies: localDependenciesWithChannel, } = await checkCompatibility(supabase, appid, channel, options.packageJson, options.nodeModules);
89
- finalCompatibility = finalCompatibilityWithChannel;
90
- localDependencies = localDependenciesWithChannel;
91
- // Check if any package is incompatible
92
- if (finalCompatibility.find(x => !isCompatible(x))) {
93
- spinner.stop(`Bundle NOT compatible with ${channel} channel`);
94
- log.warn(`You can check compatibility with "${pm.runner} @capgo/cli bundle compatibility"`);
95
- if (autoMinUpdateVersion) {
96
- minUpdateVersion = bundle;
97
- log.info(`Auto set min-update-version to ${minUpdateVersion}`);
98
- }
99
- }
100
- else if (autoMinUpdateVersion) {
101
- try {
102
- const { min_update_version: lastMinUpdateVersion } = channelData.version;
103
- if (!lastMinUpdateVersion || !regexSemver.test(lastMinUpdateVersion))
104
- uploadFail('Invalid remote min update version, skipping auto setting compatibility');
105
- minUpdateVersion = lastMinUpdateVersion;
106
- spinner.stop(`Auto set min-update-version to ${minUpdateVersion}`);
107
- }
108
- catch {
109
- uploadFail(`Cannot auto set compatibility, invalid data ${channelData}`);
110
- }
111
- }
112
- else {
113
- spinner.stop(`Bundle compatible with ${channel} channel`);
114
- }
115
- }
116
- else if (!ignoreMetadataCheck) {
117
- log.warn(`Channel ${channel} is new or it's your first upload with compatibility check, it will be ignored this time`);
118
- localDependencies = await getLocalDepenencies(options.packageJson, options.nodeModules);
119
- if (autoMinUpdateVersion) {
120
- minUpdateVersion = bundle;
121
- log.info(`Auto set min-update-version to ${minUpdateVersion}`);
122
- }
123
- }
124
- if (updateMetadataRequired && !minUpdateVersion && !ignoreMetadataCheck) {
125
- uploadFail('You need to provide a min-update-version to upload a bundle to this channel');
126
- }
127
- if (minUpdateVersion) {
128
- if (!regexSemver.test(minUpdateVersion))
129
- uploadFail(`Your minimal version update ${minUpdateVersion}, is not valid it should follow semver convention : https://semver.org/`);
130
- }
131
- const hashedLocalDependencies = localDependencies
132
- ? new Map(localDependencies
133
- .filter(a => !!a.native && a.native !== undefined)
134
- .map(a => [a.name, a]))
135
- : new Map();
136
- const nativePackages = (hashedLocalDependencies.size > 0 || !options.ignoreMetadataCheck) ? Array.from(hashedLocalDependencies, ([name, value]) => ({ name, version: value.version })) : undefined;
137
- return { nativePackages, minUpdateVersion };
138
- }
139
- async function checkTrial(supabase, orgId, localConfig) {
140
- const { data: isTrial, error: isTrialsError } = await supabase
141
- .rpc('is_trial_org', { orgid: orgId })
142
- .single();
143
- if ((isTrial && isTrial > 0) || isTrialsError) {
144
- // TODO: Come back to this to fix for orgs v3
145
- log.warn(`WARNING !!\nTrial expires in ${isTrial} days`);
146
- log.warn(`Upgrade here: ${localConfig.hostWeb}/dashboard/settings/plans?oid=${orgId}`);
147
- }
148
- }
149
- async function checkVersionExists(supabase, appid, bundle, versionExistsOk = false) {
150
- // check if app already exist
151
- // apikey is sooo legacy code, current prod does not use it
152
- // TODO: remove apikey and create a new function who not need it
153
- const { data: appVersion, error: appVersionError } = await supabase
154
- .rpc('exist_app_versions', { appid, apikey: '', name_version: bundle })
155
- .single();
156
- if (appVersion || appVersionError) {
157
- if (versionExistsOk) {
158
- log.warn(`Version ${bundle} already exists - exiting gracefully due to --silent-fail option`);
159
- outro('Bundle version already exists - exiting gracefully 🎉');
160
- return true;
161
- }
162
- uploadFail(`Version ${bundle} already exists ${formatError(appVersionError)}`);
163
- }
164
- return false;
165
- }
166
- async function prepareBundleFile(path, options, apikey, orgId, appid, maxUploadLength, alertUploadSize) {
167
- let ivSessionKey;
168
- let sessionKey;
169
- let checksum = '';
170
- let zipped = null;
171
- let encryptionMethod = 'none';
172
- let finalKeyData = '';
173
- const keyV2 = options.keyV2;
174
- const noKey = options.key === false;
175
- zipped = await zipFile(path);
176
- const s = spinnerC();
177
- s.start(`Calculating checksum`);
178
- const root = join(findRoot(cwd()), PACKNAME);
179
- // options.packageJson
180
- const dependencies = await getAllPackagesDependencies(undefined, options.packageJson || root);
181
- const updaterVersion = dependencies.get('@capgo/capacitor-updater');
182
- let isv7 = false;
183
- const coerced = coerceVersion(updaterVersion);
184
- if (!updaterVersion) {
185
- 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');
186
- }
187
- else if (coerced) {
188
- isv7 = semverGte(coerced.version, '7.0.0');
189
- }
190
- else if (updaterVersion === 'link:@capgo/capacitor-updater') {
191
- log.warn('Using local @capgo/capacitor-updater. Assuming v7');
192
- isv7 = true;
193
- }
194
- if (((keyV2 || options.keyDataV2 || existsSync(baseKeyV2)) && !noKey) || isv7) {
195
- checksum = await getChecksum(zipped, 'sha256');
196
- }
197
- else {
198
- checksum = await getChecksum(zipped, 'crc32');
199
- }
200
- s.stop(`Checksum: ${checksum}`);
201
- // key should be undefined or a string if false it should ingore encryption DO NOT REPLACE key === false With !key it will not work
202
- if (noKey) {
203
- log.info(`Encryption ignored`);
204
- }
205
- else if ((keyV2 || existsSync(baseKeyV2) || options.keyDataV2) && !options.oldEncryption) {
206
- const privateKey = typeof keyV2 === 'string' ? keyV2 : baseKeyV2;
207
- let keyDataV2 = options.keyDataV2 || '';
208
- if (!keyDataV2 && !existsSync(privateKey))
209
- uploadFail(`Cannot find private key ${privateKey}`);
210
- await sendEvent(apikey, {
211
- channel: 'app',
212
- event: 'App encryption v2',
213
- icon: '🔑',
214
- user_id: orgId,
215
- tags: {
216
- 'app-id': appid,
217
- },
218
- notify: false,
219
- });
220
- if (!keyDataV2) {
221
- const keyFile = readFileSync(privateKey);
222
- keyDataV2 = keyFile.toString();
223
- }
224
- log.info('Encrypting your bundle with V2');
225
- const { sessionKey: sKey, ivSessionKey: ivKey } = generateSessionKey(keyDataV2);
226
- const encryptedData = encryptSourceV2(zipped, sKey, ivKey);
227
- checksum = encryptChecksumV2(checksum, keyDataV2);
228
- ivSessionKey = ivKey;
229
- sessionKey = sKey;
230
- encryptionMethod = 'v2';
231
- finalKeyData = keyDataV2;
232
- if (options.displayIvSession) {
233
- log.info(`Your Iv Session key is ${ivSessionKey},
234
- keep it safe, you will need it to decrypt your bundle.
235
- It will be also visible in your dashboard\n`);
236
- }
237
- zipped = encryptedData;
238
- }
239
- const mbSize = Math.floor((zipped?.byteLength ?? 0) / 1024 / 1024);
240
- const mbSizeMax = Math.floor(maxUploadLength / 1024 / 1024);
241
- if (zipped?.byteLength > maxUploadLength) {
242
- uploadFail(`The bundle size is ${mbSize} Mb, this is greater than the maximum upload length ${mbSizeMax} Mb, please reduce the size of your bundle`);
243
- }
244
- else if (zipped?.byteLength > alertUploadSize) {
245
- log.warn(`WARNING !!\nThe bundle size is ${mbSize} Mb, this may take a while to download for users\n`);
246
- log.info(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`);
247
- await sendEvent(apikey, {
248
- channel: 'app-error',
249
- event: 'App Too Large',
250
- icon: '🚛',
251
- user_id: orgId,
252
- tags: {
253
- 'app-id': appid,
254
- },
255
- notify: false,
256
- });
257
- }
258
- return { zipped, ivSessionKey, sessionKey, checksum, encryptionMethod, finalKeyData };
259
- }
260
- async function uploadBundleToCapgoCloud(apikey, supabase, appid, bundle, orgId, zipped, options, tusChunkSize) {
261
- const spinner = spinnerC();
262
- spinner.start(`Uploading Bundle`);
263
- const startTime = performance.now();
264
- let isTus = false;
265
- if (options.dryUpload) {
266
- spinner.stop(`Dry run, bundle not uploaded\nBundle uploaded 💪 in 0 seconds`);
267
- return;
268
- }
269
- try {
270
- const localConfig = await getLocalConfig();
271
- if ((options.multipart !== undefined && options.multipart) || (options.tus !== undefined && options.tus)) {
272
- if (options.multipart) {
273
- log.info(`Uploading bundle with multipart is deprecated, we upload with TUS instead`);
274
- }
275
- else {
276
- log.info(`Uploading bundle with TUS protocol`);
277
- }
278
- await uploadTUS(apikey, zipped, orgId, appid, bundle, spinner, localConfig, tusChunkSize);
279
- isTus = true;
280
- const filePath = `orgs/${orgId}/apps/${appid}/${bundle}.zip`;
281
- const { error: changeError } = await supabase
282
- .from('app_versions')
283
- .update({ r2_path: filePath })
284
- .eq('name', bundle)
285
- .eq('app_id', appid);
286
- if (changeError) {
287
- log.error(`Cannot finish TUS upload ${formatError(changeError)}`);
288
- return Promise.reject(new Error('Cannot finish TUS upload'));
289
- }
290
- }
291
- else {
292
- const url = await uploadUrl(supabase, appid, bundle);
293
- if (!url) {
294
- log.error(`Cannot get upload url`);
295
- return Promise.reject(new Error('Cannot get upload url'));
296
- }
297
- await ky.put(url, {
298
- timeout: options.timeout || UPLOAD_TIMEOUT,
299
- retry: 5,
300
- body: zipped,
301
- headers: {
302
- 'Content-Type': 'application/zip',
303
- },
304
- });
305
- }
306
- }
307
- catch (errorUpload) {
308
- const endTime = performance.now();
309
- const uploadTime = ((endTime - startTime) / 1000).toFixed(2);
310
- spinner.stop(`Failed to upload bundle ( after ${uploadTime} seconds)`);
311
- if (errorUpload instanceof HTTPError) {
312
- try {
313
- const text = await errorUpload.response.text();
314
- if (text.startsWith('<?xml')) {
315
- // Parse XML error message
316
- const matches = text.match(/<Message>(.*?)<\/Message>/s);
317
- const message = matches ? matches[1] : 'Unknown S3 error';
318
- log.error(`S3 Upload Error: ${message}`);
319
- }
320
- else {
321
- const body = JSON.parse(text);
322
- log.error(`Response Error: ${body.error || body.status || body.message}`);
323
- }
324
- }
325
- catch {
326
- log.error(`Upload failed with status ${errorUpload.response.status}: ${errorUpload.message}`);
327
- }
328
- }
329
- else {
330
- if (!options.tus) {
331
- log.error(`Cannot upload bundle ( try again with --tus option) ${formatError(errorUpload)}`);
332
- }
333
- else {
334
- log.error(`Cannot upload bundle please contact support if the issue persists ${formatError(errorUpload)}`);
335
- }
336
- }
337
- // call delete version on path /delete_failed_version to delete the version
338
- await deletedFailedVersion(supabase, appid, bundle);
339
- throw errorUpload instanceof Error ? errorUpload : new Error(String(errorUpload));
340
- }
341
- const endTime = performance.now();
342
- const uploadTime = ((endTime - startTime) / 1000).toFixed(2);
343
- spinner.stop(`Bundle uploaded 💪 in (${uploadTime} seconds)`);
344
- await sendEvent(apikey, {
345
- channel: 'performance',
346
- event: isTus ? 'TUS upload zip performance' : 'Upload zip performance',
347
- icon: '🚄',
348
- user_id: orgId,
349
- tags: {
350
- 'app-id': appid,
351
- 'time': uploadTime,
352
- },
353
- notify: false,
354
- });
355
- }
356
- // It is really important that his function never terminates the program, it should always return, even if it fails
357
- async function deleteLinkedBundleOnUpload(supabase, appid, channel) {
358
- const { data, error } = await supabase
359
- .from('channels')
360
- .select('version ( id, name, deleted )')
361
- .eq('app_id', appid)
362
- .eq('name', channel);
363
- if (error) {
364
- log.error(`Cannot delete linked bundle on upload ${formatError(error)}`);
365
- return;
366
- }
367
- if (data.length === 0) {
368
- log.warn('No linked bundle found in the channel you are trying to upload to');
369
- return;
370
- }
371
- const version = data[0].version;
372
- if (version.deleted) {
373
- log.warn('The linked bundle is already deleted');
374
- return;
375
- }
376
- const { error: deleteError } = await supabase
377
- .from('app_versions')
378
- .update({ deleted: true })
379
- .eq('id', version.id);
380
- if (deleteError) {
381
- log.error(`Cannot delete linked bundle on upload ${formatError(deleteError)}`);
382
- return;
383
- }
384
- log.info('Linked bundle deleted');
385
- }
386
- async function setVersionInChannel(supabase, apikey, displayBundleUrl, bundle, channel, userId, orgId, appid, localConfig, selfAssign) {
387
- const { data: versionId } = await supabase
388
- .rpc('get_app_versions', { apikey, name_version: bundle, appid })
389
- .single();
390
- if (!versionId)
391
- uploadFail('Cannot get version id, cannot set channel');
392
- const { data: apiAccess } = await supabase
393
- .rpc('is_allowed_capgkey', { apikey, keymode: ['write', 'all'] })
394
- .single();
395
- if (apiAccess) {
396
- const { error: dbError3, data } = await updateOrCreateChannel(supabase, {
397
- name: channel,
398
- app_id: appid,
399
- created_by: userId,
400
- version: versionId,
401
- owner_org: orgId,
402
- ...(selfAssign ? { allow_device_self_set: true } : {}),
403
- });
404
- if (dbError3)
405
- uploadFail(`Cannot set channel, the upload key is not allowed to do that, use the "all" for this. ${formatError(dbError3)}`);
406
- const bundleUrl = `${localConfig.hostWeb}/app/p/${appid}/channel/${data.id}`;
407
- if (data?.public)
408
- log.info('Your update is now available in your public channel 🎉');
409
- else if (data?.id)
410
- log.info(`Link device to this bundle to try it: ${bundleUrl}`);
411
- if (displayBundleUrl)
412
- log.info(`Bundle url: ${bundleUrl}`);
413
- }
414
- else {
415
- log.warn('The upload key is not allowed to set the version in the channel');
416
- }
417
- }
418
- export async function getDefaulUploadChannel(appId, supabase, hostWeb) {
419
- const { error, data } = await supabase.from('apps')
420
- .select('default_upload_channel')
421
- .single();
422
- if (error) {
423
- log.warn('Cannot find default upload channel');
424
- log.info(`You can set it here: ${hostWeb}/app/p/${appId}/settings`);
425
- return null;
426
- }
427
- return data.default_upload_channel;
428
- }
429
- export async function uploadBundle(preAppid, options, shouldExit = true) {
430
- if (shouldExit)
431
- intro(`Uploading with CLI version ${pack.version}`);
432
- let sessionKey;
433
- const pm = getPMAndCommand();
434
- await checkAlerts();
435
- const { s3Region, s3Apikey, s3Apisecret, s3BucketName, s3Endpoint, s3Port, s3SSL } = options;
436
- const apikey = getApikey(options);
437
- const extConfig = await getConfig();
438
- const fileConfig = await getRemoteFileConfig();
439
- const { appid, path } = getAppIdAndPath(preAppid, options, extConfig.config);
440
- const bundle = await getBundle(extConfig.config, options);
441
- const defaultStorageProvider = options.external ? 'external' : 'r2-direct';
442
- let encryptionMethod = 'none';
443
- if (options.autoSetBundle) {
444
- await updateConfigUpdater({ version: bundle });
445
- }
446
- checkNotifyAppReady(options, path);
447
- log.info(`Upload ${appid}@${bundle} started from path "${path}" to Capgo cloud`);
448
- const localConfig = await getLocalConfig();
449
- if (options.supaHost && options.supaAnon) {
450
- log.info('Using custom supabase instance from provided options');
451
- localConfig.supaHost = options.supaHost;
452
- localConfig.supaKey = options.supaAnon;
453
- }
454
- const supabase = await createSupabaseClient(apikey, options.supaHost, options.supaAnon);
455
- const userId = await verifyUser(supabase, apikey, ['write', 'all', 'upload']);
456
- const channel = options.channel || await getDefaulUploadChannel(appid, supabase, localConfig.hostWeb) || 'dev';
457
- // Now if it does exist we will fetch the org id
458
- const orgId = await getOrganizationId(supabase, appid);
459
- await checkRemoteCliMessages(supabase, orgId, pack.version);
460
- await checkPlanValidUpload(supabase, orgId, apikey, appid, true);
461
- await checkTrial(supabase, orgId, localConfig);
462
- const { nativePackages, minUpdateVersion } = await verifyCompatibility(supabase, pm, options, channel, appid, bundle);
463
- const versionAlreadyExists = await checkVersionExists(supabase, appid, bundle, options.versionExistsOk);
464
- if (versionAlreadyExists) {
465
- return {
466
- success: true,
467
- skipped: true,
468
- reason: 'VERSION_EXISTS',
469
- bundle,
470
- checksum: null,
471
- encryptionMethod,
472
- storageProvider: defaultStorageProvider,
473
- };
474
- }
475
- if (options.external && !options.external.startsWith('https://')) {
476
- uploadFail(`External link should should start with "https://" current is "${options.external}"`);
477
- }
478
- if (options.deleteLinkedBundleOnUpload) {
479
- 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.');
480
- log.warn('Please make sure you want to do this, if you are not sure, please do not use this option.');
481
- }
482
- const versionData = {
483
- name: bundle,
484
- app_id: appid,
485
- session_key: undefined,
486
- external_url: options.external,
487
- storage_provider: defaultStorageProvider,
488
- min_update_version: minUpdateVersion,
489
- native_packages: nativePackages,
490
- owner_org: orgId,
491
- user_id: userId,
492
- checksum: undefined,
493
- link: options.link || null,
494
- comment: options.comment || null,
495
- };
496
- let zipped = null;
497
- let finalKeyData = '';
498
- if (!options.external) {
499
- const { zipped: _zipped, ivSessionKey, checksum, sessionKey: sk, encryptionMethod: em, finalKeyData: fkd } = await prepareBundleFile(path, options, apikey, orgId, appid, fileConfig.maxUploadLength, fileConfig.alertUploadSize);
500
- versionData.session_key = ivSessionKey;
501
- versionData.checksum = checksum;
502
- sessionKey = sk;
503
- zipped = _zipped;
504
- encryptionMethod = em;
505
- finalKeyData = fkd;
506
- if (!options.ignoreChecksumCheck) {
507
- await checkChecksum(supabase, appid, channel, checksum);
508
- }
509
- }
510
- else {
511
- await sendEvent(apikey, {
512
- channel: 'app',
513
- event: 'App external',
514
- icon: '📤',
515
- user_id: orgId,
516
- tags: {
517
- 'app-id': appid,
518
- },
519
- notify: false,
520
- });
521
- versionData.session_key = options.ivSessionKey;
522
- versionData.checksum = options.encryptedChecksum;
523
- }
524
- if (options.zip) {
525
- options.tus = false;
526
- }
527
- // ALLOW TO OVERRIDE THE FILE CONFIG WITH THE OPTIONS IF THE FILE CONFIG IS FORCED
528
- else if (!fileConfig.TUSUpload || options.external) {
529
- options.tus = false;
530
- }
531
- else {
532
- options.tus = options.tus || fileConfig.TUSUploadForced;
533
- }
534
- if (!fileConfig.partialUpload || options.external) {
535
- options.delta = false;
536
- }
537
- else {
538
- options.delta = options.delta || options.partial || options.deltaOnly || options.partialOnly || fileConfig.partialUploadForced;
539
- }
540
- if (options.encryptPartial && encryptionMethod === 'v1')
541
- uploadFail('You cannot encrypt the partial update if you are not using the v2 encryption method');
542
- // Auto-encrypt partial updates for updater versions > 6.14.5 if encryption method is v2
543
- if (options.delta && encryptionMethod === 'v2' && !options.encryptPartial) {
544
- // Check updater version
545
- const root = join(findRoot(cwd()), PACKNAME);
546
- const dependencies = await getAllPackagesDependencies(undefined, options.packageJson || root);
547
- const updaterVersion = dependencies.get('@capgo/capacitor-updater');
548
- const coerced = coerceVersion(updaterVersion);
549
- if (updaterVersion && coerced && semverGte(coerced.version, '6.14.4')) {
550
- log.info(`Auto-enabling partial update encryption for updater version ${coerced.version} (> 6.14.4)`);
551
- options.encryptPartial = true;
552
- }
553
- }
554
- const manifest = options.delta ? await prepareBundlePartialFiles(path, apikey, orgId, appid, options.encryptPartial ? encryptionMethod : 'none', finalKeyData) : [];
555
- const { error: dbError } = await updateOrCreateVersion(supabase, versionData);
556
- if (dbError)
557
- uploadFail(`Cannot add bundle ${formatError(dbError)}`);
558
- if (options.tusChunkSize && options.tusChunkSize > fileConfig.maxChunkSize) {
559
- log.error(`Chunk size ${options.tusChunkSize} is greater than the maximum chunk size ${fileConfig.maxChunkSize}, using the maximum chunk size`);
560
- options.tusChunkSize = fileConfig.maxChunkSize;
561
- }
562
- else if (!options.tusChunkSize) {
563
- options.tusChunkSize = fileConfig.maxChunkSize;
564
- }
565
- if (zipped && (s3BucketName || s3Endpoint || s3Region || s3Apikey || s3Apisecret || s3Port || s3SSL)) {
566
- if (!s3BucketName || !s3Endpoint || !s3Region || !s3Apikey || !s3Apisecret || !s3Port)
567
- uploadFail('Missing argument, for S3 upload you need to provide a bucket name, endpoint, region, port, API key, and API secret');
568
- log.info('Uploading to S3');
569
- const endPoint = s3SSL ? `https://${s3Endpoint}` : `http://${s3Endpoint}`;
570
- const s3Client = new S3Client({
571
- endPoint: s3Endpoint,
572
- region: s3Region,
573
- port: s3Port,
574
- pathStyle: true,
575
- bucket: s3BucketName,
576
- accessKey: s3Apikey,
577
- secretKey: s3Apisecret,
578
- });
579
- const fileName = `${appid}-${bundle}`;
580
- const encodeFileName = encodeURIComponent(fileName);
581
- await s3Client.putObject(fileName, Uint8Array.from(zipped));
582
- versionData.external_url = `${endPoint}/${encodeFileName}`;
583
- versionData.storage_provider = 'external';
584
- }
585
- else if (zipped) {
586
- if (!options.partialOnly && !options.deltaOnly) {
587
- await uploadBundleToCapgoCloud(apikey, supabase, appid, bundle, orgId, zipped, options, options.tusChunkSize);
588
- }
589
- let finalManifest = null;
590
- try {
591
- if (options.dryUpload) {
592
- options.delta = false;
593
- }
594
- const encryptionData = versionData.session_key && options.encryptPartial && sessionKey
595
- ? {
596
- sessionKey,
597
- ivSessionKey: versionData.session_key,
598
- }
599
- : undefined;
600
- finalManifest = options.delta
601
- ? await uploadPartial(apikey, manifest, path, appid, bundle, orgId, encryptionData, options)
602
- : null;
603
- }
604
- catch (err) {
605
- 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`);
606
- }
607
- versionData.storage_provider = 'r2';
608
- versionData.manifest = finalManifest;
609
- const { error: dbError2 } = await updateOrCreateVersion(supabase, versionData);
610
- if (dbError2)
611
- uploadFail(`Cannot update bundle ${formatError(dbError2)}`);
612
- }
613
- // Check we have app access to this appId
614
- const permissions = await checkAppExistsAndHasPermissionOrgErr(supabase, apikey, appid, OrganizationPerm.upload);
615
- if (options.deleteLinkedBundleOnUpload && hasOrganizationPerm(permissions, OrganizationPerm.write)) {
616
- await deleteLinkedBundleOnUpload(supabase, appid, channel);
617
- }
618
- else if (options.deleteLinkedBundleOnUpload) {
619
- log.warn('Cannot delete linked bundle on upload as a upload organization member');
620
- }
621
- if (hasOrganizationPerm(permissions, OrganizationPerm.write)) {
622
- await setVersionInChannel(supabase, apikey, !!options.bundleUrl, bundle, channel, userId, orgId, appid, localConfig, options.selfAssign);
623
- }
624
- else {
625
- log.warn('Cannot set channel as a upload organization member');
626
- }
627
- await sendEvent(apikey, {
628
- channel: 'app',
629
- event: 'App Uploaded',
630
- icon: '⏫',
631
- user_id: orgId,
632
- tags: {
633
- 'app-id': appid,
634
- },
635
- notify: false,
636
- });
637
- const result = {
638
- success: true,
639
- bundle,
640
- checksum: versionData.checksum ?? null,
641
- encryptionMethod,
642
- sessionKey: sessionKey ? sessionKey.toString('base64') : undefined,
643
- ivSessionKey: typeof versionData.session_key === 'string' ? versionData.session_key : undefined,
644
- storageProvider: versionData.storage_provider,
645
- };
646
- if (shouldExit && !result.skipped)
647
- outro('Time to share your update to the world 🌍');
648
- return result;
649
- }
650
- function checkValidOptions(options) {
651
- if (options.ivSessionKey && !options.external) {
652
- uploadFail('You need to provide an external url if you want to use the --iv-session-key option');
653
- }
654
- if (options.encryptedChecksum && !options.external) {
655
- uploadFail('You need to provide an external url if you want to use the --encrypted-checksum option');
656
- }
657
- if ((options.partial || options.delta || options.partialOnly || options.deltaOnly) && options.external) {
658
- uploadFail('You cannot use the --partial/--delta/--partial-only/--delta-only option with an external url');
659
- }
660
- if (options.tus && options.external) {
661
- uploadFail('You cannot use the --tus option with an external url');
662
- }
663
- if (options.dryUpload && options.external) {
664
- uploadFail('You cannot use the --dry-upload option with an external url');
665
- }
666
- if (options.multipart && options.external) {
667
- uploadFail('You cannot use the --multipart option with an external url');
668
- }
669
- // cannot set key if external
670
- if (options.external && (options.keyV2 || options.keyDataV2)) {
671
- uploadFail('You cannot set a key if you are uploading to an external url');
672
- }
673
- // cannot set key-v2 and key-data-v2
674
- if (options.keyV2 && options.keyDataV2) {
675
- uploadFail('You cannot set both key-v2 and key-data-v2');
676
- }
677
- // cannot set s3 and external
678
- if (options.external && (options.s3Region || options.s3Apikey || options.s3Apisecret || options.s3Endpoint || options.s3BucketName || options.s3Port || options.s3SSL)) {
679
- uploadFail('You cannot set S3 options if you are uploading to an external url, it\'s automatically handled');
680
- }
681
- // cannot set --encrypted-checksum if not external
682
- if (options.encryptedChecksum && !options.external) {
683
- uploadFail('You cannot set the --encrypted-checksum option if you are not uploading to an external url');
684
- }
685
- // cannot set min-update-version and auto-min-update-version
686
- if (options.minUpdateVersion && options.autoMinUpdateVersion) {
687
- uploadFail('You cannot set both min-update-version and auto-min-update-version, use only one of them');
688
- }
689
- }
690
- export async function uploadCommand(appid, options) {
691
- try {
692
- checkValidOptions(options);
693
- await uploadBundle(appid, options, true);
694
- }
695
- catch (error) {
696
- log.error(formatError(error));
697
- throw error instanceof Error ? error : new Error(String(error));
698
- }
699
- }
700
- //# sourceMappingURL=upload.js.map