@capgo/cli 5.0.0-alpha.3 โ†’ 5.0.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 (59) hide show
  1. package/README.md +197 -37
  2. package/dist/index.js +327 -65172
  3. package/dist/package.json +83 -0
  4. package/package.json +48 -61
  5. package/.eslintignore +0 -4
  6. package/.github/FUNDING.yml +0 -1
  7. package/.github/workflows/build.yml +0 -46
  8. package/.github/workflows/bump_version.yml +0 -56
  9. package/.github/workflows/test.yml +0 -30
  10. package/.prettierignore +0 -6
  11. package/.vscode/launch.json +0 -23
  12. package/.vscode/settings.json +0 -5
  13. package/.vscode/tasks.json +0 -42
  14. package/CHANGELOG.md +0 -2727
  15. package/build.mjs +0 -23
  16. package/bun.lockb +0 -0
  17. package/capacitor.config.ts +0 -33
  18. package/crypto_explained.png +0 -0
  19. package/eslint.config.js +0 -3
  20. package/renovate.json +0 -23
  21. package/src/api/app.ts +0 -75
  22. package/src/api/channels.ts +0 -142
  23. package/src/api/crypto.ts +0 -121
  24. package/src/api/devices_override.ts +0 -41
  25. package/src/api/update.ts +0 -12
  26. package/src/api/versions.ts +0 -98
  27. package/src/app/add.ts +0 -154
  28. package/src/app/debug.ts +0 -214
  29. package/src/app/delete.ts +0 -68
  30. package/src/app/info.ts +0 -87
  31. package/src/app/list.ts +0 -63
  32. package/src/app/set.ts +0 -94
  33. package/src/bundle/check.ts +0 -42
  34. package/src/bundle/cleanup.ts +0 -128
  35. package/src/bundle/compatibility.ts +0 -70
  36. package/src/bundle/decrypt.ts +0 -65
  37. package/src/bundle/delete.ts +0 -53
  38. package/src/bundle/encrypt.ts +0 -69
  39. package/src/bundle/list.ts +0 -43
  40. package/src/bundle/unlink.ts +0 -80
  41. package/src/bundle/upload.ts +0 -434
  42. package/src/bundle/zip.ts +0 -137
  43. package/src/channel/add.ts +0 -73
  44. package/src/channel/currentBundle.ts +0 -73
  45. package/src/channel/delete.ts +0 -51
  46. package/src/channel/list.ts +0 -49
  47. package/src/channel/set.ts +0 -171
  48. package/src/index.ts +0 -285
  49. package/src/init.ts +0 -301
  50. package/src/key.ts +0 -158
  51. package/src/login.ts +0 -66
  52. package/src/types/capacitor__cli.d.ts +0 -6
  53. package/src/types/supabase.types.ts +0 -2065
  54. package/src/utils.ts +0 -719
  55. package/test/chunk_convert.ts +0 -28
  56. package/test/data.ts +0 -18769
  57. package/test/test_headers_rls.ts +0 -24
  58. package/test/test_semver.ts +0 -13
  59. package/tsconfig.json +0 -39
