@capgo/cli 4.10.2 → 4.10.6-beta.1
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/.github/workflows/build.yml +12 -12
- package/.github/workflows/bump_version.yml +2 -2
- package/.github/workflows/check_posix_paths.yml +222 -0
- package/.github/workflows/test.yml +1 -1
- package/CHANGELOG.md +414 -0
- package/bun.lockb +0 -0
- package/dist/index.js +54258 -39666
- package/package.json +22 -17
- package/src/api/channels.ts +28 -5
- package/src/api/versions.ts +2 -3
- package/src/app/add.ts +5 -5
- package/src/app/debug.ts +39 -24
- package/src/app/delete.ts +12 -8
- package/src/app/info.ts +4 -1
- package/src/app/set.ts +5 -3
- package/src/bundle/cleanup.ts +5 -5
- package/src/bundle/delete.ts +2 -2
- package/src/bundle/list.ts +2 -2
- package/src/bundle/upload.ts +15 -24
- package/src/bundle/zip.ts +93 -89
- package/src/channel/add.ts +10 -4
- package/src/channel/delete.ts +8 -3
- package/src/channel/set.ts +1 -2
- package/src/index.ts +5 -6
- package/src/init.ts +121 -21
- package/src/key.ts +2 -2
- package/src/types/supabase.types.ts +97 -172
- package/src/utils.ts +76 -15
- package/test/VerifyZip.java +83 -0
- package/test/VerifyZip.swift +54 -0
- package/test/check-posix-paths.js +21 -0
- package/test/test_upload/app.js +3 -0
- package/test/test_upload/assets/check-posix-paths.js +21 -0
- package/test/test_upload/index.html +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capgo/cli",
|
|
3
|
-
"version": "4.10.
|
|
3
|
+
"version": "4.10.6-beta.1",
|
|
4
4
|
"description": "A CLI to upload to capgo servers",
|
|
5
5
|
"author": "github.com/riderx",
|
|
6
6
|
"license": "Apache 2.0",
|
|
@@ -40,51 +40,56 @@
|
|
|
40
40
|
"pack": "pkg",
|
|
41
41
|
"types": "npx --yes supabase gen types typescript --project-id=xvwzpoazmxkqosrdewyv > src/types/supabase.types.ts",
|
|
42
42
|
"test_rls": "ts-node ./test/test_headers_rls.ts",
|
|
43
|
-
"lint": "eslint \"src/**/*.ts\" --fix"
|
|
43
|
+
"lint": "eslint \"src/**/*.ts\" --fix",
|
|
44
|
+
"check-posix-paths": "node test/check-posix-paths.js"
|
|
44
45
|
},
|
|
45
46
|
"dependencies": {
|
|
46
|
-
"@aws-sdk/client-s3": "^3.
|
|
47
|
-
"@capacitor/cli": "6.
|
|
47
|
+
"@aws-sdk/client-s3": "^3.600.0",
|
|
48
|
+
"@capacitor/cli": "6.1.0",
|
|
48
49
|
"@capgo/find-package-manager": "^0.0.17",
|
|
49
50
|
"@clack/prompts": "^0.7.0",
|
|
50
51
|
"@manypkg/find-root": "^2.2.1",
|
|
51
|
-
"@supabase/supabase-js": "^2.43.
|
|
52
|
+
"@supabase/supabase-js": "^2.43.5",
|
|
52
53
|
"@tomasklaen/checksum": "^1.1.0",
|
|
53
54
|
"@trufflesuite/spinnies": "^0.1.1",
|
|
54
|
-
"adm-zip": "^0.5.
|
|
55
|
+
"adm-zip": "^0.5.14",
|
|
55
56
|
"ci-info": "^4.0.0",
|
|
56
|
-
"commander": "12.
|
|
57
|
-
"console-table-printer": "^2.12.
|
|
57
|
+
"commander": "12.1.0",
|
|
58
|
+
"console-table-printer": "^2.12.1",
|
|
58
59
|
"get-latest-version": "^5.1.0",
|
|
59
|
-
"
|
|
60
|
+
"is-wsl": "^3.1.0",
|
|
61
|
+
"jszip": "^3.10.1",
|
|
62
|
+
"ky": "^1.3.0",
|
|
60
63
|
"logsnag": "1.0.0",
|
|
61
64
|
"mime": "^4.0.3",
|
|
62
65
|
"node-dir": "^0.1.17",
|
|
63
66
|
"open": "^10.1.0",
|
|
64
67
|
"prettyjson": "^1.2.5",
|
|
65
68
|
"prompt-sync": "^4.2.0",
|
|
66
|
-
"semver": "^7.6.2"
|
|
69
|
+
"semver": "^7.6.2",
|
|
70
|
+
"tmp": "^0.2.3"
|
|
67
71
|
},
|
|
68
72
|
"devDependencies": {
|
|
69
|
-
"@antfu/eslint-config": "^2.
|
|
73
|
+
"@antfu/eslint-config": "^2.21.1",
|
|
70
74
|
"@types/adm-zip": "0.5.5",
|
|
71
75
|
"@types/mime": "^4.0.0",
|
|
72
|
-
"@types/node": "^20.
|
|
76
|
+
"@types/node": "^20.14.7",
|
|
73
77
|
"@types/node-dir": "^0.0.37",
|
|
74
78
|
"@types/npmcli__ci-detect": "^2.0.3",
|
|
75
79
|
"@types/prettyjson": "^0.0.33",
|
|
76
80
|
"@types/prompt-sync": "^4.2.3",
|
|
77
81
|
"@types/semver": "^7.5.8",
|
|
78
|
-
"@
|
|
79
|
-
"@typescript-eslint/
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
+
"@types/tmp": "^0.2.6",
|
|
83
|
+
"@typescript-eslint/eslint-plugin": "^7.13.0",
|
|
84
|
+
"@typescript-eslint/parser": "^7.13.1",
|
|
85
|
+
"esbuild": "^0.21.5",
|
|
86
|
+
"eslint": "9.5.0",
|
|
82
87
|
"git-format-staged": "3.1.1",
|
|
83
88
|
"husky": "^9.0.11",
|
|
84
89
|
"pkg": "5.8.1",
|
|
85
90
|
"ts-loader": "^9.5.1",
|
|
86
91
|
"ts-node": "^10.9.2",
|
|
87
92
|
"tsconfig-paths": "4.2.0",
|
|
88
|
-
"typescript": "5.
|
|
93
|
+
"typescript": "5.5.2"
|
|
89
94
|
}
|
|
90
95
|
}
|
package/src/api/channels.ts
CHANGED
|
@@ -51,7 +51,13 @@ export function findUnknownVersion(supabase: SupabaseClient<Database>, appId: st
|
|
|
51
51
|
.eq('app_id', appId)
|
|
52
52
|
.eq('name', 'unknown')
|
|
53
53
|
.throwOnError()
|
|
54
|
-
.single().then(({ data }) =>
|
|
54
|
+
.single().then(({ data, error }) => {
|
|
55
|
+
if (error) {
|
|
56
|
+
p.log.error(`Cannot call findUnknownVersion as it returned an error.\n${formatError(error)}`)
|
|
57
|
+
program.error('')
|
|
58
|
+
}
|
|
59
|
+
return data
|
|
60
|
+
})
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
export function createChannel(supabase: SupabaseClient<Database>, update: Database['public']['Tables']['channels']['Insert']) {
|
|
@@ -74,7 +80,24 @@ interface version {
|
|
|
74
80
|
id: string
|
|
75
81
|
name: string
|
|
76
82
|
}
|
|
77
|
-
|
|
83
|
+
interface Channel {
|
|
84
|
+
id: number
|
|
85
|
+
name: string
|
|
86
|
+
public: boolean
|
|
87
|
+
ios: boolean
|
|
88
|
+
android: boolean
|
|
89
|
+
disableAutoUpdate: string
|
|
90
|
+
disableAutoUpdateUnderNative: boolean
|
|
91
|
+
allow_device_self_set: boolean
|
|
92
|
+
enable_progressive_deploy: boolean
|
|
93
|
+
secondaryVersionPercentage: number
|
|
94
|
+
secondVersion?: version
|
|
95
|
+
enableAbTesting: boolean
|
|
96
|
+
allow_emulator: boolean
|
|
97
|
+
allow_dev: boolean
|
|
98
|
+
version?: version
|
|
99
|
+
}
|
|
100
|
+
export function displayChannels(data: Channel[]) {
|
|
78
101
|
const t = new Table({
|
|
79
102
|
title: 'Channels',
|
|
80
103
|
charLength: { '❌': 2, '✅': 2 },
|
|
@@ -86,8 +109,8 @@ export function displayChannels(data: (Database['public']['Tables']['channels'][
|
|
|
86
109
|
'Name': row.name,
|
|
87
110
|
...(row.version ? { Version: row.version.name } : undefined),
|
|
88
111
|
'Public': row.public ? '✅' : '❌',
|
|
89
|
-
'iOS': row.ios ? '
|
|
90
|
-
'Android': row.android ? '
|
|
112
|
+
'iOS': row.ios ? '✅' : '❌',
|
|
113
|
+
'Android': row.android ? '✅' : '❌',
|
|
91
114
|
'⬆️ limit': row.disableAutoUpdate,
|
|
92
115
|
'⬇️ under native': row.disableAutoUpdateUnderNative ? '❌' : '✅',
|
|
93
116
|
'Self assign': row.allow_device_self_set ? '✅' : '❌',
|
|
@@ -136,5 +159,5 @@ export async function getActiveChannels(supabase: SupabaseClient<Database>, appi
|
|
|
136
159
|
p.log.error(`App ${appid} not found in database`)
|
|
137
160
|
program.error('')
|
|
138
161
|
}
|
|
139
|
-
return data
|
|
162
|
+
return data as any as Channel[]
|
|
140
163
|
}
|
package/src/api/versions.ts
CHANGED
|
@@ -24,7 +24,7 @@ export async function deleteAppVersion(supabase: SupabaseClient<Database>, appid
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export async function deleteSpecificVersion(supabase: SupabaseClient<Database>, appid: string,
|
|
27
|
+
export async function deleteSpecificVersion(supabase: SupabaseClient<Database>, appid: string, bundle: string) {
|
|
28
28
|
const versionData = await getVersionData(supabase, appid, bundle)
|
|
29
29
|
await checkVersionNotUsedInChannel(supabase, appid, versionData)
|
|
30
30
|
await checkVersionNotUsedInDeviceOverride(supabase, appid, versionData)
|
|
@@ -55,12 +55,11 @@ export function displayBundles(data: (Database['public']['Tables']['app_versions
|
|
|
55
55
|
p.log.success(t.render())
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
export async function getActiveAppVersions(supabase: SupabaseClient<Database>, appid: string
|
|
58
|
+
export async function getActiveAppVersions(supabase: SupabaseClient<Database>, appid: string) {
|
|
59
59
|
const { data, error: vError } = await supabase
|
|
60
60
|
.from('app_versions')
|
|
61
61
|
.select()
|
|
62
62
|
.eq('app_id', appid)
|
|
63
|
-
// .eq('user_id', userId)
|
|
64
63
|
.eq('deleted', false)
|
|
65
64
|
.order('created_at', { ascending: false })
|
|
66
65
|
|
package/src/app/add.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto'
|
|
2
1
|
import { existsSync, readFileSync } from 'node:fs'
|
|
3
2
|
import process from 'node:process'
|
|
4
3
|
import mime from 'mime'
|
|
@@ -49,7 +48,7 @@ export async function addAppInternal(appId: string, options: Options, organizati
|
|
|
49
48
|
|
|
50
49
|
const supabase = await createSupabaseClient(options.apikey)
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
await verifyUser(supabase, options.apikey, ['write', 'all'])
|
|
53
52
|
|
|
54
53
|
// Check we have app access to this appId
|
|
55
54
|
const appExist = await checkAppExists(supabase, appId)
|
|
@@ -95,23 +94,24 @@ export async function addAppInternal(appId: string, options: Options, organizati
|
|
|
95
94
|
p.log.warn(`Cannot find app icon in any of the following locations: ${icon}, ${newIconPath}`)
|
|
96
95
|
}
|
|
97
96
|
|
|
98
|
-
const fileName = `
|
|
97
|
+
const fileName = `icon`
|
|
99
98
|
let signedURL = 'https://xvwzpoazmxkqosrdewyv.supabase.co/storage/v1/object/public/images/capgo.png'
|
|
100
99
|
|
|
101
100
|
// upload image if available
|
|
102
101
|
if (iconBuff && iconType) {
|
|
103
102
|
const { error } = await supabase.storage
|
|
104
|
-
.from(`images/${
|
|
103
|
+
.from(`images/org/${organizationUid}/${appId}`)
|
|
105
104
|
.upload(fileName, iconBuff, {
|
|
106
105
|
contentType: iconType,
|
|
107
106
|
})
|
|
108
107
|
if (error) {
|
|
108
|
+
console.error(error)
|
|
109
109
|
p.log.error(`Could not add app ${formatError(error)}`)
|
|
110
110
|
program.error('')
|
|
111
111
|
}
|
|
112
112
|
const { data: signedURLData } = await supabase
|
|
113
113
|
.storage
|
|
114
|
-
.from(`images/${
|
|
114
|
+
.from(`images/org/${organizationUid}/${appId}`)
|
|
115
115
|
.getPublicUrl(fileName)
|
|
116
116
|
signedURL = signedURLData?.publicUrl || signedURL
|
|
117
117
|
}
|
package/src/app/debug.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import process from 'node:process'
|
|
2
|
+
import ky from 'ky'
|
|
2
3
|
import * as p from '@clack/prompts'
|
|
3
|
-
import type { SupabaseClient } from '@supabase/supabase-js'
|
|
4
4
|
import { program } from 'commander'
|
|
5
5
|
import type LogSnag from 'logsnag'
|
|
6
6
|
import type { Database } from '../types/supabase.types'
|
|
@@ -46,18 +46,36 @@ interface QueryStats {
|
|
|
46
46
|
devicesId?: string[]
|
|
47
47
|
search?: string
|
|
48
48
|
order?: Order[]
|
|
49
|
-
rangeStart?:
|
|
50
|
-
rangeEnd?:
|
|
51
|
-
|
|
49
|
+
rangeStart?: string
|
|
50
|
+
rangeEnd?: string
|
|
51
|
+
limit?: number
|
|
52
52
|
}
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
interface LogData {
|
|
54
|
+
app_id: string
|
|
55
|
+
device_id: string
|
|
56
|
+
action: Database['public']['Enums']['stats_action']
|
|
57
|
+
version_id: number
|
|
58
|
+
version?: number
|
|
59
|
+
created_at: string
|
|
60
|
+
}
|
|
61
|
+
export async function getStats(apikey: string, query: QueryStats, after: string | null): Promise<LogData | null> {
|
|
55
62
|
try {
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
const defaultApiHostPreprod = 'https://api-preprod.capgo.app'
|
|
64
|
+
const dataD = await ky
|
|
65
|
+
.post(`${defaultApiHostPreprod}/private/stats`, {
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
'capgkey': apikey,
|
|
69
|
+
},
|
|
70
|
+
body: JSON.stringify(query),
|
|
71
|
+
})
|
|
72
|
+
.then(res => res.json<LogData[]>())
|
|
73
|
+
.catch((err) => {
|
|
74
|
+
console.error('Cannot get devices', err)
|
|
75
|
+
return [] as LogData[]
|
|
76
|
+
})
|
|
77
|
+
if (dataD?.length > 0 && (after === null || after !== dataD[0].created_at))
|
|
78
|
+
return dataD[0]
|
|
61
79
|
}
|
|
62
80
|
catch (error) {
|
|
63
81
|
p.log.error(`Cannot get stats ${formatError(error)}`)
|
|
@@ -65,9 +83,8 @@ export async function getStats(supabase: SupabaseClient<Database>, query: QueryS
|
|
|
65
83
|
return null
|
|
66
84
|
}
|
|
67
85
|
|
|
68
|
-
export async function waitLog(channel: string,
|
|
86
|
+
export async function waitLog(channel: string, apikey: string, appId: string, snag: LogSnag, orgId: string, deviceId?: string) {
|
|
69
87
|
let loop = true
|
|
70
|
-
let now = new Date().toISOString()
|
|
71
88
|
const appIdUrl = convertAppName(appId)
|
|
72
89
|
const config = await getLocalConfig()
|
|
73
90
|
const baseUrl = `${config.hostWeb}/app/p/${appIdUrl}`
|
|
@@ -79,14 +96,14 @@ export async function waitLog(channel: string, supabase: SupabaseClient<Database
|
|
|
79
96
|
key: 'created_at',
|
|
80
97
|
sortable: 'desc',
|
|
81
98
|
}],
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
after: now,
|
|
99
|
+
limit: 1,
|
|
100
|
+
rangeStart: new Date().toISOString(),
|
|
85
101
|
}
|
|
102
|
+
let after: string | null = null
|
|
86
103
|
while (loop) {
|
|
87
|
-
const data = await getStats(
|
|
88
|
-
// console.log('data', data)
|
|
104
|
+
const data = await getStats(apikey, query, after)
|
|
89
105
|
if (data) {
|
|
106
|
+
after = data.created_at
|
|
90
107
|
p.log.info(`Log from Device: ${data.device_id}`)
|
|
91
108
|
if (data.action === 'get') {
|
|
92
109
|
p.log.info('Update Sent your your device, wait until event download complete')
|
|
@@ -122,7 +139,7 @@ export async function waitLog(channel: string, supabase: SupabaseClient<Database
|
|
|
122
139
|
p.log.error('Your bundle is missing, please check how you build your app ')
|
|
123
140
|
}
|
|
124
141
|
else if (data.action === 'noNew') {
|
|
125
|
-
p.log.error(`Your version in ${data.
|
|
142
|
+
p.log.error(`Your version in ${data.device_id} is the same as your version uploaded, change it to see the update`)
|
|
126
143
|
}
|
|
127
144
|
else if (data.action === 'disablePlatformIos') {
|
|
128
145
|
p.log.error(`iOS is disabled in the default channel and your device is an iOS device ${baseUrl}`)
|
|
@@ -166,10 +183,8 @@ export async function waitLog(channel: string, supabase: SupabaseClient<Database
|
|
|
166
183
|
else {
|
|
167
184
|
p.log.error(`Log from Capgo ${data.action}`)
|
|
168
185
|
}
|
|
169
|
-
now = new Date().toISOString()
|
|
170
|
-
query.after = now
|
|
171
186
|
}
|
|
172
|
-
await wait(
|
|
187
|
+
await wait(5000)
|
|
173
188
|
}
|
|
174
189
|
return Promise.resolve()
|
|
175
190
|
}
|
|
@@ -208,8 +223,8 @@ export async function debugApp(appId: string, options: OptionsBaseDebug) {
|
|
|
208
223
|
await cancelCommand('debug', doRun, userId, snag)
|
|
209
224
|
if (doRun) {
|
|
210
225
|
p.log.info(`Wait logs sent to Capgo from ${appId} device, Put the app in background and open it again.`)
|
|
211
|
-
p.log.info('Waiting...')
|
|
212
|
-
await waitLog('debug',
|
|
226
|
+
p.log.info('Waiting... (there is a usual delay of 15 seconds until the backend process the logs)')
|
|
227
|
+
await waitLog('debug', options.apikey, appId, snag, orgId, deviceId)
|
|
213
228
|
p.outro(`Done ✅`)
|
|
214
229
|
}
|
|
215
230
|
else {
|
package/src/app/delete.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { program } from 'commander'
|
|
|
3
3
|
import * as p from '@clack/prompts'
|
|
4
4
|
import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
|
|
5
5
|
import type { OptionsBase } from '../utils'
|
|
6
|
-
import { OrganizationPerm, createSupabaseClient, findSavedKey, formatError, getConfig, useLogSnag, verifyUser } from '../utils'
|
|
6
|
+
import { OrganizationPerm, createSupabaseClient, findSavedKey, formatError, getConfig, getOrganizationId, useLogSnag, verifyUser } from '../utils'
|
|
7
7
|
|
|
8
8
|
export async function deleteApp(appId: string, options: OptionsBase) {
|
|
9
9
|
p.intro(`Deleting`)
|
|
@@ -27,11 +27,11 @@ export async function deleteApp(appId: string, options: OptionsBase) {
|
|
|
27
27
|
await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.super_admin)
|
|
28
28
|
|
|
29
29
|
const { data: appOwnerRaw, error: appOwnerError } = await supabase.from('apps')
|
|
30
|
-
.select(`owner_org ( created_by )`)
|
|
30
|
+
.select(`owner_org ( created_by, id )`)
|
|
31
31
|
.eq('app_id', appId)
|
|
32
32
|
.single()
|
|
33
33
|
|
|
34
|
-
const appOwner = appOwnerRaw as { owner_org: { created_by: string } } | null
|
|
34
|
+
const appOwner = appOwnerRaw as { owner_org: { created_by: string, id: string } } | null
|
|
35
35
|
|
|
36
36
|
if (!appOwnerError && (appOwner?.owner_org.created_by ?? '') !== userId) {
|
|
37
37
|
// We are dealing with a member user that is not the owner
|
|
@@ -66,11 +66,14 @@ export async function deleteApp(appId: string, options: OptionsBase) {
|
|
|
66
66
|
|
|
67
67
|
const { error } = await supabase
|
|
68
68
|
.storage
|
|
69
|
-
.from(`images
|
|
70
|
-
.remove([appId])
|
|
71
|
-
if (error)
|
|
69
|
+
.from(`images`)
|
|
70
|
+
.remove([`org/${appOwner?.owner_org.id}/${appId}/icon`])
|
|
71
|
+
if (error) {
|
|
72
|
+
console.error(error, `images/org/${appOwner?.owner_org.id}/${appId}`)
|
|
72
73
|
p.log.error('Could not delete app logo')
|
|
74
|
+
}
|
|
73
75
|
|
|
76
|
+
// TODO: make the version delete in R2 too
|
|
74
77
|
const { error: delError } = await supabase
|
|
75
78
|
.storage
|
|
76
79
|
.from(`apps/${appId}/${userId}`)
|
|
@@ -84,17 +87,18 @@ export async function deleteApp(appId: string, options: OptionsBase) {
|
|
|
84
87
|
.from('apps')
|
|
85
88
|
.delete()
|
|
86
89
|
.eq('app_id', appId)
|
|
87
|
-
.eq('user_id', userId)
|
|
90
|
+
// .eq('user_id', userId)
|
|
88
91
|
|
|
89
92
|
if (dbError) {
|
|
90
93
|
p.log.error('Could not delete app')
|
|
91
94
|
program.error('')
|
|
92
95
|
}
|
|
96
|
+
const orgId = await getOrganizationId(supabase, appId)
|
|
93
97
|
await snag.track({
|
|
94
98
|
channel: 'app',
|
|
95
99
|
event: 'App Deleted',
|
|
96
100
|
icon: '🗑️',
|
|
97
|
-
user_id:
|
|
101
|
+
user_id: orgId,
|
|
98
102
|
tags: {
|
|
99
103
|
'app-id': appId,
|
|
100
104
|
},
|
package/src/app/info.ts
CHANGED
|
@@ -39,7 +39,10 @@ async function getInstalledDependencies() {
|
|
|
39
39
|
'@capgo/cli': pack.version,
|
|
40
40
|
}
|
|
41
41
|
for (const dependency in dependencies) {
|
|
42
|
-
if (Object.prototype.hasOwnProperty.call(dependencies, dependency)
|
|
42
|
+
if (Object.prototype.hasOwnProperty.call(dependencies, dependency)
|
|
43
|
+
&& dependency.startsWith('@capgo/')
|
|
44
|
+
&& dependency.startsWith('@capawesome/')
|
|
45
|
+
&& dependency.startsWith('capacitor')) {
|
|
43
46
|
// remove ^ or ~ from version
|
|
44
47
|
const version = dependencies[dependency].replace('^', '').replace('~', '')
|
|
45
48
|
installedDependencies[dependency] = version
|
package/src/app/set.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { program } from 'commander'
|
|
|
6
6
|
import * as p from '@clack/prompts'
|
|
7
7
|
import type { Options } from '../api/app'
|
|
8
8
|
import { checkAppExistsAndHasPermissionOrgErr, newIconPath } from '../api/app'
|
|
9
|
-
import { OrganizationPerm, createSupabaseClient, findSavedKey, formatError, getConfig, verifyUser } from '../utils'
|
|
9
|
+
import { OrganizationPerm, createSupabaseClient, findSavedKey, formatError, getConfig, getOrganization, verifyUser } from '../utils'
|
|
10
10
|
|
|
11
11
|
export async function setApp(appId: string, options: Options) {
|
|
12
12
|
p.intro(`Set app`)
|
|
@@ -23,6 +23,8 @@ export async function setApp(appId: string, options: Options) {
|
|
|
23
23
|
program.error(``)
|
|
24
24
|
}
|
|
25
25
|
const supabase = await createSupabaseClient(options.apikey)
|
|
26
|
+
const organization = await getOrganization(supabase, ['admin', 'super_admin'])
|
|
27
|
+
const organizationUid = organization.gid
|
|
26
28
|
|
|
27
29
|
const userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
|
|
28
30
|
// Check we have app access to this appId
|
|
@@ -61,7 +63,7 @@ export async function setApp(appId: string, options: Options) {
|
|
|
61
63
|
}
|
|
62
64
|
if (iconBuff && iconType) {
|
|
63
65
|
const { error } = await supabase.storage
|
|
64
|
-
.from(`images/${
|
|
66
|
+
.from(`images/org/${organizationUid}/${appId}`)
|
|
65
67
|
.upload(fileName, iconBuff, {
|
|
66
68
|
contentType: iconType,
|
|
67
69
|
})
|
|
@@ -71,7 +73,7 @@ export async function setApp(appId: string, options: Options) {
|
|
|
71
73
|
}
|
|
72
74
|
const { data: signedURLData } = await supabase
|
|
73
75
|
.storage
|
|
74
|
-
.from(`images/${
|
|
76
|
+
.from(`images/org/${organizationUid}/${appId}`)
|
|
75
77
|
.getPublicUrl(fileName)
|
|
76
78
|
signedURL = signedURLData?.publicUrl || signedURL
|
|
77
79
|
}
|
package/src/bundle/cleanup.ts
CHANGED
|
@@ -20,11 +20,11 @@ interface Options extends OptionsBase {
|
|
|
20
20
|
|
|
21
21
|
const prompt = promptSync()
|
|
22
22
|
|
|
23
|
-
async function removeVersions(toRemove: Database['public']['Tables']['app_versions']['Row'][], supabase: SupabaseClient<Database>, appid: string
|
|
23
|
+
async function removeVersions(toRemove: Database['public']['Tables']['app_versions']['Row'][], supabase: SupabaseClient<Database>, appid: string) {
|
|
24
24
|
// call deleteSpecificVersion one by one from toRemove sync
|
|
25
25
|
for await (const row of toRemove) {
|
|
26
26
|
p.log.warn(`Removing ${row.name} created on ${(getHumanDate(row.created_at))}`)
|
|
27
|
-
await deleteSpecificVersion(supabase, appid,
|
|
27
|
+
await deleteSpecificVersion(supabase, appid, row.name)
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -57,14 +57,14 @@ export async function cleanupBundle(appid: string, options: Options) {
|
|
|
57
57
|
}
|
|
58
58
|
const supabase = await createSupabaseClient(options.apikey)
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
await verifyUser(supabase, options.apikey, ['write', 'all'])
|
|
61
61
|
|
|
62
62
|
// Check we have app access to this appId
|
|
63
63
|
await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appid, OrganizationPerm.write)
|
|
64
64
|
p.log.info(`Querying all available versions in Capgo`)
|
|
65
65
|
|
|
66
66
|
// Get all active app versions we might possibly be able to cleanup
|
|
67
|
-
let allVersions: (Database['public']['Tables']['app_versions']['Row'] & { keep?: string })[] = await getActiveAppVersions(supabase, appid
|
|
67
|
+
let allVersions: (Database['public']['Tables']['app_versions']['Row'] & { keep?: string })[] = await getActiveAppVersions(supabase, appid)
|
|
68
68
|
|
|
69
69
|
const versionInUse = await getChannelsVersion(supabase, appid)
|
|
70
70
|
|
|
@@ -121,7 +121,7 @@ export async function cleanupBundle(appid: string, options: Options) {
|
|
|
121
121
|
|
|
122
122
|
// Yes, lets clean it up
|
|
123
123
|
p.log.success('You have confirmed removal, removing versions now')
|
|
124
|
-
await removeVersions(toRemove, supabase, appid
|
|
124
|
+
await removeVersions(toRemove, supabase, appid)
|
|
125
125
|
p.outro(`Done ✅`)
|
|
126
126
|
process.exit()
|
|
127
127
|
}
|
package/src/bundle/delete.ts
CHANGED
|
@@ -26,7 +26,7 @@ export async function deleteBundle(bundleId: string, appId: string, options: Opt
|
|
|
26
26
|
}
|
|
27
27
|
const supabase = await createSupabaseClient(options.apikey)
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
await verifyUser(supabase, options.apikey, ['write', 'all'])
|
|
30
30
|
// Check we have app access to this appId
|
|
31
31
|
await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.write)
|
|
32
32
|
|
|
@@ -46,7 +46,7 @@ export async function deleteBundle(bundleId: string, appId: string, options: Opt
|
|
|
46
46
|
|
|
47
47
|
p.log.info(`Deleting bundle ${appId}@${bundleId} from Capgo`)
|
|
48
48
|
|
|
49
|
-
await deleteSpecificVersion(supabase, appId,
|
|
49
|
+
await deleteSpecificVersion(supabase, appId, bundleId)
|
|
50
50
|
p.log.success(`Bundle ${appId}@${bundleId} deleted in Capgo`)
|
|
51
51
|
p.outro(`Done`)
|
|
52
52
|
process.exit()
|
package/src/bundle/list.ts
CHANGED
|
@@ -25,7 +25,7 @@ export async function listBundle(appId: string, options: OptionsBase) {
|
|
|
25
25
|
|
|
26
26
|
const supabase = await createSupabaseClient(options.apikey)
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
await verifyUser(supabase, options.apikey, ['write', 'all', 'read', 'upload'])
|
|
29
29
|
|
|
30
30
|
p.log.info(`Querying available versions of: ${appId} in Capgo`)
|
|
31
31
|
|
|
@@ -33,7 +33,7 @@ export async function listBundle(appId: string, options: OptionsBase) {
|
|
|
33
33
|
await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.read)
|
|
34
34
|
|
|
35
35
|
// Get all active app versions we might possibly be able to cleanup
|
|
36
|
-
const allVersions = await getActiveAppVersions(supabase, appId
|
|
36
|
+
const allVersions = await getActiveAppVersions(supabase, appId)
|
|
37
37
|
|
|
38
38
|
p.log.info(`Active versions in Capgo: ${allVersions?.length}`)
|
|
39
39
|
|
package/src/bundle/upload.ts
CHANGED
|
@@ -3,12 +3,11 @@ import { existsSync, readFileSync } from 'node:fs'
|
|
|
3
3
|
import process from 'node:process'
|
|
4
4
|
import type { Buffer } from 'node:buffer'
|
|
5
5
|
import { performance } from 'node:perf_hooks'
|
|
6
|
-
import AdmZip from 'adm-zip'
|
|
7
6
|
import { program } from 'commander'
|
|
8
7
|
import * as p from '@clack/prompts'
|
|
9
8
|
import { checksum as getChecksum } from '@tomasklaen/checksum'
|
|
10
9
|
import ciDetect from 'ci-info'
|
|
11
|
-
import ky from 'ky'
|
|
10
|
+
import ky, { HTTPError } from 'ky'
|
|
12
11
|
import {
|
|
13
12
|
PutObjectCommand,
|
|
14
13
|
S3Client,
|
|
@@ -43,6 +42,7 @@ import {
|
|
|
43
42
|
uploadUrl,
|
|
44
43
|
useLogSnag,
|
|
45
44
|
verifyUser,
|
|
45
|
+
zipFile,
|
|
46
46
|
} from '../utils'
|
|
47
47
|
import { checkIndexPosition, searchInDirectory } from './check'
|
|
48
48
|
|
|
@@ -100,8 +100,6 @@ export async function uploadBundle(appid: string, options: Options, shouldExit =
|
|
|
100
100
|
channel = channel || 'dev'
|
|
101
101
|
|
|
102
102
|
const config = await getConfig()
|
|
103
|
-
const localS3: boolean = (config?.app?.extConfig?.plugins && config?.app?.extConfig?.plugins?.CapacitorUpdater
|
|
104
|
-
&& config?.app?.extConfig?.plugins?.CapacitorUpdater?.localS3) === true
|
|
105
103
|
|
|
106
104
|
const checkNotifyAppReady = options.codeCheck
|
|
107
105
|
appid = appid || config?.app?.appId
|
|
@@ -258,9 +256,7 @@ export async function uploadBundle(appid: string, options: Options, shouldExit =
|
|
|
258
256
|
let checksum = ''
|
|
259
257
|
let zipped: Buffer | null = null
|
|
260
258
|
if (!external && useS3 === false) {
|
|
261
|
-
|
|
262
|
-
zip.addLocalFolder(path)
|
|
263
|
-
zipped = zip.toBuffer()
|
|
259
|
+
zipped = await zipFile(path)
|
|
264
260
|
const s = p.spinner()
|
|
265
261
|
s.start(`Calculating checksum`)
|
|
266
262
|
checksum = await getChecksum(zipped, 'crc32')
|
|
@@ -289,7 +285,7 @@ export async function uploadBundle(appid: string, options: Options, shouldExit =
|
|
|
289
285
|
channel: 'app',
|
|
290
286
|
event: 'App encryption',
|
|
291
287
|
icon: '🔑',
|
|
292
|
-
user_id:
|
|
288
|
+
user_id: orgId,
|
|
293
289
|
tags: {
|
|
294
290
|
'app-id': appid,
|
|
295
291
|
},
|
|
@@ -319,7 +315,7 @@ It will be also visible in your dashboard\n`)
|
|
|
319
315
|
channel: 'app-error',
|
|
320
316
|
event: 'App Too Large',
|
|
321
317
|
icon: '🚛',
|
|
322
|
-
user_id:
|
|
318
|
+
user_id: orgId,
|
|
323
319
|
tags: {
|
|
324
320
|
'app-id': appid,
|
|
325
321
|
},
|
|
@@ -333,9 +329,7 @@ It will be also visible in your dashboard\n`)
|
|
|
333
329
|
}
|
|
334
330
|
else {
|
|
335
331
|
if (useS3) {
|
|
336
|
-
|
|
337
|
-
zip.addLocalFolder(path)
|
|
338
|
-
zipped = zip.toBuffer()
|
|
332
|
+
zipped = await zipFile(path)
|
|
339
333
|
const s = p.spinner()
|
|
340
334
|
s.start(`Calculating checksum`)
|
|
341
335
|
checksum = await getChecksum(zipped, 'crc32')
|
|
@@ -345,7 +339,7 @@ It will be also visible in your dashboard\n`)
|
|
|
345
339
|
channel: 'app',
|
|
346
340
|
event: 'App external',
|
|
347
341
|
icon: '📤',
|
|
348
|
-
user_id:
|
|
342
|
+
user_id: orgId,
|
|
349
343
|
tags: {
|
|
350
344
|
'app-id': appid,
|
|
351
345
|
},
|
|
@@ -387,7 +381,8 @@ It will be also visible in your dashboard\n`)
|
|
|
387
381
|
|
|
388
382
|
try {
|
|
389
383
|
if (options.multipart !== undefined && options.multipart) {
|
|
390
|
-
|
|
384
|
+
p.log.info(`Uploading bundle as multipart`)
|
|
385
|
+
await uploadMultipart(supabase, appid, bundle, zipped, orgId)
|
|
391
386
|
}
|
|
392
387
|
else {
|
|
393
388
|
const url = await uploadUrl(supabase, appid, bundle)
|
|
@@ -395,18 +390,10 @@ It will be also visible in your dashboard\n`)
|
|
|
395
390
|
p.log.error(`Cannot get upload url`)
|
|
396
391
|
program.error('')
|
|
397
392
|
}
|
|
398
|
-
|
|
399
393
|
await ky.put(url, {
|
|
400
394
|
timeout: options.timeout || UPLOAD_TIMEOUT,
|
|
401
395
|
retry: 5,
|
|
402
396
|
body: zipped,
|
|
403
|
-
headers: (!localS3
|
|
404
|
-
? {
|
|
405
|
-
'Content-Type': 'application/octet-stream',
|
|
406
|
-
'Cache-Control': 'public, max-age=456789, immutable',
|
|
407
|
-
'x-amz-meta-crc32': checksum,
|
|
408
|
-
}
|
|
409
|
-
: undefined),
|
|
410
397
|
})
|
|
411
398
|
}
|
|
412
399
|
}
|
|
@@ -414,7 +401,11 @@ It will be also visible in your dashboard\n`)
|
|
|
414
401
|
const endTime = performance.now()
|
|
415
402
|
const uploadTime = ((endTime - startTime) / 1000).toFixed(2)
|
|
416
403
|
spinner.stop(`Failed to upload bundle ( after ${uploadTime} seconds)`)
|
|
417
|
-
p.log.error(`Cannot upload bundle ${formatError(errorUpload)}`)
|
|
404
|
+
p.log.error(`Cannot upload bundle ( try again with --multipart option) ${formatError(errorUpload)}`)
|
|
405
|
+
if (errorUpload instanceof HTTPError) {
|
|
406
|
+
const body = await errorUpload.response.text()
|
|
407
|
+
p.log.error(`Response: ${formatError(body)}`)
|
|
408
|
+
}
|
|
418
409
|
// call delete version on path /delete_failed_version to delete the version
|
|
419
410
|
await deletedFailedVersion(supabase, appid, bundle)
|
|
420
411
|
program.error('')
|
|
@@ -496,7 +487,7 @@ It will be also visible in your dashboard\n`)
|
|
|
496
487
|
channel: 'app',
|
|
497
488
|
event: 'App Uploaded',
|
|
498
489
|
icon: '⏫',
|
|
499
|
-
user_id:
|
|
490
|
+
user_id: orgId,
|
|
500
491
|
tags: {
|
|
501
492
|
'app-id': appid,
|
|
502
493
|
},
|