@capgo/cli 4.12.12 → 4.12.14-beta.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.
@@ -1,17 +1,19 @@
1
1
  import { randomUUID } from 'node:crypto'
2
2
  import { existsSync, readFileSync } from 'node:fs'
3
+ import { exit } from 'node:process'
3
4
  import type { Buffer } from 'node:buffer'
4
- import process from 'node:process'
5
- import * as p from '@clack/prompts'
6
5
  import { program } from 'commander'
7
6
  import { checksum as getChecksum } from '@tomasklaen/checksum'
8
7
  import ciDetect from 'ci-info'
9
8
  import type LogSnag from 'logsnag'
9
+ import { S3Client } from '@bradenmacdonald/s3-lite-client'
10
10
  import ky, { HTTPError } from 'ky'
11
+ import { confirm as confirmC, intro, log, outro, spinner as spinnerC } from '@clack/prompts'
11
12
  import { encryptSource } from '../api/crypto'
12
- import { type OptionsBase, OrganizationPerm, baseKeyPub, checkChecksum, checkCompatibility, checkPlanValid, convertAppName, createSupabaseClient, deletedFailedVersion, findSavedKey, formatError, getConfig, getLocalConfig, getLocalDepenencies, getOrganizationId, getPMAndCommand, hasOrganizationPerm, regexSemver, updateOrCreateChannel, updateOrCreateVersion, uploadMultipart, uploadUrl, useLogSnag, verifyUser, zipFile } from '../utils'
13
+ import { type OptionsBase, OrganizationPerm, baseKeyPub, checkChecksum, checkCompatibility, checkPlanValid, convertAppName, createSupabaseClient, deletedFailedVersion, findSavedKey, formatError, getConfig, getLocalConfig, getLocalDepenencies, getOrganizationId, getPMAndCommand, hasOrganizationPerm, readPackageJson, regexSemver, updateOrCreateChannel, updateOrCreateVersion, uploadMultipart, uploadUrl, useLogSnag, verifyUser, zipFile } from '../utils'
13
14
  import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
14
15
  import { checkLatest } from '../api/update'
16
+ import type { CapacitorConfig } from '../config'
15
17
  import { checkIndexPosition, searchInDirectory } from './check'
16
18
 
