@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/CHANGELOG.md +11 -0
- package/README.md +4 -0
- package/bun.lockb +0 -0
- package/dist/index.js +31308 -712
- package/package.json +2 -1
- package/src/app/add.ts +2 -2
- package/src/bundle/unlink.ts +7 -2
- package/src/bundle/upload.ts +75 -4
- package/src/channel/set.ts +3 -1
- package/src/index.ts +4 -0
- package/src/types/supabase.types.ts +381 -361
- package/src/utils.ts +27 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/cli",
|
|
3
|
-
"version": "4.2
|
|
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'
|
package/src/bundle/unlink.ts
CHANGED
|
@@ -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
|
|
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,
|
|
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)
|
package/src/bundle/upload.ts
CHANGED
|
@@ -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
|
-
|
|
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('
|
|
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()
|
package/src/channel/set.ts
CHANGED
|
@@ -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,
|
|
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')
|