@@ -1,434 +0,0 @@
1
- import { randomUUID } from 'node:crypto'
2
- import { existsSync, readFileSync } from 'node:fs'
3
- import process from 'node:process'
4
- import type { Buffer } from 'node:buffer'
5
- import AdmZip from 'adm-zip'
6
- import { program } from 'commander'
7
- import * as p from '@clack/prompts'
8
- import { checksum as getChecksum } from '@tomasklaen/checksum'
9
- import ciDetect from 'ci-info'
10
- import ky from 'ky'
11
- import { checkLatest } from '../api/update'
12
- import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
13
- import { encryptSource } from '../api/crypto'
14
- import type {
15
- OptionsBase,
16
- } from '../utils'
17
- import {
18
- OrganizationPerm,
19
- baseKey,
20
- checKOldEncryption,
21
- checkCompatibility,
22
- checkPlanValid,
23
- convertAppName,
24
- createSupabaseClient,
25
- findSavedKey,
26
- formatError,
27
- getAppOwner,
28
- getConfig,
29
- getLocalConfig,
30
- getLocalDepenencies,
31
- hasOrganizationPerm,
32
- regexSemver,
33
- requireUpdateMetadata,
34
- updateOrCreateChannel,
35
- updateOrCreateVersion,
36
- uploadUrl,
37
- useLogSnag,
38
- verifyUser,
39
- } from '../utils'
40
- import { checkIndexPosition, searchInDirectory } from './check'
41
-
42
- const alertMb = 20
43
-
44
- interface Options extends OptionsBase {
45
- bundle?: string
46
- path?: string
47
- channel?: string
48
- displayIvSession?: boolean
49
- external?: string
50
- key?: boolean | string
51
- keyData?: string
52
- ivSessionKey?: string
53
- bundleUrl?: boolean
54
- codeCheck?: boolean
55
- minUpdateVersion?: string
56
- autoMinUpdateVersion?: boolean
57
- ignoreMetadataCheck?: boolean
58
- }
59
-
60
- export async function uploadBundle(appid: string, options: Options, shouldExit = true) {
61
- p.intro(`Uploading`)
62
- await checkLatest()
63
- let { bundle, path, channel } = options
64
- const { external, key, displayIvSession, autoMinUpdateVersion, ignoreMetadataCheck } = options
65
- let { minUpdateVersion } = options
66
- options.apikey = options.apikey || findSavedKey()
67
- const snag = useLogSnag()
68
-
69
- channel = channel || 'dev'
70
-
71
- const config = await getConfig()
72
- const localS3: boolean = (config.app.extConfig.plugins && config.app.extConfig.plugins.CapacitorUpdater
73
- && config.app.extConfig.plugins.CapacitorUpdater.localS3) === true
74
-
75
- const checkNotifyAppReady = options.codeCheck
76
- appid = appid || config?.app?.appId
77
- // create bundle name format : 1.0.0-beta.x where x is a uuid
78
- const uuid = randomUUID().split('-')[0]
79
- bundle = bundle || config?.app?.extConfig?.plugins?.CapacitorUpdater?.version || config?.app?.package?.version || `0.0.1-beta.${uuid}`
80
- // check if bundle is valid
81
- if (!regexSemver.test(bundle)) {
82
- p.log.error(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`)
83
- program.error('')
84
- }
85
- path = path || config?.app?.webDir
86
- if (!options.apikey) {
87
- p.log.error(`Missing API key, you need to provide a API key to upload your bundle`)
88
- program.error('')
89
- }
90
- if (!appid || !bundle || !path) {
91
- p.log.error('Missing argument, you need to provide a appid and a bundle and a path, or be in a capacitor project')
92
- program.error('')
93
- }
94
- // check if path exist
95
- if (!existsSync(path)) {
96
- p.log.error(`Path ${path} does not exist, build your app first, or provide a valid path`)
97
- program.error('')
98
- }
99
-
100
- if (typeof checkNotifyAppReady === 'undefined' || checkNotifyAppReady) {
101
- const isPluginConfigured = searchInDirectory(path, 'notifyAppReady')
102
- if (!isPluginConfigured) {
103
- p.log.error(`notifyAppReady() is missing in the source code. see: https://capgo.app/docs/plugin/api/#notifyappready`)
104
- program.error('')
105
- }
106
- const foundIndex = checkIndexPosition(path)
107
- if (!foundIndex) {
108
- p.log.error(`index.html is missing in the root folder or in the only folder in the root folder`)
109
- program.error('')
110
- }
111
- }
112
-
113
- p.log.info(`Upload ${appid}@${bundle} started from path "${path}" to Capgo cloud`)
114
-
115
- const localConfig = await getLocalConfig()
116
- const supabase = await createSupabaseClient(options.apikey)
117
- const userId = await verifyUser(supabase, options.apikey, ['write', 'all', 'upload'])
118
- // Check we have app access to this appId
119
- // await checkAppExistsAndHasPermissionErr(supabase, options.apikey, appid);
120
-
121
- const permissions = await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appid, OrganizationPerm.upload)
122
- await checkPlanValid(supabase, userId, options.apikey, appid, true)
123
-
124
- const updateMetadataRequired = await requireUpdateMetadata(supabase, channel)
125
-
126
- // Check compatibility here
127
- const { data: channelData, error: channelError } = await supabase
128
- .from('channels')
129
- .select('version ( minUpdateVersion, native_packages )')
130
- .eq('name', channel)
131
- .eq('app_id', appid)
132
- .single()
133
-
134
- // eslint-disable-next-line no-undef-init
135
- let localDependencies: Awaited<ReturnType<typeof getLocalDepenencies>> | undefined = undefined
136
- let finalCompatibility: Awaited<ReturnType<typeof checkCompatibility>>['finalCompatibility']
137
-
138
- // We only check compatibility IF the channel exists
139
- if (!channelError && channelData && channelData.version && (channelData.version as any).native_packages && !ignoreMetadataCheck) {
140
- const spinner = p.spinner()
141
- spinner.start(`Checking bundle compatibility with channel ${channel}`)
142
- const {
143
- finalCompatibility: finalCompatibilityWithChannel,
144
- localDependencies: localDependenciesWithChannel,
145
- } = await checkCompatibility(supabase, appid, channel)
146
-
147
- finalCompatibility = finalCompatibilityWithChannel
148
- localDependencies = localDependenciesWithChannel
149
-
150
- if (finalCompatibility.find(x => x.localVersion !== x.remoteVersion)) {
151
- p.log.error(`Your bundle is not compatible with the channel ${channel}`)
152
- p.log.warn(`You can check compatibility with "npx @capgo/cli bundle compatibility"`)
153
-
154
- if (autoMinUpdateVersion) {
155
- minUpdateVersion = bundle
156
- p.log.info(`Auto set min-update-version to ${minUpdateVersion}`)
157
- }
158
- }
159
- else if (autoMinUpdateVersion) {
160
- try {
161
- const { minUpdateVersion: lastMinUpdateVersion } = channelData.version as any
162
- if (!lastMinUpdateVersion || !regexSemver.test(lastMinUpdateVersion)) {
163
- p.log.error('Invalid remote min update version, skipping auto setting compatibility')
164
- program.error('')
165
- }
166
-
167
- minUpdateVersion = lastMinUpdateVersion
168
- p.log.info(`Auto set min-update-version to ${minUpdateVersion}`)
169
- }
170
- catch (error) {
171
- p.log.error(`Cannot auto set compatibility, invalid data ${channelData}`)
172
- program.error('')
173
- }
174
- }
175
- spinner.stop(`Bundle compatible with ${channel} channel`)
176
- }
177
- else if (!ignoreMetadataCheck) {
178
- p.log.warn(`Channel ${channel} is new or it's your first upload with compatibility check, it will be ignored this time`)
179
- localDependencies = await getLocalDepenencies()
180
-
181
- if (autoMinUpdateVersion) {
182
- minUpdateVersion = bundle
183
- p.log.info(`Auto set min-update-version to ${minUpdateVersion}`)
184
- }
185
- }
186
-
187
- if (updateMetadataRequired && !minUpdateVersion && !ignoreMetadataCheck) {
188
- p.log.error(`You need to provide a min-update-version to upload a bundle to this channel`)
189
- program.error('')
190
- }
191
-
192
- if (minUpdateVersion) {
193
- if (!regexSemver.test(minUpdateVersion)) {
194
- p.log.error(`Your minimal version update ${minUpdateVersion}, is not valid it should follow semver convention : https://semver.org/`)
195
- program.error('')
196
- }
197
- }
198
-
199
- const { data: isTrial, error: isTrialsError } = await supabase
200
- .rpc('is_trial', { userid: userId })
201
- .single()
202
- if ((isTrial && isTrial > 0) || isTrialsError) {
203
- p.log.warn(`WARNING !!\nTrial expires in ${isTrial} days`)
204
- p.log.warn(`Upgrade here: ${localConfig.hostWeb}/dashboard/settings/plans`)
205
- }
206
-
207
- // check if app already exist
208
- const { data: appVersion, error: appVersionError } = await supabase
209
- .rpc('exist_app_versions', { appid, apikey: options.apikey, name_version: bundle })
210
- .single()
211
-
212
- if (appVersion || appVersionError) {
213
- p.log.error(`Version already exists ${formatError(appVersionError)}`)
214
- program.error('')
215
- }
216
- // make bundle safe for s3 name https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html
217
- const safeBundle = bundle.replace(/[^a-zA-Z0-9-_.!*'()]/g, '__')
218
- const fileName = `${safeBundle}.zip`
219
-
220
- let sessionKey
221
- let checksum = ''
222
- let zipped: Buffer | null = null
223
- if (!external) {
224
- const zip = new AdmZip()
225
- zip.addLocalFolder(path)
226
- zipped = zip.toBuffer()
227
- const s = p.spinner()
228
- s.start(`Calculating checksum`)
229
- checksum = await getChecksum(zipped, 'crc32')
230
- s.stop(`Checksum: ${checksum}`)
231
- if (key === false) {
232
- p.log.info(`Encryption ignored`)
233
- }
234
- else if (key || existsSync(baseKey)) {
235
- await checKOldEncryption()
236
- const privateKey = typeof key === 'string' ? key : baseKey
237
- let keyData = options.keyData || ''
238
- // check if privateKey exist
239
- if (!keyData && !existsSync(privateKey)) {
240
- p.log.error(`Cannot find private key ${privateKey}`)
241
- if (ciDetect.isCI)
242
- program.error('')
243
-
244
- const res = await p.confirm({ message: 'Do you want to use our public key ?' })
245
- if (!res) {
246
- p.log.error(`Error: Missing public key`)
247
- program.error('')
248
- }
249
- keyData = localConfig.signKey || ''
250
- }
251
- await snag.track({
252
- channel: 'app',
253
- event: 'App encryption',
254
- icon: '๐Ÿ”‘',
255
- user_id: userId,
256
- tags: {
257
- 'app-id': appid,
258
- },
259
- notify: false,
260
- }).catch()
261
- // open with fs privateKey path
262
- if (!keyData) {
263
- const keyFile = readFileSync(privateKey)
264
- keyData = keyFile.toString()
265
- }
266
- // encrypt
267
- p.log.info(`Encrypting your bundle`)
268
- const res = encryptSource(zipped, keyData)
269
- sessionKey = res.ivSessionKey
270
- if (displayIvSession) {
271
- p.log.info(`Your Iv Session key is ${sessionKey},
272
- keep it safe, you will need it to decrypt your bundle.
273
- It will be also visible in your dashboard\n`)
274
- }
275
- zipped = res.encryptedData
276
- }
277
- const mbSize = Math.floor(zipped.byteLength / 1024 / 1024)
278
- if (mbSize > alertMb) {
279
- p.log.warn(`WARNING !!\nThe app size is ${mbSize} Mb, this may take a while to download for users\n`)
280
- p.log.info(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`)
281
- await snag.track({
282
- channel: 'app-error',
283
- event: 'App Too Large',
284
- icon: '๐Ÿš›',
285
- user_id: userId,
286
- tags: {
287
- 'app-id': appid,
288
- },
289
- notify: false,
290
- }).catch()
291
- }
292
- }
293
- else if (external && !external.startsWith('https://')) {
294
- p.log.error(`External link should should start with "https://" current is "${external}"`)
295
- program.error('')
296
- }
297
- else {
298
- await snag.track({
299
- channel: 'app',
300
- event: 'App external',
301
- icon: '๐Ÿ“ค',
302
- user_id: userId,
303
- tags: {
304
- 'app-id': appid,
305
- },
306
- notify: false,
307
- }).catch()
308
- sessionKey = options.ivSessionKey
309
- }
310
-
311
- const hashedLocalDependencies = localDependencies
312
- ? new Map(localDependencies
313
- .filter(a => !!a.native && a.native !== undefined)
314
- .map(a => [a.name, a]))
315
- : new Map()
316
-
317
- const nativePackages = (hashedLocalDependencies.size > 0 || !options.ignoreMetadataCheck) ? Array.from(hashedLocalDependencies, ([name, value]) => ({ name, version: value.version })) : undefined
318
-
319
- const appOwner = await getAppOwner(supabase, appid)
320
-
321
- const versionData = {
322
- bucket_id: external ? undefined : fileName,
323
- user_id: appOwner,
324
- name: bundle,
325
- app_id: appid,
326
- session_key: sessionKey,
327
- external_url: external,
328
- storage_provider: external ? 'external' : 'r2-direct',
329
- minUpdateVersion,
330
- native_packages: nativePackages,
331
- checksum,
332
- }
333
- const { error: dbError } = await updateOrCreateVersion(supabase, versionData)
334
- if (dbError) {
335
- p.log.error(`Cannot add bundle ${formatError(dbError)}`)
336
- program.error('')
337
- }
338
- if (!external && zipped) {
339
- const spinner = p.spinner()
340
- spinner.start(`Uploading Bundle`)
341
-
342
- const url = await uploadUrl(supabase, appid, fileName)
343
- if (!url) {
344
- p.log.error(`Cannot get upload url`)
345
- program.error('')
346
- }
347
- await ky.put(url, {
348
- timeout: 60000,
349
- body: zipped,
350
- headers: (!localS3
351
- ? {
352
- 'Content-Type': 'application/octet-stream',
353
- 'Cache-Control': 'public, max-age=456789, immutable',
354
- 'x-amz-meta-crc32': checksum,
355
- }
356
- : undefined),
357
- })
358
- versionData.storage_provider = 'r2'
359
- const { error: dbError2 } = await updateOrCreateVersion(supabase, versionData)
360
- if (dbError2) {
361
- p.log.error(`Cannot update bundle ${formatError(dbError2)}`)
362
- program.error('')
363
- }
364
- spinner.stop('Bundle Uploaded ๐Ÿ’ช')
365
- }
366
- const { data: versionId } = await supabase
367
- .rpc('get_app_versions', { apikey: options.apikey, name_version: bundle, appid })
368
- .single()
369
-
370
- if (versionId && hasOrganizationPerm(permissions, OrganizationPerm.write)) {
371
- const { error: dbError3, data } = await updateOrCreateChannel(supabase, {
372
- name: channel,
373
- app_id: appid,
374
- created_by: appOwner,
375
- version: versionId,
376
- })
377
- if (dbError3) {
378
- p.log.error(`Cannot set channel, the upload key is not allowed to do that, use the "all" for this. ${formatError(dbError3)}`)
379
- program.error('')
380
- }
381
- const appidWeb = convertAppName(appid)
382
- const bundleUrl = `${localConfig.hostWeb}/app/p/${appidWeb}/channel/${data.id}`
383
- if (data?.public)
384
- p.log.info('Your update is now available in your public channel ๐ŸŽ‰')
385
- else if (data?.id)
386
- p.log.info(`Link device to this bundle to try it: ${bundleUrl}`)
387
-
388
- if (options.bundleUrl)
389
- p.log.info(`Bundle url: ${bundleUrl}`)
390
- }
391
- else if (!versionId) {
392
- p.log.warn('Cannot set bundle with upload key, use key with more rights for that')
393
- program.error('')
394
- }
395
- else if (!hasOrganizationPerm(permissions, OrganizationPerm.write)) {
396
- p.log.warn('Cannot set channel as a upload organization member')
397
- }
398
- await snag.track({
399
- channel: 'app',
400
- event: 'App Uploaded',
401
- icon: 'โซ',
402
- user_id: userId,
403
- tags: {
404
- 'app-id': appid,
405
- },
406
- notify: false,
407
- }).catch()
408
- if (shouldExit) {
409
- p.outro('Time to share your update to the world ๐ŸŒ')
410
- process.exit()
411
- }
412
- return true
413
- }
414
-
415
- export async function uploadCommand(apikey: string, options: Options) {
416
- try {
417
- await uploadBundle(apikey, options, true)
418
- }
419
- catch (error) {
420
- p.log.error(formatError(error))
421
- program.error('')
422
- }
423
- }
424
-
425
- export async function uploadDeprecatedCommand(apikey: string, options: Options) {
426
- p.log.warn('โš ๏ธ This command is deprecated, use "npx @capgo/cli bundle upload" instead โš ๏ธ')
427
- try {
428
- await uploadBundle(apikey, options, true)
429
- }
430
- catch (error) {
431
- p.log.error(formatError(error))
432
- program.error('')
433
- }
434
- }
package/src/bundle/zip.ts DELETED
@@ -1,137 +0,0 @@
1
- import { randomUUID } from 'node:crypto'
2
- import { writeFileSync } from 'node:fs'
3
- import process from 'node:process'
4
- import AdmZip from 'adm-zip'
5
- import { program } from 'commander'
6
- import * as p from '@clack/prompts'
7
- import { checksum as getChecksum } from '@tomasklaen/checksum'
8
- import { checkLatest } from '../api/update'
9
- import type {
10
- OptionsBase,
11
- } from '../utils'
12
- import {
13
- formatError,
14
- getConfig,
15
- regexSemver,
16
- useLogSnag,
17
- } from '../utils'
18
- import { checkIndexPosition, searchInDirectory } from './check'
19
-
20
- const alertMb = 20
21
-
22
- interface Options extends OptionsBase {
23
- bundle?: string
24
- path?: string
25
- codeCheck?: boolean
26
- name?: string
27
- json?: boolean
28
- }
29
-
30
- export async function zipBundle(appId: string, options: Options) {
31
- let { bundle, path } = options
32
- const { json } = options
33
- const snag = useLogSnag()
34
- if (!json)
35
- await checkLatest()
36
-
37
- const config = await getConfig()
38
- appId = appId || config?.app?.appId
39
- // create bundle name format : 1.0.0-beta.x where x is a uuid
40
- const uuid = randomUUID().split('-')[0]
41
- bundle = bundle || config?.app?.package?.version || `0.0.1-beta.${uuid}`
42
- if (!json)
43
- p.intro(`Zipping ${appId}@${bundle}`)
44
- // check if bundle is valid
45
- if (!regexSemver.test(bundle)) {
46
- if (!json)
47
- p.log.error(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`)
48
- else
49
- console.error(formatError({ error: 'invalid_semver' }))
50
- program.error('')
51
- }
52
- path = path || config?.app?.webDir
53
- if (!appId || !bundle || !path) {
54
- if (!json)
55
- p.log.error('Missing argument, you need to provide a appId and a bundle and a path, or be in a capacitor project')
56
- else
57
- console.error(formatError({ error: 'missing_argument' }))
58
- program.error('')
59
- }
60
- if (!json)
61
- p.log.info(`Started from path "${path}"`)
62
- const checkNotifyAppReady = options.codeCheck
63
- if (typeof checkNotifyAppReady === 'undefined' || checkNotifyAppReady) {
64
- const isPluginConfigured = searchInDirectory(path, 'notifyAppReady')
65
- if (!isPluginConfigured) {
66
- if (!json)
67
- p.log.error(`notifyAppReady() is missing in the source code. see: https://capgo.app/docs/plugin/api/#notifyappready`)
68
- else
69
- console.error(formatError({ error: 'notifyAppReady_not_in_source_code' }))
70
- program.error('')
71
- }
72
- const foundIndex = checkIndexPosition(path)
73
- if (!foundIndex) {
74
- if (!json)
75
- p.log.error(`index.html is missing in the root folder or in the only folder in the root folder`)
76
- else
77
- console.error(formatError({ error: 'index_html_not_found' }))
78
- program.error('')
79
- }
80
- }
81
- const zip = new AdmZip()
82
- zip.addLocalFolder(path)
83
- const zipped = zip.toBuffer()
84
- if (!json)
85
- p.log.info(`Zipped ${zipped.byteLength} bytes`)
86
- const s = p.spinner()
87
- if (!json)
88
- s.start(`Calculating checksum`)
89
- const checksum = await getChecksum(zipped, 'crc32')
90
- if (!json)
91
- s.stop(`Checksum: ${checksum}`)
92
- const mbSize = Math.floor(zipped.byteLength / 1024 / 1024)
93
- // We do not issue this warning for json
94
- if (mbSize > alertMb && !json) {
95
- p.log.warn(`WARNING !!\nThe app size is ${mbSize} Mb, this may take a while to download for users\n`)
96
- p.log.warn(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`)
97
- await snag.track({
98
- channel: 'app-error',
99
- event: 'App Too Large',
100
- icon: '๐Ÿš›',
101
- tags: {
102
- 'app-id': appId,
103
- },
104
- notify: false,
105
- }).catch()
106
- }
107
- const s2 = p.spinner()
108
- const name = options.name || `${appId}_${bundle}.zip`
109
- if (!json)
110
- s2.start(`Saving to ${name}`)
111
- writeFileSync(name, zipped)
112
- if (!json)
113
- s2.stop(`Saved to ${name}`)
114
-
115
- if (options.json) {
116
- const output = {
117
- bundle,
118
- filename: name,
119
- checksum,
120
- }
121
- p.log.info(formatError(output))
122
- }
123
-
124
- await snag.track({
125
- channel: 'app',
126
- event: 'App zip',
127
- icon: 'โซ',
128
- tags: {
129
- 'app-id': appId,
130
- },
131
- notify: false,
132
- }).catch()
133
-
134
- if (!json)
135
- p.outro(`Done โœ…`)
136
- process.exit()
137
- }
@@ -1,73 +0,0 @@
1
- import process from 'node:process'
2
- import { program } from 'commander'
3
- import * as p from '@clack/prompts'
4
- import { checkAppExistsAndHasPermissionErr } from '../api/app'
5
- import { createChannel, findUnknownVersion } from '../api/channels'
6
- import type { OptionsBase } from '../utils'
7
- import { createSupabaseClient, findSavedKey, getConfig, useLogSnag, verifyUser } from '../utils'
8
-
9
- interface Options extends OptionsBase {
10
- default?: boolean
11
- }
12
-
13
- export async function addChannel(channelId: string, appId: string, options: Options, shouldExit = true) {
14
- p.intro(`Create channel`)
15
- options.apikey = options.apikey || findSavedKey()
16
- const config = await getConfig()
17
- appId = appId || config?.app?.appId
18
- const snag = useLogSnag()
19
-
20
- if (!options.apikey) {
21
- p.log.error('Missing API key, you need to provide a API key to upload your bundle')
22
- program.error('')
23
- }
24
- if (!appId) {
25
- p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
26
- program.error('')
27
- }
28
- const supabase = await createSupabaseClient(options.apikey)
29
-
30
- const userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
31
- // Check we have app access to this appId
32
- await checkAppExistsAndHasPermissionErr(supabase, options.apikey, appId)
33
-
34
- p.log.info(`Creating channel ${appId}#${channelId} to Capgo`)
35
- try {
36
- const data = await findUnknownVersion(supabase, appId)
37
- if (!data) {
38
- p.log.error(`Cannot find default version for channel creation, please contact Capgo support ๐Ÿคจ`)
39
- program.error('')
40
- }
41
- await createChannel(supabase, {
42
- name: channelId,
43
- app_id: appId,
44
- version: data.id,
45
- created_by: userId,
46
- })
47
- p.log.success(`Channel created โœ…`)
48
- await snag.track({
49
- channel: 'channel',
50
- event: 'Create channel',
51
- icon: 'โœ…',
52
- user_id: userId,
53
- tags: {
54
- 'app-id': appId,
55
- 'channel': channelId,
56
- },
57
- notify: false,
58
- }).catch()
59
- }
60
- catch (error) {
61
- p.log.error(`Cannot create Channel ๐Ÿ™€`)
62
- return false
63
- }
64
- if (shouldExit) {
65
- p.outro(`Done โœ…`)
66
- process.exit()
67
- }
68
- return true
69
- }
70
-
71
- export async function addChannelCommand(apikey: string, appId: string, options: Options) {
72
- addChannel(apikey, appId, options, true)
73
- }