17
19
  interface Options extends OptionsBase {
@@ -27,6 +29,9 @@ interface Options extends OptionsBase {
27
29
  s3Apikey?: string
28
30
  s3Apisecret?: string
29
31
  s3BucketName?: string
32
+ s3Port?: number
33
+ s3SSL?: boolean
34
+ s3Endpoint?: string
30
35
  bundleUrl?: boolean
31
36
  codeCheck?: boolean
32
37
  minUpdateVersion?: string
@@ -40,20 +45,20 @@ interface Options extends OptionsBase {
40
45
  const alertMb = 20
41
46
  const UPLOAD_TIMEOUT = 120000
42
47
 
43
- type ConfigType = Awaited<ReturnType<typeof getConfig>>
44
48
  type SupabaseType = Awaited<ReturnType<typeof createSupabaseClient>>
45
49
  type pmType = ReturnType<typeof getPMAndCommand>
46
50
  type localConfigType = Awaited<ReturnType<typeof getLocalConfig>>
47
51
 
48
- function getBundle(config: ConfigType, options: Options) {
52
+ async function getBundle(config: CapacitorConfig, options: Options) {
53
+ const pkg = await readPackageJson()
49
54
  // create bundle name format : 1.0.0-beta.x where x is a uuid
50
55
  const bundle = options.bundle
51
- || config?.app?.extConfig?.plugins?.CapacitorUpdater?.version
52
- || config?.app?.package?.version
56
+ || config?.plugins?.CapacitorUpdater?.version
57
+ || pkg?.version
53
58
  || `0.0.1-beta.${randomUUID().split('-')[0]}`
54
59
 
55
60
  if (!regexSemver.test(bundle)) {
56
- p.log.error(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`)
61
+ log.error(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`)
57
62
  program.error('')
58
63
  }
59
64
 
@@ -63,24 +68,24 @@ function getBundle(config: ConfigType, options: Options) {
63
68
  function getApikey(options: Options) {
64
69
  const apikey = options.apikey || findSavedKey()
65
70
  if (!apikey) {
66
- p.log.error(`Missing API key, you need to provide a API key to upload your bundle`)
71
+ log.error(`Missing API key, you need to provide a API key to upload your bundle`)
67
72
  program.error('')
68
73
  }
69
74
 
70
75
  return apikey
71
76
  }
72
77
 
73
- function getAppIdAndPath(appId: string | undefined, options: Options, config: ConfigType) {
74
- const finalAppId = appId || config?.app?.appId
75
- const path = options.path || config?.app?.webDir
78
+ function getAppIdAndPath(appId: string | undefined, options: Options, config: CapacitorConfig) {
79
+ const finalAppId = appId || config?.appId
80
+ const path = options.path || config?.webDir
76
81
 
77
82
  if (!finalAppId || !path) {
78
- p.log.error('Missing argument, you need to provide a appid and a path (--path), or be in a capacitor project')
83
+ log.error('Missing argument, you need to provide a appid and a path (--path), or be in a capacitor project')
79
84
  program.error('')
80
85
  }
81
86
 
82
87
  if (!existsSync(path)) {
83
- p.log.error(`Path ${path} does not exist, build your app first, or provide a valid path`)
88
+ log.error(`Path ${path} does not exist, build your app first, or provide a valid path`)
84
89
  program.error('')
85
90
  }
86
91
 
@@ -93,12 +98,12 @@ function checkNotifyAppReady(options: Options, path: string) {
93
98
  if (typeof checkNotifyAppReady === 'undefined' || checkNotifyAppReady) {
94
99
  const isPluginConfigured = searchInDirectory(path, 'notifyAppReady')
95
100
  if (!isPluginConfigured) {
96
- p.log.error(`notifyAppReady() is missing in the source code. see: https://capgo.app/docs/plugin/api/#notifyappready`)
101
+ log.error(`notifyAppReady() is missing in the source code. see: https://capgo.app/docs/plugin/api/#notifyappready`)
97
102
  program.error('')
98
103
  }
99
104
  const foundIndex = checkIndexPosition(path)
100
105
  if (!foundIndex) {
101
- p.log.error(`index.html is missing in the root folder or in the only folder in the root folder`)
106
+ log.error(`index.html is missing in the root folder or in the only folder in the root folder`)
102
107
  program.error('')
103
108
  }
104
109
  }
@@ -125,7 +130,7 @@ async function verifyCompatibility(supabase: SupabaseType, pm: pmType, options:
125
130
 
126
131
  // We only check compatibility IF the channel exists
127
132
  if (!channelError && channelData && channelData.version && (channelData.version as any).native_packages && !ignoreMetadataCheck) {
128
- const spinner = p.spinner()
133
+ const spinner = spinnerC()
129
134
  spinner.start(`Checking bundle compatibility with channel ${channel}`)
130
135
  const {
131
136
  finalCompatibility: finalCompatibilityWithChannel,
@@ -137,18 +142,18 @@ async function verifyCompatibility(supabase: SupabaseType, pm: pmType, options:
137
142
 
138
143
  if (finalCompatibility.find(x => x.localVersion !== x.remoteVersion)) {
139
144
  spinner.stop(`Bundle NOT compatible with ${channel} channel`)
140
- p.log.warn(`You can check compatibility with "${pm.runner} @capgo/cli bundle compatibility"`)
145
+ log.warn(`You can check compatibility with "${pm.runner} @capgo/cli bundle compatibility"`)
141
146
 
142
147
  if (autoMinUpdateVersion) {
143
148
  minUpdateVersion = bundle
144
- p.log.info(`Auto set min-update-version to ${minUpdateVersion}`)
149
+ log.info(`Auto set min-update-version to ${minUpdateVersion}`)
145
150
  }
146
151
  }
147
152
  else if (autoMinUpdateVersion) {
148
153
  try {
149
154
  const { minUpdateVersion: lastMinUpdateVersion } = channelData.version as any
150
155
  if (!lastMinUpdateVersion || !regexSemver.test(lastMinUpdateVersion)) {
151
- p.log.error('Invalid remote min update version, skipping auto setting compatibility')
156
+ log.error('Invalid remote min update version, skipping auto setting compatibility')
152
157
  program.error('')
153
158
  }
154
159
 
@@ -156,7 +161,7 @@ async function verifyCompatibility(supabase: SupabaseType, pm: pmType, options:
156
161
  spinner.stop(`Auto set min-update-version to ${minUpdateVersion}`)
157
162
  }
158
163
  catch (error) {
159
- p.log.error(`Cannot auto set compatibility, invalid data ${channelData}`)
164
+ log.error(`Cannot auto set compatibility, invalid data ${channelData}`)
160
165
  program.error('')
161
166
  }
162
167
  }
@@ -165,23 +170,23 @@ async function verifyCompatibility(supabase: SupabaseType, pm: pmType, options:
165
170
  }
166
171
  }
167
172
  else if (!ignoreMetadataCheck) {
168
- p.log.warn(`Channel ${channel} is new or it's your first upload with compatibility check, it will be ignored this time`)
173
+ log.warn(`Channel ${channel} is new or it's your first upload with compatibility check, it will be ignored this time`)
169
174
  localDependencies = await getLocalDepenencies()
170
175
 
171
176
  if (autoMinUpdateVersion) {
172
177
  minUpdateVersion = bundle
173
- p.log.info(`Auto set min-update-version to ${minUpdateVersion}`)
178
+ log.info(`Auto set min-update-version to ${minUpdateVersion}`)
174
179
  }
175
180
  }
176
181
 
177
182
  if (updateMetadataRequired && !minUpdateVersion && !ignoreMetadataCheck) {
178
- p.log.error(`You need to provide a min-update-version to upload a bundle to this channel`)
183
+ log.error(`You need to provide a min-update-version to upload a bundle to this channel`)
179
184
  program.error('')
180
185
  }
181
186
 
182
187
  if (minUpdateVersion) {
183
188
  if (!regexSemver.test(minUpdateVersion)) {
184
- p.log.error(`Your minimal version update ${minUpdateVersion}, is not valid it should follow semver convention : https://semver.org/`)
189
+ log.error(`Your minimal version update ${minUpdateVersion}, is not valid it should follow semver convention : https://semver.org/`)
185
190
  program.error('')
186
191
  }
187
192
  }
@@ -203,8 +208,8 @@ async function checkTrial(supabase: SupabaseType, orgId: string, localConfig: lo
203
208
  .single()
204
209
  if ((isTrial && isTrial > 0) || isTrialsError) {
205
210
  // TODO: Come back to this to fix for orgs v3
206
- p.log.warn(`WARNING !!\nTrial expires in ${isTrial} days`)
207
- p.log.warn(`Upgrade here: ${localConfig.hostWeb}/dashboard/settings/plans?oid=${orgId}`)
211
+ log.warn(`WARNING !!\nTrial expires in ${isTrial} days`)
212
+ log.warn(`Upgrade here: ${localConfig.hostWeb}/dashboard/settings/plans?oid=${orgId}`)
208
213
  }
209
214
  }
210
215
 
@@ -217,7 +222,7 @@ async function checkVersionExists(supabase: SupabaseType, appid: string, bundle:
217
222
  .single()
218
223
 
219
224
  if (appVersion || appVersionError) {
220
- p.log.error(`Version already exists ${formatError(appVersionError)}`)
225
+ log.error(`Version already exists ${formatError(appVersionError)}`)
221
226
  program.error('')
222
227
  }
223
228
  }
@@ -229,28 +234,28 @@ async function prepareBundleFile(path: string, options: Options, localConfig: lo
229
234
  const key = options.key
230
235
 
231
236
  zipped = await zipFile(path)
232
- const s = p.spinner()
237
+ const s = spinnerC()
233
238
  s.start(`Calculating checksum`)
234
239
  checksum = await getChecksum(zipped, 'crc32')
235
240
  s.stop(`Checksum: ${checksum}`)
236
241
  // key should be undefined or a string if false it should ingore encryption
237
242
  if (!key) {
238
- p.log.info(`Encryption ignored`)
243
+ log.info(`Encryption ignored`)
239
244
  }
240
245
  else if (key || existsSync(baseKeyPub)) {
241
246
  const publicKey = typeof key === 'string' ? key : baseKeyPub
242
247
  let keyData = options.keyData || ''
243
248
  // check if publicKey exist
244
249
  if (!keyData && !existsSync(publicKey)) {
245
- p.log.error(`Cannot find public key ${publicKey}`)
250
+ log.error(`Cannot find public key ${publicKey}`)
246
251
  if (ciDetect.isCI) {
247
- p.log.error('Cannot ask if user wants to use capgo public key on the cli')
252
+ log.error('Cannot ask if user wants to use capgo public key on the cli')
248
253
  program.error('')
249
254
  }
250
255
 
251
- const res = await p.confirm({ message: 'Do you want to use our public key ?' })
256
+ const res = await confirmC({ message: 'Do you want to use our public key ?' })
252
257
  if (!res) {
253
- p.log.error(`Error: Missing public key`)
258
+ log.error(`Error: Missing public key`)
254
259
  program.error('')
255
260
  }
256
261
  keyData = localConfig.signKey || ''
@@ -271,11 +276,11 @@ async function prepareBundleFile(path: string, options: Options, localConfig: lo
271
276
  keyData = keyFile.toString()
272
277
  }
273
278
  // encrypt
274
- p.log.info(`Encrypting your bundle`)
279
+ log.info(`Encrypting your bundle`)
275
280
  const res = encryptSource(zipped, keyData)
276
281
  sessionKey = res.ivSessionKey
277
282
  if (options.displayIvSession) {
278
- p.log.info(`Your Iv Session key is ${sessionKey},
283
+ log.info(`Your Iv Session key is ${sessionKey},
279
284
  keep it safe, you will need it to decrypt your bundle.
280
285
  It will be also visible in your dashboard\n`)
281
286
  }
@@ -283,8 +288,8 @@ It will be also visible in your dashboard\n`)
283
288
  }
284
289
  const mbSize = Math.floor((zipped?.byteLength ?? 0) / 1024 / 1024)
285
290
  if (mbSize > alertMb) {
286
- p.log.warn(`WARNING !!\nThe app size is ${mbSize} Mb, this may take a while to download for users\n`)
287
- p.log.info(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`)
291
+ log.warn(`WARNING !!\nThe app size is ${mbSize} Mb, this may take a while to download for users\n`)
292
+ log.info(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`)
288
293
  await snag.track({
289
294
  channel: 'app-error',
290
295
  event: 'App Too Large',
@@ -301,19 +306,19 @@ It will be also visible in your dashboard\n`)
301
306
  }
302
307
 
303
308
  async function uploadBundleToCapgoCloud(supabase: SupabaseType, appid: string, bundle: string, orgId: string, zipped: Buffer, options: Options) {
304
- const spinner = p.spinner()
309
+ const spinner = spinnerC()
305
310
  spinner.start(`Uploading Bundle`)
306
311
  const startTime = performance.now()
307
312
 
308
313
  try {
309
314
  if (options.multipart !== undefined && options.multipart) {
310
- p.log.info(`Uploading bundle as multipart`)
315
+ log.info(`Uploading bundle as multipart`)
311
316
  await uploadMultipart(supabase, appid, bundle, zipped, orgId)
312
317
  }
313
318
  else {
314
319
  const url = await uploadUrl(supabase, appid, bundle)
315
320
  if (!url) {
316
- p.log.error(`Cannot get upload url`)
321
+ log.error(`Cannot get upload url`)
317
322
  program.error('')
318
323
  }
319
324
  await ky.put(url, {
@@ -327,10 +332,10 @@ async function uploadBundleToCapgoCloud(supabase: SupabaseType, appid: string, b
327
332
  const endTime = performance.now()
328
333
  const uploadTime = ((endTime - startTime) / 1000).toFixed(2)
329
334
  spinner.stop(`Failed to upload bundle ( after ${uploadTime} seconds)`)
330
- p.log.error(`Cannot upload bundle ( try again with --multipart option) ${formatError(errorUpload)}`)
335
+ log.error(`Cannot upload bundle ( try again with --multipart option) ${formatError(errorUpload)}`)
331
336
  if (errorUpload instanceof HTTPError) {
332
337
  const body = await errorUpload.response.text()
333
- p.log.error(`Response: ${formatError(body)}`)
338
+ log.error(`Response: ${formatError(body)}`)
334
339
  }
335
340
  // call delete version on path /delete_failed_version to delete the version
336
341
  await deletedFailedVersion(supabase, appid, bundle)
@@ -367,68 +372,46 @@ async function setVersionInChannel(
367
372
  owner_org: orgId,
368
373
  })
369
374
  if (dbError3) {
370
- p.log.error(`Cannot set channel, the upload key is not allowed to do that, use the "all" for this. ${formatError(dbError3)}`)
375
+ log.error(`Cannot set channel, the upload key is not allowed to do that, use the "all" for this. ${formatError(dbError3)}`)
371
376
  program.error('')
372
377
  }
373
378
  const appidWeb = convertAppName(appid)
374
379
  const bundleUrl = `${localConfig.hostWeb}/app/p/${appidWeb}/channel/${data.id}`
375
380
  if (data?.public)
376
- p.log.info('Your update is now available in your public channel 🎉')
381
+ log.info('Your update is now available in your public channel 🎉')
377
382
  else if (data?.id)
378
- p.log.info(`Link device to this bundle to try it: ${bundleUrl}`)
383
+ log.info(`Link device to this bundle to try it: ${bundleUrl}`)
379
384
 
380
385
  if (displayBundleUrl) {
381
- p.log.info(`Bundle url: ${bundleUrl}`)
386
+ log.info(`Bundle url: ${bundleUrl}`)
382
387
  }
383
388
  else if (!versionId) {
384
- p.log.warn('Cannot set bundle with upload key, use key with more rights for that')
389
+ log.warn('Cannot set bundle with upload key, use key with more rights for that')
385
390
  program.error('')
386
391
  }
387
392
  else if (!hasOrganizationPerm(permissions, OrganizationPerm.write)) {
388
- p.log.warn('Cannot set channel as a upload organization member')
393
+ log.warn('Cannot set channel as a upload organization member')
389
394
  }
390
395
  }
391
396
  }
392
397
 
393
398
  export async function uploadBundle(preAppid: string, options: Options, shouldExit = true) {
394
- p.intro(`Uploading`)
399
+ intro(`Uploading`)
395
400
  const pm = getPMAndCommand()
396
401
  await checkLatest()
397
402
 
398
- const { s3Region, s3Apikey, s3Apisecret, s3BucketName } = options
399
-
400
- if (s3BucketName || s3Region || s3Apikey || s3Apisecret) {
401
- if (!s3BucketName || !s3Region || !s3Apikey || !s3Apisecret) {
402
- p.log.error('Missing argument, for S3 upload you need to provide a bucket name, region, API key, and API secret')
403
- program.error('')
404
- }
405
- }
406
-
407
- if (s3Region && s3Apikey && s3Apisecret && s3BucketName) {
408
- p.log.info('Uploading to S3')
409
- // const s3Client = new S3Client({
410
- // region: s3Region,
411
- // credentials: {
412
- // accessKeyId: s3Apikey,
413
- // secretAccessKey: s3Apisecret,
414
- // },
415
- // })
416
- p.log.error('S3 upload is not available we have currenly an issue with it')
417
- program.error('')
418
- // todo: figure out s3 upload
419
- return
420
- }
403
+ const { s3Region, s3Apikey, s3Apisecret, s3BucketName, s3Endpoint, s3Port, s3SSL } = options
421
404
 
422
405
  const apikey = getApikey(options)
423
- const config = await getConfig()
424
- const { appid, path } = getAppIdAndPath(preAppid, options, config)
425
- const bundle = getBundle(config, options)
406
+ const extConfig = await getConfig()
407
+ const { appid, path } = getAppIdAndPath(preAppid, options, extConfig.config)
408
+ const bundle = await getBundle(extConfig.config, options)
426
409
  const channel = options.channel || 'dev'
427
410
  const snag = useLogSnag()
428
411
 
429
412
  checkNotifyAppReady(options, path)
430
413
 
431
- p.log.info(`Upload ${appid}@${bundle} started from path "${path}" to Capgo cloud`)
414
+ log.info(`Upload ${appid}@${bundle} started from path "${path}" to Capgo cloud`)
432
415
 
433
416
  const localConfig = await getLocalConfig()
434
417
  const supabase = await createSupabaseClient(apikey)
@@ -444,7 +427,7 @@ export async function uploadBundle(preAppid: string, options: Options, shouldExi
444
427
  await checkVersionExists(supabase, appid, bundle)
445
428
 
446
429
  if (options.external && !options.external.startsWith('https://')) {
447
- p.log.error(`External link should should start with "https://" current is "${external}"`)
430
+ log.error(`External link should should start with "https://" current is "${external}"`)
448
431
  program.error('')
449
432
  }
450
433
 
@@ -488,17 +471,41 @@ export async function uploadBundle(preAppid: string, options: Options, shouldExi
488
471
 
489
472
  const { error: dbError } = await updateOrCreateVersion(supabase, versionData)
490
473
  if (dbError) {
491
- p.log.error(`Cannot add bundle ${formatError(dbError)}`)
474
+ log.error(`Cannot add bundle ${formatError(dbError)}`)
492
475
  program.error('')
493
476
  }
494
477
 
495
- if (zipped) {
478
+ if (zipped && (s3BucketName || s3Endpoint || s3Region || s3Apikey || s3Apisecret || s3Port || s3SSL)) {
479
+ if (!s3BucketName || !s3Endpoint || !s3Region || !s3Apikey || !s3Apisecret || !s3Port) {
480
+ log.error('Missing argument, for S3 upload you need to provide a bucket name, endpoint, region, port, API key, and API secret')
481
+ program.error('')
482
+ }
483
+
484
+ log.info('Uploading to S3')
485
+ const s3Client = new S3Client({
486
+ endPoint: s3Endpoint,
487
+ region: s3Region,
488
+ port: s3Port,
489
+ useSSL: s3SSL,
490
+ bucket: s3BucketName,
491
+ credentials: {
492
+ accessKeyId: s3Apikey,
493
+ secretAccessKey: s3Apisecret,
494
+ },
495
+ })
496
+ const fileName = `${appid}-${bundle}`
497
+ const encodeFileName = encodeURIComponent(fileName)
498
+ await s3Client.putObject(fileName, zipped)
499
+ versionData.external_url = `https://${s3Endpoint}/${encodeFileName}`
500
+ versionData.storage_provider = 'external'
501
+ }
502
+ else if (zipped) {
496
503
  await uploadBundleToCapgoCloud(supabase, appid, bundle, orgId, zipped, options)
497
504
 
498
505
  versionData.storage_provider = 'r2'
499
506
  const { error: dbError2 } = await updateOrCreateVersion(supabase, versionData)
500
507
  if (dbError2) {
501
- p.log.error(`Cannot update bundle ${formatError(dbError2)}`)
508
+ log.error(`Cannot update bundle ${formatError(dbError2)}`)
502
509
  program.error('')
503
510
  }
504
511
  }
@@ -516,8 +523,8 @@ export async function uploadBundle(preAppid: string, options: Options, shouldExi
516
523
  notify: false,
517
524
  }).catch()
518
525
  if (shouldExit) {
519
- p.outro('Time to share your update to the world 🌍')
520
- process.exit()
526
+ outro('Time to share your update to the world 🌍')
527
+ exit()
521
528
  }
522
529
  return true
523
530
  }
@@ -527,19 +534,19 @@ export async function uploadCommand(appid: string, options: Options) {
527
534
  await uploadBundle(appid, options, true)
528
535
  }
529
536
  catch (error) {
530
- p.log.error(formatError(error))
537
+ log.error(formatError(error))
531
538
  program.error('')
532
539
  }
533
540
  }
534
541
 
535
542
  export async function uploadDeprecatedCommand(appid: string, options: Options) {
536
543
  const pm = getPMAndCommand()
537
- p.log.warn(`⚠️ This command is deprecated, use "${pm.runner} @capgo/cli bundle upload" instead ⚠️`)
544
+ log.warn(`⚠️ This command is deprecated, use "${pm.runner} @capgo/cli bundle upload" instead ⚠️`)
538
545
  try {
539
546
  await uploadBundle(appid, options, true)
540
547
  }
541
548
  catch (error) {
542
- p.log.error(formatError(error))
549
+ log.error(formatError(error))
543
550
  program.error('')
544
551
  }
545
552
  }
package/src/bundle/zip.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { randomUUID } from 'node:crypto'
2
2
  import { writeFileSync } from 'node:fs'
3
- import process from 'node:process'
3
+ import { exit } from 'node:process'
4
4
  import { program } from 'commander'
5
- import * as p from '@clack/prompts'
6
5
  import { checksum as getChecksum } from '@tomasklaen/checksum'
6
+ import { intro, log, outro, spinner } from '@clack/prompts'
7
7
  import { checkLatest } from '../api/update'
8
8
  import type {
9
9
  OptionsBase,
@@ -11,6 +11,7 @@ import type {
11
11
  import {
12
12
  formatError,
13
13
  getConfig,
14
+ readPackageJson,
14
15
  regexSemver,
15
16
  useLogSnag,
16
17
  zipFile,
@@ -35,37 +36,38 @@ export async function zipBundle(appId: string, options: Options) {
35
36
  if (!json)
36
37
  await checkLatest()
37
38
 
38
- const config = await getConfig()
39
- appId = appId || config?.app?.appId
39
+ const extConfig = await getConfig()
40
+ appId = appId || extConfig?.config?.appId
40
41
  // create bundle name format : 1.0.0-beta.x where x is a uuid
41
42
  const uuid = randomUUID().split('-')[0]
42
- bundle = bundle || config?.app?.package?.version || `0.0.1-beta.${uuid}`
43
+ const pack = await readPackageJson()
44
+ bundle = bundle || pack?.version || `0.0.1-beta.${uuid}`
43
45
  if (!json)
44
- p.intro(`Zipping ${appId}@${bundle}`)
46
+ intro(`Zipping ${appId}@${bundle}`)
45
47
  // check if bundle is valid
46
- if (!regexSemver.test(bundle)) {
48
+ if (bundle && !regexSemver.test(bundle)) {
47
49
  if (!json)
48
- p.log.error(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`)
50
+ log.error(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`)
49
51
  else
50
52
  console.error(formatError({ error: 'invalid_semver' }))
51
53
  program.error('')
52
54
  }
53
- path = path || config?.app?.webDir
55
+ path = path || extConfig?.config?.webDir
54
56
  if (!appId || !bundle || !path) {
55
57
  if (!json)
56
- p.log.error('Missing argument, you need to provide a appId and a bundle and a path, or be in a capacitor project')
58
+ log.error('Missing argument, you need to provide a appId and a bundle and a path, or be in a capacitor project')
57
59
  else
58
60
  console.error(formatError({ error: 'missing_argument' }))
59
61
  program.error('')
60
62
  }
61
63
  if (!json)
62
- p.log.info(`Started from path "${path}"`)
64
+ log.info(`Started from path "${path}"`)
63
65
  const checkNotifyAppReady = options.codeCheck
64
66
  if (typeof checkNotifyAppReady === 'undefined' || checkNotifyAppReady) {
65
67
  const isPluginConfigured = searchInDirectory(path, 'notifyAppReady')
66
68
  if (!isPluginConfigured) {
67
69
  if (!json)
68
- p.log.error(`notifyAppReady() is missing in the source code. see: https://capgo.app/docs/plugin/api/#notifyappready`)
70
+ log.error(`notifyAppReady() is missing in the source code. see: https://capgo.app/docs/plugin/api/#notifyappready`)
69
71
  else
70
72
  console.error(formatError({ error: 'notifyAppReady_not_in_source_code' }))
71
73
  program.error('')
@@ -73,7 +75,7 @@ export async function zipBundle(appId: string, options: Options) {
73
75
  const foundIndex = checkIndexPosition(path)
74
76
  if (!foundIndex) {
75
77
  if (!json)
76
- p.log.error(`index.html is missing in the root folder or in the only folder in the root folder`)
78
+ log.error(`index.html is missing in the root folder or in the only folder in the root folder`)
77
79
  else
78
80
  console.error(formatError({ error: 'index_html_not_found' }))
79
81
  program.error('')
@@ -81,8 +83,8 @@ export async function zipBundle(appId: string, options: Options) {
81
83
  }
82
84
  const zipped = await zipFile(path)
83
85
  if (!json)
84
- p.log.info(`Zipped ${zipped.byteLength} bytes`)
85
- const s = p.spinner()
86
+ log.info(`Zipped ${zipped.byteLength} bytes`)
87
+ const s = spinner()
86
88
  if (!json)
87
89
  s.start(`Calculating checksum`)
88
90
  const checksum = await getChecksum(zipped, 'crc32')
@@ -91,8 +93,8 @@ export async function zipBundle(appId: string, options: Options) {
91
93
  const mbSize = Math.floor(zipped.byteLength / 1024 / 1024)
92
94
  // We do not issue this warning for json
93
95
  if (mbSize > alertMb && !json) {
94
- p.log.warn(`WARNING !!\nThe app size is ${mbSize} Mb, this may take a while to download for users\n`)
95
- p.log.warn(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`)
96
+ log.warn(`WARNING !!\nThe app size is ${mbSize} Mb, this may take a while to download for users\n`)
97
+ log.warn(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`)
96
98
  await snag.track({
97
99
  channel: 'app-error',
98
100
  event: 'App Too Large',
@@ -103,7 +105,7 @@ export async function zipBundle(appId: string, options: Options) {
103
105
  notify: false,
104
106
  }).catch()
105
107
  }
106
- const s2 = p.spinner()
108
+ const s2 = spinner()
107
109
  const name = options.name || `${appId}_${bundle}.zip`
108
110
  if (!json)
109
111
  s2.start(`Saving to ${name}`)
@@ -122,7 +124,7 @@ export async function zipBundle(appId: string, options: Options) {
122
124
  }).catch()
123
125
 
124
126
  if (!json)
125
- p.outro(`Done ✅`)
127
+ outro(`Done ✅`)
126
128
 
127
129
  if (json) {
128
130
  const output = {
@@ -134,10 +136,10 @@ export async function zipBundle(appId: string, options: Options) {
134
136
  // eslint-disable-next-line no-console
135
137
  console.log(JSON.stringify(output, null, 2))
136
138
  }
137
- process.exit()
139
+ exit()
138
140
  }
139
141
  catch (error) {
140
- p.log.error(formatError(error))
142
+ log.error(formatError(error))
141
143
  program.error('')
142
144
  }
143
145
  }
@@ -1,6 +1,6 @@
1
- import process from 'node:process'
1
+ import { exit } from 'node:process'
2
2
  import { program } from 'commander'
3
- import * as p from '@clack/prompts'
3
+ import { intro, log, outro } from '@clack/prompts'
4
4
  import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
5
5
  import { createChannel, findUnknownVersion } from '../api/channels'
6
6
  import type { OptionsBase } from '../utils'
@@ -11,18 +11,18 @@ interface Options extends OptionsBase {
11
11
  }
12
12
 
13
13
  export async function addChannel(channelId: string, appId: string, options: Options, shouldExit = true) {
14
- p.intro(`Create channel`)
14
+ intro(`Create channel`)
15
15
  options.apikey = options.apikey || findSavedKey()
16
- const config = await getConfig()
17
- appId = appId || config?.app?.appId
16
+ const extConfig = await getConfig()
17
+ appId = appId || extConfig?.config?.appId
18
18
  const snag = useLogSnag()
19
19
 
20
20
  if (!options.apikey) {
21
- p.log.error('Missing API key, you need to provide a API key to upload your bundle')
21
+ log.error('Missing API key, you need to provide a API key to upload your bundle')
22
22
  program.error('')
23
23
  }
24
24
  if (!appId) {
25
- p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
25
+ log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
26
26
  program.error('')
27
27
  }
28
28
  const supabase = await createSupabaseClient(options.apikey)
@@ -31,12 +31,12 @@ export async function addChannel(channelId: string, appId: string, options: Opti
31
31
  // Check we have app access to this appId
32
32
  await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.admin)
33
33
 
34
- p.log.info(`Creating channel ${appId}#${channelId} to Capgo`)
34
+ log.info(`Creating channel ${appId}#${channelId} to Capgo`)
35
35
  try {
36
36
  const data = await findUnknownVersion(supabase, appId)
37
37
  const orgId = await getOrganizationId(supabase, appId)
38
38
  if (!data) {
39
- p.log.error(`Cannot find default version for channel creation, please contact Capgo support 🤨`)
39
+ log.error(`Cannot find default version for channel creation, please contact Capgo support 🤨`)
40
40
  program.error('')
41
41
  }
42
42
  const res = await createChannel(supabase, {
@@ -47,11 +47,11 @@ export async function addChannel(channelId: string, appId: string, options: Opti
47
47
  })
48
48
 
49
49
  if (res.error) {
50
- p.log.error(`Cannot create Channel 🙀\n${formatError(res.error)}`)
50
+ log.error(`Cannot create Channel 🙀\n${formatError(res.error)}`)
51
51
  program.error('')
52
52
  }
53
53
 
54
- p.log.success(`Channel created ✅`)
54
+ log.success(`Channel created ✅`)
55
55
  await snag.track({
56
56
  channel: 'channel',
57
57
  event: 'Create channel',
@@ -65,12 +65,12 @@ export async function addChannel(channelId: string, appId: string, options: Opti
65
65
  }).catch()
66
66
  }
67
67
  catch (error) {
68
- p.log.error(`Cannot create Channel 🙀`)
68
+ log.error(`Cannot create Channel 🙀`)
69
69
  return false
70
70
  }
71
71
  if (shouldExit) {
72
- p.outro(`Done ✅`)
73
- process.exit()
72
+ outro(`Done ✅`)
73
+ exit()
74
74
  }
75
75
  return true
76
76
  }