@capgo/cli 4.10.3 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/cli",
3
- "version": "4.10.3",
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.574.0",
47
- "@capacitor/cli": "6.0.0",
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.1",
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.12",
55
+ "adm-zip": "^0.5.14",
55
56
  "ci-info": "^4.0.0",
56
- "commander": "12.0.0",
57
- "console-table-printer": "^2.12.0",
57
+ "commander": "12.1.0",
58
+ "console-table-printer": "^2.12.1",
58
59
  "get-latest-version": "^5.1.0",
59
- "ky": "^1.2.4",
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.17.0",
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.12.7",
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
- "@typescript-eslint/eslint-plugin": "^7.8.0",
79
- "@typescript-eslint/parser": "^7.8.0",
80
- "esbuild": "^0.21.2",
81
- "eslint": "9.1.1",
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.4.5"
93
+ "typescript": "5.5.2"
89
94
  }
90
95
  }
@@ -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 }) => 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
- export function displayChannels(data: (Database['public']['Tables']['channels']['Row'] & { version?: version, secondVersion?: version })[]) {
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
  }
@@ -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, _userId: string, bundle: 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, _userId: 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
- const userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
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 = `icon_${randomUUID()}`
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/${userId}/${appId}`)
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/${userId}/${appId}`)
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?: number
50
- rangeEnd?: number
51
- after?: string
49
+ rangeStart?: string
50
+ rangeEnd?: string
51
+ limit?: number
52
52
  }
53
-
54
- export async function getStats(supabase: SupabaseClient<Database>, query: QueryStats): Promise<Database['public']['Tables']['stats']['Row'] | null> {
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 pathStats = 'private/stats'
57
- const res = await supabase.functions.invoke(pathStats, { body: JSON.stringify(query) })
58
- const listData = res.data.data as Database['public']['Tables']['stats']['Row'][]
59
- if (listData?.length > 0)
60
- return listData[0]
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, supabase: SupabaseClient<Database>, appId: string, snag: LogSnag, orgId: string, deviceId?: 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
- rangeStart: 0,
83
- rangeEnd: 1,
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(supabase, query)
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.platform} is the same as your version uploaded, change it to see the update`)
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(1000)
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', supabase, appId, snag, orgId, deviceId)
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/${userId}`)
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: userId,
101
+ user_id: orgId,
98
102
  tags: {
99
103
  'app-id': appId,
100
104
  },
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/${userId}/${appId}`)
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/${userId}/${appId}`)
76
+ .from(`images/org/${organizationUid}/${appId}`)
75
77
  .getPublicUrl(fileName)
76
78
  signedURL = signedURLData?.publicUrl || signedURL
77
79
  }
@@ -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, userId: 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, userId, row.name)
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
- const userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
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, userId)
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, userId)
124
+ await removeVersions(toRemove, supabase, appid)
125
125
  p.outro(`Done ✅`)
126
126
  process.exit()
127
127
  }
@@ -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
- const userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
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, userId, bundleId)
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()
@@ -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
- const userId = await verifyUser(supabase, options.apikey, ['write', 'all', 'read', 'upload'])
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, userId)
36
+ const allVersions = await getActiveAppVersions(supabase, appId)
37
37
 
38
38
  p.log.info(`Active versions in Capgo: ${allVersions?.length}`)
39
39
 
@@ -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
- const zip = new AdmZip()
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: userId,
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: userId,
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
- const zip = new AdmZip()
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: userId,
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
- await uploadMultipart(supabase, appid, bundle, zipped)
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: userId,
490
+ user_id: orgId,
500
491
  tags: {
501
492
  'app-id': appid,
502
493
  },