@capgo/cli 4.0.12 → 4.0.13
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 +8 -0
- package/bun.lockb +0 -0
- package/capacitor.config.ts +2 -2
- package/dist/index.js +650 -654
- package/eslint.config.js +3 -0
- package/package.json +3 -2
- package/src/api/app.ts +29 -36
- package/src/api/channels.ts +53 -49
- package/src/api/crypto.ts +83 -80
- package/src/api/devices_override.ts +12 -13
- package/src/api/update.ts +10 -10
- package/src/api/versions.ts +43 -42
- package/src/app/add.ts +61 -53
- package/src/app/debug.ts +153 -151
- package/src/app/delete.ts +61 -59
- package/src/app/info.ts +74 -77
- package/src/app/list.ts +33 -31
- package/src/app/set.ts +85 -82
- package/src/bundle/check.ts +30 -32
- package/src/bundle/cleanup.ts +71 -74
- package/src/bundle/compatibility.ts +52 -55
- package/src/bundle/decrypt.ts +21 -19
- package/src/bundle/delete.ts +27 -25
- package/src/bundle/encrypt.ts +23 -21
- package/src/bundle/list.ts +42 -40
- package/src/bundle/unlink.ts +69 -60
- package/src/bundle/upload.ts +170 -149
- package/src/bundle/zip.ts +122 -118
- package/src/channel/add.ts +62 -60
- package/src/channel/currentBundle.ts +56 -56
- package/src/channel/delete.ts +46 -43
- package/src/channel/list.ts +23 -21
- package/src/channel/set.ts +76 -68
- package/src/index.ts +55 -57
- package/src/init.ts +254 -252
- package/src/key.ts +56 -52
- package/src/login.ts +30 -28
- package/src/types/capacitor__cli.d.ts +2 -3
- package/src/types/supabase.types.ts +505 -505
- package/src/utils.ts +560 -571
- package/.eslintrc +0 -71
package/src/bundle/upload.ts
CHANGED
|
@@ -1,30 +1,44 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto'
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
import type { Buffer } from 'node:buffer'
|
|
5
|
+
import AdmZip from 'adm-zip'
|
|
6
|
+
import { program } from 'commander'
|
|
7
|
+
import * as p from '@clack/prompts'
|
|
8
|
+
import { checksum as getChecksum } from '@tomasklaen/checksum'
|
|
9
|
+
import ciDetect from 'ci-info'
|
|
10
|
+
import ky from 'ky'
|
|
11
|
+
import { checkLatest } from '../api/update'
|
|
12
|
+
import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
|
|
13
|
+
import { encryptSource } from '../api/crypto'
|
|
14
|
+
import type {
|
|
13
15
|
OptionsBase,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
updateOrCreateChannel, updateOrCreateVersion,
|
|
17
|
-
formatError, findSavedKey, checkPlanValid,
|
|
18
|
-
useLogSnag, regexSemver, baseKeyPub, convertAppName, getLocalConfig, checkCompatibility, requireUpdateMetadata,
|
|
19
|
-
getLocalDepenencies,
|
|
20
|
-
verifyUser,
|
|
16
|
+
} from '../utils'
|
|
17
|
+
import {
|
|
21
18
|
OrganizationPerm,
|
|
19
|
+
baseKeyPub,
|
|
20
|
+
checkCompatibility,
|
|
21
|
+
checkPlanValid,
|
|
22
|
+
convertAppName,
|
|
23
|
+
createSupabaseClient,
|
|
24
|
+
findSavedKey,
|
|
25
|
+
formatError,
|
|
26
|
+
getAppOwner,
|
|
27
|
+
getConfig,
|
|
28
|
+
getLocalConfig,
|
|
29
|
+
getLocalDepenencies,
|
|
22
30
|
hasOrganizationPerm,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
regexSemver,
|
|
32
|
+
requireUpdateMetadata,
|
|
33
|
+
updateOrCreateChannel,
|
|
34
|
+
updateOrCreateVersion,
|
|
35
|
+
uploadUrl,
|
|
36
|
+
useLogSnag,
|
|
37
|
+
verifyUser,
|
|
38
|
+
} from '../utils'
|
|
39
|
+
import { checkIndexPosition, searchInDirectory } from './check'
|
|
26
40
|
|
|
27
|
-
const alertMb = 20
|
|
41
|
+
const alertMb = 20
|
|
28
42
|
|
|
29
43
|
interface Options extends OptionsBase {
|
|
30
44
|
bundle?: string
|
|
@@ -32,75 +46,74 @@ interface Options extends OptionsBase {
|
|
|
32
46
|
channel?: string
|
|
33
47
|
displayIvSession?: boolean
|
|
34
48
|
external?: string
|
|
35
|
-
key?: boolean | string
|
|
36
|
-
keyData?: string
|
|
37
|
-
ivSessionKey?: string
|
|
49
|
+
key?: boolean | string
|
|
50
|
+
keyData?: string
|
|
51
|
+
ivSessionKey?: string
|
|
38
52
|
bundleUrl?: boolean
|
|
39
|
-
codeCheck?: boolean
|
|
40
|
-
minUpdateVersion?: string
|
|
41
|
-
autoMinUpdateVersion?: boolean
|
|
53
|
+
codeCheck?: boolean
|
|
54
|
+
minUpdateVersion?: string
|
|
55
|
+
autoMinUpdateVersion?: boolean
|
|
42
56
|
ignoreMetadataCheck?: boolean
|
|
43
57
|
}
|
|
44
58
|
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const { external, key = false, displayIvSession, autoMinUpdateVersion, ignoreMetadataCheck } = options;
|
|
59
|
+
export async function uploadBundle(appid: string, options: Options, shouldExit = true) {
|
|
60
|
+
p.intro(`Uploading`)
|
|
61
|
+
await checkLatest()
|
|
62
|
+
let { bundle, path, channel } = options
|
|
63
|
+
const { external, key = false, displayIvSession, autoMinUpdateVersion, ignoreMetadataCheck } = options
|
|
51
64
|
let { minUpdateVersion } = options
|
|
52
65
|
options.apikey = options.apikey || findSavedKey()
|
|
53
66
|
const snag = useLogSnag()
|
|
54
67
|
|
|
55
|
-
channel = channel || 'dev'
|
|
68
|
+
channel = channel || 'dev'
|
|
56
69
|
|
|
57
|
-
const config = await getConfig()
|
|
70
|
+
const config = await getConfig()
|
|
58
71
|
const localS3: boolean = (config.app.extConfig.plugins && config.app.extConfig.plugins.CapacitorUpdater
|
|
59
|
-
&& config.app.extConfig.plugins.CapacitorUpdater.localS3) === true
|
|
72
|
+
&& config.app.extConfig.plugins.CapacitorUpdater.localS3) === true
|
|
60
73
|
|
|
61
74
|
const checkNotifyAppReady = options.codeCheck
|
|
62
75
|
appid = appid || config?.app?.appId
|
|
63
76
|
// create bundle name format : 1.0.0-beta.x where x is a uuid
|
|
64
|
-
const uuid = randomUUID().split('-')[0]
|
|
77
|
+
const uuid = randomUUID().split('-')[0]
|
|
65
78
|
bundle = bundle || config?.app?.package?.version || `0.0.1-beta.${uuid}`
|
|
66
|
-
// check if bundle is valid
|
|
79
|
+
// check if bundle is valid
|
|
67
80
|
if (!regexSemver.test(bundle)) {
|
|
68
|
-
p.log.error(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`)
|
|
69
|
-
program.error('')
|
|
81
|
+
p.log.error(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`)
|
|
82
|
+
program.error('')
|
|
70
83
|
}
|
|
71
84
|
path = path || config?.app?.webDir
|
|
72
85
|
if (!options.apikey) {
|
|
73
|
-
p.log.error(`Missing API key, you need to provide a API key to upload your bundle`)
|
|
74
|
-
program.error('')
|
|
86
|
+
p.log.error(`Missing API key, you need to provide a API key to upload your bundle`)
|
|
87
|
+
program.error('')
|
|
75
88
|
}
|
|
76
89
|
if (!appid || !bundle || !path) {
|
|
77
|
-
p.log.error(
|
|
78
|
-
program.error('')
|
|
90
|
+
p.log.error('Missing argument, you need to provide a appid and a bundle and a path, or be in a capacitor project')
|
|
91
|
+
program.error('')
|
|
79
92
|
}
|
|
80
93
|
// check if path exist
|
|
81
94
|
if (!existsSync(path)) {
|
|
82
|
-
p.log.error(`Path ${path} does not exist, build your app first, or provide a valid path`)
|
|
83
|
-
program.error('')
|
|
95
|
+
p.log.error(`Path ${path} does not exist, build your app first, or provide a valid path`)
|
|
96
|
+
program.error('')
|
|
84
97
|
}
|
|
85
98
|
|
|
86
99
|
if (typeof checkNotifyAppReady === 'undefined' || checkNotifyAppReady) {
|
|
87
100
|
const isPluginConfigured = searchInDirectory(path, 'notifyAppReady')
|
|
88
101
|
if (!isPluginConfigured) {
|
|
89
|
-
p.log.error(`notifyAppReady() is missing in the source code. see: https://capgo.app/docs/plugin/api/#notifyappready`)
|
|
90
|
-
program.error('')
|
|
102
|
+
p.log.error(`notifyAppReady() is missing in the source code. see: https://capgo.app/docs/plugin/api/#notifyappready`)
|
|
103
|
+
program.error('')
|
|
91
104
|
}
|
|
92
|
-
const foundIndex = checkIndexPosition(path)
|
|
105
|
+
const foundIndex = checkIndexPosition(path)
|
|
93
106
|
if (!foundIndex) {
|
|
94
|
-
p.log.error(`index.html is missing in the root folder or in the only folder in the root folder`)
|
|
95
|
-
program.error('')
|
|
107
|
+
p.log.error(`index.html is missing in the root folder or in the only folder in the root folder`)
|
|
108
|
+
program.error('')
|
|
96
109
|
}
|
|
97
110
|
}
|
|
98
111
|
|
|
99
|
-
p.log.info(`Upload ${appid}@${bundle} started from path "${path}" to Capgo cloud`)
|
|
112
|
+
p.log.info(`Upload ${appid}@${bundle} started from path "${path}" to Capgo cloud`)
|
|
100
113
|
|
|
101
114
|
const localConfig = await getLocalConfig()
|
|
102
115
|
const supabase = await createSupabaseClient(options.apikey)
|
|
103
|
-
const userId = await verifyUser(supabase, options.apikey, ['write', 'all', 'upload'])
|
|
116
|
+
const userId = await verifyUser(supabase, options.apikey, ['write', 'all', 'upload'])
|
|
104
117
|
// Check we have app access to this appId
|
|
105
118
|
// await checkAppExistsAndHasPermissionErr(supabase, options.apikey, appid);
|
|
106
119
|
|
|
@@ -118,73 +131,76 @@ export const uploadBundle = async (appid: string, options: Options, shouldExit =
|
|
|
118
131
|
.single()
|
|
119
132
|
|
|
120
133
|
// eslint-disable-next-line no-undef-init
|
|
121
|
-
let localDependencies: Awaited<ReturnType<typeof getLocalDepenencies>> | undefined = undefined
|
|
122
|
-
let finalCompatibility: Awaited<ReturnType<typeof checkCompatibility>>['finalCompatibility']
|
|
134
|
+
let localDependencies: Awaited<ReturnType<typeof getLocalDepenencies>> | undefined = undefined
|
|
135
|
+
let finalCompatibility: Awaited<ReturnType<typeof checkCompatibility>>['finalCompatibility']
|
|
123
136
|
|
|
124
137
|
// We only check compatibility IF the channel exists
|
|
125
138
|
if (!channelError && channelData && channelData.version && (channelData.version as any).native_packages && !ignoreMetadataCheck) {
|
|
126
|
-
const spinner = p.spinner()
|
|
127
|
-
spinner.start(`Checking bundle compatibility with channel ${channel}`)
|
|
139
|
+
const spinner = p.spinner()
|
|
140
|
+
spinner.start(`Checking bundle compatibility with channel ${channel}`)
|
|
128
141
|
const {
|
|
129
142
|
finalCompatibility: finalCompatibilityWithChannel,
|
|
130
|
-
localDependencies: localDependenciesWithChannel
|
|
143
|
+
localDependencies: localDependenciesWithChannel,
|
|
131
144
|
} = await checkCompatibility(supabase, appid, channel)
|
|
132
145
|
|
|
133
146
|
finalCompatibility = finalCompatibilityWithChannel
|
|
134
147
|
localDependencies = localDependenciesWithChannel
|
|
135
148
|
|
|
136
|
-
if (finalCompatibility.find(
|
|
137
|
-
p.log.error(`Your bundle is not compatible with the channel ${channel}`)
|
|
138
|
-
p.log.warn(`You can check compatibility with "npx @capgo/cli bundle compatibility"`)
|
|
149
|
+
if (finalCompatibility.find(x => x.localVersion !== x.remoteVersion)) {
|
|
150
|
+
p.log.error(`Your bundle is not compatible with the channel ${channel}`)
|
|
151
|
+
p.log.warn(`You can check compatibility with "npx @capgo/cli bundle compatibility"`)
|
|
139
152
|
|
|
140
153
|
if (autoMinUpdateVersion) {
|
|
141
154
|
minUpdateVersion = bundle
|
|
142
|
-
p.log.info(`Auto set min-update-version to ${minUpdateVersion}`)
|
|
155
|
+
p.log.info(`Auto set min-update-version to ${minUpdateVersion}`)
|
|
143
156
|
}
|
|
144
|
-
}
|
|
157
|
+
}
|
|
158
|
+
else if (autoMinUpdateVersion) {
|
|
145
159
|
try {
|
|
146
160
|
const { minUpdateVersion: lastMinUpdateVersion } = channelData.version as any
|
|
147
161
|
if (!lastMinUpdateVersion || !regexSemver.test(lastMinUpdateVersion)) {
|
|
148
|
-
p.log.error('Invalid remote min update version, skipping auto setting compatibility')
|
|
149
|
-
program.error('')
|
|
162
|
+
p.log.error('Invalid remote min update version, skipping auto setting compatibility')
|
|
163
|
+
program.error('')
|
|
150
164
|
}
|
|
151
165
|
|
|
152
166
|
minUpdateVersion = lastMinUpdateVersion
|
|
153
|
-
p.log.info(`Auto set min-update-version to ${minUpdateVersion}`)
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
167
|
+
p.log.info(`Auto set min-update-version to ${minUpdateVersion}`)
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
p.log.error(`Cannot auto set compatibility, invalid data ${channelData}`)
|
|
171
|
+
program.error('')
|
|
157
172
|
}
|
|
158
173
|
}
|
|
159
|
-
spinner.stop(`Bundle compatible with ${channel} channel`)
|
|
160
|
-
}
|
|
161
|
-
|
|
174
|
+
spinner.stop(`Bundle compatible with ${channel} channel`)
|
|
175
|
+
}
|
|
176
|
+
else if (!ignoreMetadataCheck) {
|
|
177
|
+
p.log.warn(`Channel ${channel} is new or it's your first upload with compatibility check, it will be ignored this time`)
|
|
162
178
|
localDependencies = await getLocalDepenencies()
|
|
163
179
|
|
|
164
180
|
if (autoMinUpdateVersion) {
|
|
165
181
|
minUpdateVersion = bundle
|
|
166
|
-
p.log.info(`Auto set min-update-version to ${minUpdateVersion}`)
|
|
182
|
+
p.log.info(`Auto set min-update-version to ${minUpdateVersion}`)
|
|
167
183
|
}
|
|
168
184
|
}
|
|
169
185
|
|
|
170
186
|
if (updateMetadataRequired && !minUpdateVersion && !ignoreMetadataCheck) {
|
|
171
|
-
p.log.error(`You need to provide a min-update-version to upload a bundle to this channel`)
|
|
172
|
-
program.error('')
|
|
187
|
+
p.log.error(`You need to provide a min-update-version to upload a bundle to this channel`)
|
|
188
|
+
program.error('')
|
|
173
189
|
}
|
|
174
190
|
|
|
175
191
|
if (minUpdateVersion) {
|
|
176
192
|
if (!regexSemver.test(minUpdateVersion)) {
|
|
177
|
-
p.log.error(`Your minimal version update ${minUpdateVersion}, is not valid it should follow semver convention : https://semver.org/`)
|
|
178
|
-
program.error('')
|
|
193
|
+
p.log.error(`Your minimal version update ${minUpdateVersion}, is not valid it should follow semver convention : https://semver.org/`)
|
|
194
|
+
program.error('')
|
|
179
195
|
}
|
|
180
196
|
}
|
|
181
197
|
|
|
182
198
|
const { data: isTrial, error: isTrialsError } = await supabase
|
|
183
199
|
.rpc('is_trial', { userid: userId })
|
|
184
200
|
.single()
|
|
185
|
-
if (isTrial && isTrial > 0 || isTrialsError) {
|
|
186
|
-
p.log.warn(`WARNING !!\nTrial expires in ${isTrial} days`)
|
|
187
|
-
p.log.warn(`Upgrade here: ${localConfig.hostWeb}/dashboard/settings/plans`)
|
|
201
|
+
if ((isTrial && isTrial > 0) || isTrialsError) {
|
|
202
|
+
p.log.warn(`WARNING !!\nTrial expires in ${isTrial} days`)
|
|
203
|
+
p.log.warn(`Upgrade here: ${localConfig.hostWeb}/dashboard/settings/plans`)
|
|
188
204
|
}
|
|
189
205
|
|
|
190
206
|
// check if app already exist
|
|
@@ -193,37 +209,37 @@ export const uploadBundle = async (appid: string, options: Options, shouldExit =
|
|
|
193
209
|
.single()
|
|
194
210
|
|
|
195
211
|
if (appVersion || appVersionError) {
|
|
196
|
-
p.log.error(`Version already exists ${formatError(appVersionError)}`)
|
|
197
|
-
program.error('')
|
|
212
|
+
p.log.error(`Version already exists ${formatError(appVersionError)}`)
|
|
213
|
+
program.error('')
|
|
198
214
|
}
|
|
199
215
|
// make bundle safe for s3 name https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-keys.html
|
|
200
|
-
const safeBundle = bundle.replace(/[^a-zA-Z0-9-_.!*'()]/g, '__')
|
|
201
|
-
const fileName = `${safeBundle}.zip
|
|
216
|
+
const safeBundle = bundle.replace(/[^a-zA-Z0-9-_.!*'()]/g, '__')
|
|
217
|
+
const fileName = `${safeBundle}.zip`
|
|
202
218
|
|
|
203
|
-
let sessionKey
|
|
219
|
+
let sessionKey
|
|
204
220
|
let checksum = ''
|
|
205
|
-
let zipped: Buffer | null = null
|
|
221
|
+
let zipped: Buffer | null = null
|
|
206
222
|
if (!external) {
|
|
207
|
-
const zip = new AdmZip()
|
|
208
|
-
zip.addLocalFolder(path)
|
|
209
|
-
zipped = zip.toBuffer()
|
|
223
|
+
const zip = new AdmZip()
|
|
224
|
+
zip.addLocalFolder(path)
|
|
225
|
+
zipped = zip.toBuffer()
|
|
210
226
|
const s = p.spinner()
|
|
211
|
-
s.start(`Calculating checksum`)
|
|
212
|
-
checksum = await getChecksum(zipped, 'crc32')
|
|
213
|
-
s.stop(`Checksum: ${checksum}`)
|
|
227
|
+
s.start(`Calculating checksum`)
|
|
228
|
+
checksum = await getChecksum(zipped, 'crc32')
|
|
229
|
+
s.stop(`Checksum: ${checksum}`)
|
|
214
230
|
if (key || existsSync(baseKeyPub)) {
|
|
215
231
|
const publicKey = typeof key === 'string' ? key : baseKeyPub
|
|
216
232
|
let keyData = options.keyData || ''
|
|
217
233
|
// check if publicKey exist
|
|
218
234
|
if (!keyData && !existsSync(publicKey)) {
|
|
219
|
-
p.log.error(`Cannot find public key ${publicKey}`)
|
|
220
|
-
if (ciDetect.isCI)
|
|
221
|
-
program.error('')
|
|
222
|
-
|
|
235
|
+
p.log.error(`Cannot find public key ${publicKey}`)
|
|
236
|
+
if (ciDetect.isCI)
|
|
237
|
+
program.error('')
|
|
238
|
+
|
|
223
239
|
const res = await p.confirm({ message: 'Do you want to use our public key ?' })
|
|
224
240
|
if (!res) {
|
|
225
|
-
p.log.error(`Error: Missing public key`)
|
|
226
|
-
program.error('')
|
|
241
|
+
p.log.error(`Error: Missing public key`)
|
|
242
|
+
program.error('')
|
|
227
243
|
}
|
|
228
244
|
keyData = localConfig.signKey || ''
|
|
229
245
|
}
|
|
@@ -243,20 +259,20 @@ export const uploadBundle = async (appid: string, options: Options, shouldExit =
|
|
|
243
259
|
keyData = keyFile.toString()
|
|
244
260
|
}
|
|
245
261
|
// encrypt
|
|
246
|
-
p.log.info(`Encrypting your bundle`)
|
|
262
|
+
p.log.info(`Encrypting your bundle`)
|
|
247
263
|
const res = encryptSource(zipped, keyData)
|
|
248
264
|
sessionKey = res.ivSessionKey
|
|
249
265
|
if (displayIvSession) {
|
|
250
266
|
p.log.info(`Your Iv Session key is ${sessionKey},
|
|
251
267
|
keep it safe, you will need it to decrypt your bundle.
|
|
252
|
-
It will be also visible in your dashboard\n`)
|
|
268
|
+
It will be also visible in your dashboard\n`)
|
|
253
269
|
}
|
|
254
270
|
zipped = res.encryptedData
|
|
255
271
|
}
|
|
256
|
-
const mbSize = Math.floor(zipped.byteLength / 1024 / 1024)
|
|
272
|
+
const mbSize = Math.floor(zipped.byteLength / 1024 / 1024)
|
|
257
273
|
if (mbSize > alertMb) {
|
|
258
|
-
p.log.warn(`WARNING !!\nThe app size is ${mbSize} Mb, this may take a while to download for users\n`)
|
|
259
|
-
p.log.info(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`)
|
|
274
|
+
p.log.warn(`WARNING !!\nThe app size is ${mbSize} Mb, this may take a while to download for users\n`)
|
|
275
|
+
p.log.info(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`)
|
|
260
276
|
await snag.track({
|
|
261
277
|
channel: 'app-error',
|
|
262
278
|
event: 'App Too Large',
|
|
@@ -268,10 +284,12 @@ It will be also visible in your dashboard\n`);
|
|
|
268
284
|
notify: false,
|
|
269
285
|
}).catch()
|
|
270
286
|
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
287
|
+
}
|
|
288
|
+
else if (external && !external.startsWith('https://')) {
|
|
289
|
+
p.log.error(`External link should should start with "https://" current is "${external}"`)
|
|
290
|
+
program.error('')
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
275
293
|
await snag.track({
|
|
276
294
|
channel: 'app',
|
|
277
295
|
event: 'App external',
|
|
@@ -285,11 +303,12 @@ It will be also visible in your dashboard\n`);
|
|
|
285
303
|
sessionKey = options.ivSessionKey
|
|
286
304
|
}
|
|
287
305
|
|
|
288
|
-
const hashedLocalDependencies = localDependencies
|
|
289
|
-
|
|
290
|
-
|
|
306
|
+
const hashedLocalDependencies = localDependencies
|
|
307
|
+
? new Map(localDependencies
|
|
308
|
+
.filter(a => !!a.native && a.native !== undefined)
|
|
309
|
+
.map(a => [a.name, a]))
|
|
310
|
+
: new Map()
|
|
291
311
|
|
|
292
|
-
// eslint-disable-next-line max-len
|
|
293
312
|
const nativePackages = (hashedLocalDependencies.size > 0 || !options.ignoreMetadataCheck) ? Array.from(hashedLocalDependencies, ([name, value]) => ({ name, version: value.version })) : undefined
|
|
294
313
|
|
|
295
314
|
const appOwner = await getAppOwner(supabase, appid)
|
|
@@ -306,35 +325,36 @@ It will be also visible in your dashboard\n`);
|
|
|
306
325
|
native_packages: nativePackages,
|
|
307
326
|
checksum,
|
|
308
327
|
}
|
|
309
|
-
const { error: dbError } = await updateOrCreateVersion(supabase, versionData
|
|
328
|
+
const { error: dbError } = await updateOrCreateVersion(supabase, versionData)
|
|
310
329
|
if (dbError) {
|
|
311
|
-
p.log.error(`Cannot add bundle ${formatError(dbError)}`)
|
|
312
|
-
program.error('')
|
|
330
|
+
p.log.error(`Cannot add bundle ${formatError(dbError)}`)
|
|
331
|
+
program.error('')
|
|
313
332
|
}
|
|
314
333
|
if (!external && zipped) {
|
|
315
|
-
const spinner = p.spinner()
|
|
316
|
-
spinner.start(`Uploading Bundle`)
|
|
334
|
+
const spinner = p.spinner()
|
|
335
|
+
spinner.start(`Uploading Bundle`)
|
|
317
336
|
|
|
318
337
|
const url = await uploadUrl(supabase, appid, fileName)
|
|
319
338
|
if (!url) {
|
|
320
|
-
p.log.error(`Cannot get upload url`)
|
|
321
|
-
program.error('')
|
|
339
|
+
p.log.error(`Cannot get upload url`)
|
|
340
|
+
program.error('')
|
|
322
341
|
}
|
|
323
342
|
await ky.put(url, {
|
|
324
343
|
timeout: 60000,
|
|
325
344
|
body: zipped,
|
|
326
|
-
headers: (!localS3
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
345
|
+
headers: (!localS3
|
|
346
|
+
? {
|
|
347
|
+
'Content-Type': 'application/octet-stream',
|
|
348
|
+
'Cache-Control': 'public, max-age=456789, immutable',
|
|
349
|
+
'x-amz-meta-crc32': checksum,
|
|
350
|
+
}
|
|
351
|
+
: undefined),
|
|
331
352
|
})
|
|
332
353
|
versionData.storage_provider = 'r2'
|
|
333
|
-
const { error: dbError2 } = await updateOrCreateVersion(supabase, versionData
|
|
354
|
+
const { error: dbError2 } = await updateOrCreateVersion(supabase, versionData)
|
|
334
355
|
if (dbError2) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
program.error('');
|
|
356
|
+
p.log.error(`Cannot update bundle ${formatError(dbError2)}`)
|
|
357
|
+
program.error('')
|
|
338
358
|
}
|
|
339
359
|
spinner.stop('Bundle Uploaded 💪')
|
|
340
360
|
}
|
|
@@ -350,25 +370,24 @@ It will be also visible in your dashboard\n`);
|
|
|
350
370
|
version: versionId,
|
|
351
371
|
})
|
|
352
372
|
if (dbError3) {
|
|
353
|
-
p.log.error(`Cannot set channel, the upload key is not allowed to do that, use the "all" for this
|
|
354
|
-
|
|
355
|
-
program.error('');
|
|
373
|
+
p.log.error(`Cannot set channel, the upload key is not allowed to do that, use the "all" for this. ${formatError(dbError3)}`)
|
|
374
|
+
program.error('')
|
|
356
375
|
}
|
|
357
376
|
const appidWeb = convertAppName(appid)
|
|
358
377
|
const bundleUrl = `${localConfig.hostWeb}/app/p/${appidWeb}/channel/${data.id}`
|
|
359
|
-
if (data?.public)
|
|
378
|
+
if (data?.public)
|
|
360
379
|
p.log.info('Your update is now available in your public channel 🎉')
|
|
361
|
-
|
|
362
|
-
p.log.info(`Link device to this bundle to try it: ${bundleUrl}`)
|
|
363
|
-
}
|
|
380
|
+
else if (data?.id)
|
|
381
|
+
p.log.info(`Link device to this bundle to try it: ${bundleUrl}`)
|
|
364
382
|
|
|
365
|
-
if (options.bundleUrl)
|
|
366
|
-
p.log.info(`Bundle url: ${bundleUrl}`)
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
p.log.warn('Cannot set bundle with upload key, use key with more rights for that')
|
|
370
|
-
program.error('')
|
|
371
|
-
}
|
|
383
|
+
if (options.bundleUrl)
|
|
384
|
+
p.log.info(`Bundle url: ${bundleUrl}`)
|
|
385
|
+
}
|
|
386
|
+
else if (!versionId) {
|
|
387
|
+
p.log.warn('Cannot set bundle with upload key, use key with more rights for that')
|
|
388
|
+
program.error('')
|
|
389
|
+
}
|
|
390
|
+
else if (!hasOrganizationPerm(permissions, OrganizationPerm.write)) {
|
|
372
391
|
p.log.warn('Cannot set channel as a upload organization member')
|
|
373
392
|
}
|
|
374
393
|
await snag.track({
|
|
@@ -388,21 +407,23 @@ It will be also visible in your dashboard\n`);
|
|
|
388
407
|
return true
|
|
389
408
|
}
|
|
390
409
|
|
|
391
|
-
export
|
|
410
|
+
export async function uploadCommand(apikey: string, options: Options) {
|
|
392
411
|
try {
|
|
393
412
|
await uploadBundle(apikey, options, true)
|
|
394
|
-
}
|
|
395
|
-
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
p.log.error(formatError(error))
|
|
396
416
|
program.error('')
|
|
397
417
|
}
|
|
398
418
|
}
|
|
399
419
|
|
|
400
|
-
export
|
|
420
|
+
export async function uploadDeprecatedCommand(apikey: string, options: Options) {
|
|
401
421
|
p.log.warn('⚠️ This command is deprecated, use "npx @capgo/cli bundle upload" instead ⚠️')
|
|
402
422
|
try {
|
|
403
423
|
await uploadBundle(apikey, options, true)
|
|
404
|
-
}
|
|
405
|
-
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
p.log.error(formatError(error))
|
|
406
427
|
program.error('')
|
|
407
428
|
}
|
|
408
429
|
}
|