@capgo/cli 4.2.12 → 4.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/cli",
3
- "version": "4.2.12",
3
+ "version": "4.3.2",
4
4
  "description": "A CLI to upload to capgo servers",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -43,6 +43,7 @@
43
43
  "author": "github.com/riderx",
44
44
  "license": "Apache 2.0",
45
45
  "dependencies": {
46
+ "@aws-sdk/client-s3": "^3.540.0",
46
47
  "@capacitor/cli": "5.7.0",
47
48
  "@capgo/find-package-manager": "0.0.10",
48
49
  "@clack/prompts": "^0.7.0",
package/src/app/add.ts CHANGED
@@ -39,8 +39,6 @@ export async function addApp(appId: string, options: Options, throwErr = true) {
39
39
 
40
40
  let userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
41
41
 
42
- await checkPlanValid(supabase, userId, options.apikey, undefined, false)
43
-
44
42
  // Check we have app access to this appId
45
43
  const appExist = await checkAppExists(supabase, appId)
46
44
  if (throwErr && appExist) {
@@ -82,6 +80,8 @@ export async function addApp(appId: string, options: Options, throwErr = true) {
82
80
 
83
81
  p.log.info(`Using the organization "${organization.name}" as the app owner`)
84
82
 
83
+ await checkPlanValid(supabase, organizationUid, options.apikey, undefined, false)
84
+
85
85
  let { name, icon } = options
86
86
  appId = appId || config?.app?.appId
87
87
  name = name || config?.app?.appName || 'Unknown'
@@ -15,6 +15,7 @@ import {
15
15
  findSavedKey,
16
16
  formatError,
17
17
  getConfig,
18
+ getOrganizationId,
18
19
  useLogSnag,
19
20
  verifyUser,
20
21
  } from '../utils'
@@ -47,7 +48,11 @@ export async function unlinkDevice(channel: string, appId: string, options: Opti
47
48
  }
48
49
  const supabase = await createSupabaseClient(options.apikey)
49
50
 
50
- const userId = await verifyUser(supabase, options.apikey, ['all', 'write'])
51
+ const [userId, orgId] = await Promise.all([
52
+ verifyUser(supabase, options.apikey, ['all', 'write']),
53
+ getOrganizationId(supabase, appId),
54
+ ])
55
+
51
56
  // Check we have app access to this appId
52
57
  await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.write)
53
58
 
@@ -56,7 +61,7 @@ export async function unlinkDevice(channel: string, appId: string, options: Opti
56
61
  program.error('')
57
62
  }
58
63
  try {
59
- await checkPlanValid(supabase, userId, options.apikey, appId)
64
+ await checkPlanValid(supabase, orgId, options.apikey, appId)
60
65
 
61
66
  const versionData = await getVersionData(supabase, appId, bundle)
62
67
  await checkVersionNotUsedInChannel(supabase, appId, versionData)
@@ -8,6 +8,10 @@ import * as p from '@clack/prompts'
8
8
  import { checksum as getChecksum } from '@tomasklaen/checksum'
9
9
  import ciDetect from 'ci-info'
10
10
  import ky from 'ky'
11
+ import {
12
+ PutObjectCommand,
13
+ S3Client,
14
+ } from '@aws-sdk/client-s3'
11
15
  import { checkLatest } from '../api/update'
12
16
  import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
13
17
  import { encryptSource } from '../api/crypto'
@@ -28,6 +32,7 @@ import {
28
32
  getConfig,
29
33
  getLocalConfig,
30
34
  getLocalDepenencies,
35
+ getOrganizationId,
31
36
  hasOrganizationPerm,
32
37
  regexSemver,
33
38
  requireUpdateMetadata,
@@ -50,6 +55,10 @@ interface Options extends OptionsBase {
50
55
  key?: boolean | string
51
56
  keyData?: string
52
57
  ivSessionKey?: string
58
+ s3Region?: string
59
+ s3Apikey?: string
60
+ s3Apisecret?: string
61
+ s3BucketName?: string
53
62
  bundleUrl?: boolean
54
63
  codeCheck?: boolean
55
64
  minUpdateVersion?: string
@@ -63,6 +72,21 @@ export async function uploadBundle(appid: string, options: Options, shouldExit =
63
72
  let { bundle, path, channel } = options
64
73
  const { external, key, displayIvSession, autoMinUpdateVersion, ignoreMetadataCheck } = options
65
74
  let { minUpdateVersion } = options
75
+ const { s3Region, s3Apikey, s3Apisecret, s3BucketName } = options
76
+ let useS3 = false
77
+ let s3Client
78
+ if (s3Region && s3Apikey && s3Apisecret && s3BucketName) {
79
+ p.log.info('Uploading to S3')
80
+ useS3 = true
81
+ s3Client = new S3Client({
82
+ region: s3Region,
83
+ credentials: {
84
+ accessKeyId: s3Apikey,
85
+ secretAccessKey: s3Apisecret,
86
+ },
87
+ })
88
+ }
89
+
66
90
  options.apikey = options.apikey || findSavedKey()
67
91
  const snag = useLogSnag()
68
92
 
@@ -91,6 +115,13 @@ export async function uploadBundle(appid: string, options: Options, shouldExit =
91
115
  p.log.error('Missing argument, you need to provide a appid and a bundle and a path, or be in a capacitor project')
92
116
  program.error('')
93
117
  }
118
+ // if one S3 variable is set, check that all are set
119
+ if (s3BucketName || s3Region || s3Apikey || s3Apisecret) {
120
+ if (!s3BucketName || !s3Region || !s3Apikey || !s3Apisecret) {
121
+ p.log.error('Missing argument, for S3 upload you need to provide a bucket name, region, API key, and API secret')
122
+ program.error('')
123
+ }
124
+ }
94
125
  // check if path exist
95
126
  if (!existsSync(path)) {
96
127
  p.log.error(`Path ${path} does not exist, build your app first, or provide a valid path`)
@@ -119,7 +150,10 @@ export async function uploadBundle(appid: string, options: Options, shouldExit =
119
150
  // await checkAppExistsAndHasPermissionErr(supabase, options.apikey, appid);
120
151
 
121
152
  const permissions = await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appid, OrganizationPerm.upload)
122
- await checkPlanValid(supabase, userId, options.apikey, appid, true)
153
+
154
+ // Now if it does exist we will fetch the org id
155
+ const orgId = await getOrganizationId(supabase, appid)
156
+ await checkPlanValid(supabase, orgId, options.apikey, appid, true)
123
157
 
124
158
  const updateMetadataRequired = await requireUpdateMetadata(supabase, channel, appid)
125
159
 
@@ -197,11 +231,12 @@ export async function uploadBundle(appid: string, options: Options, shouldExit =
197
231
  }
198
232
 
199
233
  const { data: isTrial, error: isTrialsError } = await supabase
200
- .rpc('is_trial', { userid: userId })
234
+ .rpc('is_trial_org', { orgid: orgId })
201
235
  .single()
202
236
  if ((isTrial && isTrial > 0) || isTrialsError) {
237
+ // TODO: Come back to this to fix for orgs v3
203
238
  p.log.warn(`WARNING !!\nTrial expires in ${isTrial} days`)
204
- p.log.warn(`Upgrade here: ${localConfig.hostWeb}/dashboard/settings/plans`)
239
+ p.log.warn(`Upgrade here: ${localConfig.hostWeb}/dashboard/settings/plans?oid=${orgId}`)
205
240
  }
206
241
 
207
242
  // check if app already exist
@@ -217,7 +252,7 @@ export async function uploadBundle(appid: string, options: Options, shouldExit =
217
252
  let sessionKey
218
253
  let checksum = ''
219
254
  let zipped: Buffer | null = null
220
- if (!external) {
255
+ if (!external && useS3 === false) {
221
256
  const zip = new AdmZip()
222
257
  zip.addLocalFolder(path)
223
258
  zipped = zip.toBuffer()
@@ -292,6 +327,15 @@ It will be also visible in your dashboard\n`)
292
327
  program.error('')
293
328
  }
294
329
  else {
330
+ if (useS3) {
331
+ const zip = new AdmZip()
332
+ zip.addLocalFolder(path)
333
+ zipped = zip.toBuffer()
334
+ const s = p.spinner()
335
+ s.start(`Calculating checksum`)
336
+ checksum = await getChecksum(zipped, 'crc32')
337
+ s.stop(`Checksum: ${checksum}`)
338
+ }
295
339
  await snag.track({
296
340
  channel: 'app',
297
341
  event: 'App external',
@@ -361,6 +405,33 @@ It will be also visible in your dashboard\n`)
361
405
  }
362
406
  spinner.stop('Bundle Uploaded 💪')
363
407
  }
408
+ else if (useS3 && zipped) {
409
+ const spinner = p.spinner()
410
+ spinner.start(`Uploading Bundle`)
411
+
412
+ const fileName = `${appid}-${bundle}`
413
+ const encodeFileName = encodeURIComponent(fileName)
414
+ const command: PutObjectCommand = new PutObjectCommand({
415
+ Bucket: s3BucketName,
416
+ Key: fileName,
417
+ Body: zipped,
418
+ })
419
+
420
+ const response = await s3Client.send(command)
421
+ if (response.$metadata.httpStatusCode !== 200) {
422
+ p.log.error(`Cannot upload to S3`)
423
+ program.error('')
424
+ }
425
+
426
+ versionData.storage_provider = 'external'
427
+ versionData.external_url = `https://${s3BucketName}.s3.amazonaws.com/${encodeFileName}`
428
+ const { error: dbError2 } = await updateOrCreateVersion(supabase, versionData)
429
+ if (dbError2) {
430
+ p.log.error(`Cannot update bundle ${formatError(dbError2)}`)
431
+ program.error('')
432
+ }
433
+ spinner.stop('Bundle Uploaded 💪')
434
+ }
364
435
  const { data: versionId } = await supabase
365
436
  .rpc('get_app_versions', { apikey: options.apikey, name_version: bundle, appid })
366
437
  .single()
@@ -13,6 +13,7 @@ import {
13
13
  findSavedKey,
14
14
  formatError,
15
15
  getConfig,
16
+ getOrganizationId,
16
17
  updateOrCreateChannel,
17
18
  useLogSnag,
18
19
  verifyUser,
@@ -53,6 +54,7 @@ export async function setChannel(channel: string, appId: string, options: Option
53
54
  const userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
54
55
  // Check we have app access to this appId
55
56
  await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.admin)
57
+ const orgId = await getOrganizationId(supabase, appId)
56
58
 
57
59
  const { bundle, latest, downgrade, upgrade, ios, android, selfAssign, state, disableAutoUpdate } = options
58
60
  if (!channel) {
@@ -76,7 +78,7 @@ export async function setChannel(channel: string, appId: string, options: Option
76
78
  program.error('')
77
79
  }
78
80
  try {
79
- await checkPlanValid(supabase, userId, options.apikey, appId)
81
+ await checkPlanValid(supabase, orgId, options.apikey, appId)
80
82
  const channelPayload: Database['public']['Tables']['channels']['Insert'] = {
81
83
  created_by: userId,
82
84
  app_id: appId,
package/src/index.ts CHANGED
@@ -113,6 +113,10 @@ bundle
113
113
  .option('-c, --channel <channel>', 'channel to link to')
114
114
  .option('-e, --external <url>', 'link to external url intead of upload to Capgo Cloud')
115
115
  .option('--iv-session-key <key>', 'Set the iv and session key for bundle url external')
116
+ .option('--s3-region <region>', 'Region for your AWS S3 bucket')
117
+ .option('--s3-apikey <apikey>', 'apikey for your AWS S3 account')
118
+ .option('--s3-apisecret <apisecret>', 'api secret for your AWS S3 account')
119
+ .option('--s3-bucket-name <bucketName>', 'Name for your AWS S3 bucket')
116
120
  .option('--key <key>', 'custom path for public signing key')
117
121
  .option('--key-data <keyData>', 'base64 public signing key')
118
122
  .option('--bundle-url', 'prints bundle url into stdout')