@capgo/cli 5.0.0-alpha.3 → 5.0.0

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.
Files changed (59) hide show
  1. package/README.md +197 -37
  2. package/dist/index.js +327 -65172
  3. package/dist/package.json +83 -0
  4. package/package.json +48 -61
  5. package/.eslintignore +0 -4
  6. package/.github/FUNDING.yml +0 -1
  7. package/.github/workflows/build.yml +0 -46
  8. package/.github/workflows/bump_version.yml +0 -56
  9. package/.github/workflows/test.yml +0 -30
  10. package/.prettierignore +0 -6
  11. package/.vscode/launch.json +0 -23
  12. package/.vscode/settings.json +0 -5
  13. package/.vscode/tasks.json +0 -42
  14. package/CHANGELOG.md +0 -2727
  15. package/build.mjs +0 -23
  16. package/bun.lockb +0 -0
  17. package/capacitor.config.ts +0 -33
  18. package/crypto_explained.png +0 -0
  19. package/eslint.config.js +0 -3
  20. package/renovate.json +0 -23
  21. package/src/api/app.ts +0 -75
  22. package/src/api/channels.ts +0 -142
  23. package/src/api/crypto.ts +0 -121
  24. package/src/api/devices_override.ts +0 -41
  25. package/src/api/update.ts +0 -12
  26. package/src/api/versions.ts +0 -98
  27. package/src/app/add.ts +0 -154
  28. package/src/app/debug.ts +0 -214
  29. package/src/app/delete.ts +0 -68
  30. package/src/app/info.ts +0 -87
  31. package/src/app/list.ts +0 -63
  32. package/src/app/set.ts +0 -94
  33. package/src/bundle/check.ts +0 -42
  34. package/src/bundle/cleanup.ts +0 -128
  35. package/src/bundle/compatibility.ts +0 -70
  36. package/src/bundle/decrypt.ts +0 -65
  37. package/src/bundle/delete.ts +0 -53
  38. package/src/bundle/encrypt.ts +0 -69
  39. package/src/bundle/list.ts +0 -43
  40. package/src/bundle/unlink.ts +0 -80
  41. package/src/bundle/upload.ts +0 -434
  42. package/src/bundle/zip.ts +0 -137
  43. package/src/channel/add.ts +0 -73
  44. package/src/channel/currentBundle.ts +0 -73
  45. package/src/channel/delete.ts +0 -51
  46. package/src/channel/list.ts +0 -49
  47. package/src/channel/set.ts +0 -171
  48. package/src/index.ts +0 -285
  49. package/src/init.ts +0 -301
  50. package/src/key.ts +0 -158
  51. package/src/login.ts +0 -66
  52. package/src/types/capacitor__cli.d.ts +0 -6
  53. package/src/types/supabase.types.ts +0 -2065
  54. package/src/utils.ts +0 -719
  55. package/test/chunk_convert.ts +0 -28
  56. package/test/data.ts +0 -18769
  57. package/test/test_headers_rls.ts +0 -24
  58. package/test/test_semver.ts +0 -13
  59. package/tsconfig.json +0 -39
package/src/init.ts DELETED
@@ -1,301 +0,0 @@
1
- import { readFileSync, writeFileSync } from 'node:fs'
2
- import type { ExecSyncOptions } from 'node:child_process'
3
- import { execSync, spawnSync } from 'node:child_process'
4
- import process from 'node:process'
5
- import { findPackageManagerType } from '@capgo/find-package-manager'
6
- import * as p from '@clack/prompts'
7
- import type { SupabaseClient } from '@supabase/supabase-js'
8
- import type LogSnag from 'logsnag'
9
- import semver from 'semver'
10
- import type { Database } from './types/supabase.types'
11
- import { markSnag, waitLog } from './app/debug'
12
- import { createKey } from './key'
13
- import { addChannel } from './channel/add'
14
- import { uploadBundle } from './bundle/upload'
15
- import { login } from './login'
16
- import { addApp } from './app/add'
17
- import { checkLatest } from './api/update'
18
- import type { Options } from './api/app'
19
- import { convertAppName, createSupabaseClient, findMainFile, findSavedKey, getConfig, useLogSnag, verifyUser } from './utils'
20
-
21
- interface SuperOptions extends Options {
22
- local: boolean
23
- }
24
- const importInject = 'import { CapacitorUpdater } from \'@capgo/capacitor-updater\''
25
- const codeInject = 'CapacitorUpdater.notifyAppReady()'
26
- // create regex to find line who start by 'import ' and end by ' from '
27
- const regexImport = /import.*from.*/g
28
- const defaultChannel = 'production'
29
- const execOption = { stdio: 'pipe' }
30
-
31
- async function cancelCommand(command: boolean | symbol, userId: string, snag: LogSnag) {
32
- if (p.isCancel(command)) {
33
- await markSnag('onboarding-v2', userId, snag, 'canceled', '🤷')
34
- process.exit()
35
- }
36
- }
37
-
38
- async function markStep(userId: string, snag: LogSnag, step: number | string) {
39
- return markSnag('onboarding-v2', userId, snag, `onboarding-step-${step}`)
40
- }
41
-
42
- async function step2(userId: string, snag: LogSnag, appId: string, options: SuperOptions) {
43
- const doAdd = await p.confirm({ message: `Add ${appId} in Capgo?` })
44
- await cancelCommand(doAdd, userId, snag)
45
- if (doAdd) {
46
- const s = p.spinner()
47
- s.start(`Running: npx @capgo/cli@latest app add ${appId}`)
48
- const addRes = await addApp(appId, options, false)
49
- if (!addRes)
50
- s.stop(`App already add ✅`)
51
- else
52
- s.stop(`App add Done ✅`)
53
- }
54
- else {
55
- p.log.info(`Run yourself "npx @capgo/cli@latest app add ${appId}"`)
56
- }
57
- await markStep(userId, snag, 2)
58
- }
59
-
60
- async function step3(userId: string, snag: LogSnag, apikey: string, appId: string) {
61
- const doChannel = await p.confirm({ message: `Create default channel ${defaultChannel} for ${appId} in Capgo?` })
62
- await cancelCommand(doChannel, userId, snag)
63
- if (doChannel) {
64
- const s = p.spinner()
65
- // create production channel public
66
- s.start(`Running: npx @capgo/cli@latest channel add ${defaultChannel} ${appId} --default`)
67
- const addChannelRes = await addChannel(defaultChannel, appId, {
68
- default: true,
69
- apikey,
70
- }, false)
71
- if (!addChannelRes)
72
- s.stop(`Channel already added ✅`)
73
- else
74
- s.stop(`Channel add Done ✅`)
75
- }
76
- else {
77
- p.log.info(`Run yourself "npx @capgo/cli@latest channel add ${defaultChannel} ${appId} --default"`)
78
- }
79
- await markStep(userId, snag, 3)
80
- }
81
-
82
- const urlMigrateV5 = 'https://capacitorjs.com/docs/updating/5-0'
83
- async function step4(userId: string, snag: LogSnag, apikey: string, appId: string) {
84
- const doInstall = await p.confirm({ message: `Automatic Install "@capgo/capacitor-updater" dependency in ${appId}?` })
85
- await cancelCommand(doInstall, userId, snag)
86
- if (doInstall) {
87
- const s = p.spinner()
88
- s.start(`Checking if @capgo/capacitor-updater is installed`)
89
- const pack = JSON.parse(readFileSync('package.json').toString())
90
- let coreVersion = pack.dependencies['@capacitor/core'] || pack.devDependencies['@capacitor/core']
91
- coreVersion = coreVersion?.replace('^', '').replace('~', '')
92
- if (!coreVersion) {
93
- s.stop(`Cannot find @capacitor/core in package.json, please run \`capgo init\` in a capacitor project`)
94
- process.exit()
95
- }
96
- else if (semver.lt(coreVersion, '5.0.0')) {
97
- s.stop(`@capacitor/core version is ${coreVersion}, please update to Capacitor v5 first: ${urlMigrateV5}`)
98
- process.exit()
99
- }
100
- const pm = findPackageManagerType()
101
- if (pm === 'unknown') {
102
- s.stop(`Cannot reconize package manager, please run \`capgo init\` in a capacitor project with npm, pnpm or yarn`)
103
- process.exit()
104
- }
105
- // // use pm to install capgo
106
- // // run command pm install @capgo/capacitor-updater@latest
107
- const installCmd = pm === 'yarn' ? 'add' : 'install'
108
- // check if capgo is already installed in package.json
109
- if (pack.dependencies['@capgo/capacitor-updater']) {
110
- s.stop(`Capgo already installed ✅`)
111
- }
112
- else {
113
- await execSync(`${pm} ${installCmd} @capgo/capacitor-updater@latest`, execOption as ExecSyncOptions)
114
- s.stop(`Install Done ✅`)
115
- }
116
- }
117
- else {
118
- p.log.info(`Run yourself "npm i @capgo/capacitor-updater@latest"`)
119
- }
120
- await markStep(userId, snag, 4)
121
- }
122
-
123
- async function step5(userId: string, snag: LogSnag, apikey: string, appId: string) {
124
- const doAddCode = await p.confirm({ message: `Automatic Add "${codeInject}" code and import in ${appId}?` })
125
- await cancelCommand(doAddCode, userId, snag)
126
- if (doAddCode) {
127
- const s = p.spinner()
128
- s.start(`Adding @capacitor-updater to your main file`)
129
- const mainFilePath = await findMainFile()
130
- if (!mainFilePath) {
131
- s.stop('No main.ts, main.js, index.ts or index.js file found, You need to add @capgo/capacitor-updater manually')
132
- process.exit()
133
- }
134
- // open main file and inject codeInject
135
- const mainFile = readFileSync(mainFilePath)
136
- // find the last import line in the file and inject codeInject after it
137
- const mainFileContent = mainFile.toString()
138
- const matches = mainFileContent.match(regexImport)
139
- const last = matches?.pop()
140
- if (!last) {
141
- s.stop(`Cannot find import line in main file, use manual installation: https://capgo.app/docs/plugin/installation/`)
142
- process.exit()
143
- }
144
-
145
- if (mainFileContent.includes(codeInject)) {
146
- s.stop(`Code already added to ${mainFilePath} ✅`)
147
- }
148
- else {
149
- const newMainFileContent = mainFileContent.replace(last, `${last}\n${importInject};\n\n${codeInject};\n`)
150
- writeFileSync(mainFilePath, newMainFileContent)
151
- s.stop(`Code added to ${mainFilePath} ✅`)
152
- }
153
- await markStep(userId, snag, 5)
154
- }
155
- else {
156
- p.log.info(`Add to your main file the following code:\n\n${importInject};\n\n${codeInject};\n`)
157
- }
158
- }
159
-
160
- async function step6(userId: string, snag: LogSnag, apikey: string, appId: string) {
161
- const doEncrypt = await p.confirm({ message: `Automatic configure end-to-end encryption in ${appId} updates?` })
162
- await cancelCommand(doEncrypt, userId, snag)
163
- if (doEncrypt) {
164
- const s = p.spinner()
165
- s.start(`Running: npx @capgo/cli@latest key create`)
166
- const keyRes = await createKey({}, false)
167
- if (!keyRes) {
168
- s.stop(`Cannot create key ❌`)
169
- process.exit(1)
170
- }
171
- else {
172
- s.stop(`key created 🔑`)
173
- }
174
- markSnag('onboarding-v2', userId, snag, 'Use encryption')
175
- }
176
- await markStep(userId, snag, 6)
177
- }
178
-
179
- async function step7(userId: string, snag: LogSnag, apikey: string, appId: string) {
180
- const doBuild = await p.confirm({ message: `Automatic build ${appId} with "npm run build" ?` })
181
- await cancelCommand(doBuild, userId, snag)
182
- if (doBuild) {
183
- const s = p.spinner()
184
- s.start(`Running: npm run build && npx cap sync`)
185
- const pack = JSON.parse(readFileSync('package.json').toString())
186
- // check in script build exist
187
- if (!pack.scripts?.build) {
188
- s.stop(`Cannot find build script in package.json, please add it and run \`capgo init\` again`)
189
- process.exit()
190
- }
191
- execSync(`npm run build && npx cap sync`, execOption as ExecSyncOptions)
192
- s.stop(`Build & Sync Done ✅`)
193
- }
194
- else {
195
- p.log.info(`Build yourself with command: npm run build && npx cap sync`)
196
- }
197
- await markStep(userId, snag, 7)
198
- }
199
-
200
- async function step8(userId: string, snag: LogSnag, apikey: string, appId: string) {
201
- const doBundle = await p.confirm({ message: `Automatic upload ${appId} bundle to Capgo?` })
202
- await cancelCommand(doBundle, userId, snag)
203
- if (doBundle) {
204
- const s = p.spinner()
205
- s.start(`Running: npx @capgo/cli@latest bundle upload`)
206
- const uploadRes = await uploadBundle(appId, {
207
- channel: defaultChannel,
208
- apikey,
209
- }, false)
210
- if (!uploadRes) {
211
- s.stop(`Upload failed ❌`)
212
- process.exit()
213
- }
214
- else {
215
- s.stop(`Upload Done ✅`)
216
- }
217
- }
218
- else {
219
- p.log.info(`Upload yourself with command: npx @capgo/cli@latest bundle upload`)
220
- }
221
- await markStep(userId, snag, 8)
222
- }
223
-
224
- async function step9(userId: string, snag: LogSnag) {
225
- const doRun = await p.confirm({ message: `Run in device now ?` })
226
- await cancelCommand(doRun, userId, snag)
227
- if (doRun) {
228
- const plaformType = await p.select({
229
- message: 'Pick a platform to run your app',
230
- options: [
231
- { value: 'ios', label: 'IOS' },
232
- { value: 'android', label: 'Android' },
233
- ],
234
- })
235
- if (p.isCancel(plaformType))
236
- process.exit()
237
-
238
- const platform = plaformType as 'ios' | 'android'
239
- const s = p.spinner()
240
- s.start(`Running: npx cap run ${platform}`)
241
- await spawnSync('npx', ['cap', 'run', platform], { stdio: 'inherit' })
242
- s.stop(`Started Done ✅`)
243
- }
244
- else {
245
- p.log.info(`Run yourself with command: npx cap run <ios|android>`)
246
- }
247
- await markStep(userId, snag, 9)
248
- }
249
-
250
- async function step10(userId: string, snag: LogSnag, supabase: SupabaseClient<Database>, appId: string) {
251
- const doRun = await p.confirm({ message: `Automatic check if update working in device ?` })
252
- await cancelCommand(doRun, userId, snag)
253
- if (doRun) {
254
- p.log.info(`Wait logs sent to Capgo from ${appId} device, Put the app in background and open it again.`)
255
- p.log.info('Waiting...')
256
- await waitLog('onboarding-v2', supabase, appId, snag, userId)
257
- }
258
- else {
259
- const appIdUrl = convertAppName(appId)
260
- p.log.info(`Check logs in https://web.capgo.app/app/p/${appIdUrl}/logs to see if update works.`)
261
- }
262
- await markStep(userId, snag, 10)
263
- }
264
-
265
- export async function initApp(apikey: string, appId: string, options: SuperOptions) {
266
- p.intro(`Capgo onboarding 🛫`)
267
- await checkLatest()
268
- const snag = useLogSnag()
269
- const config = await getConfig()
270
- appId = appId || config?.app?.appId
271
- apikey = apikey || findSavedKey()
272
-
273
- const log = p.spinner()
274
- log.start('Running: npx @capgo/cli@latest login ***')
275
- const loginRes = await login(apikey, options, false)
276
- if (!loginRes)
277
- log.stop('Login already done ✅')
278
- else
279
- log.stop('Login Done ✅')
280
-
281
- const supabase = await createSupabaseClient(apikey)
282
- const userId = await verifyUser(supabase, apikey, ['upload', 'all', 'read', 'write'])
283
- await markStep(userId, snag, 1)
284
-
285
- await step2(userId, snag, appId, options)
286
- await step3(userId, snag, apikey, appId)
287
- await step4(userId, snag, apikey, appId)
288
- await step5(userId, snag, apikey, appId)
289
- await step6(userId, snag, apikey, appId)
290
- await step7(userId, snag, apikey, appId)
291
- await step8(userId, snag, apikey, appId)
292
- await step9(userId, snag)
293
- await step10(userId, snag, supabase, appId)
294
-
295
- await markStep(userId, snag, 0)
296
- p.log.info(`Welcome onboard ✈️!`)
297
- p.log.info(`Your Capgo update system is setup`)
298
- p.log.info(`Next time use \`npx @capgo/cli@latest bundle upload\` to only upload your bundle`)
299
- p.outro(`Bye 👋`)
300
- process.exit()
301
- }
package/src/key.ts DELETED
@@ -1,158 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'node:fs'
2
- import { program } from 'commander'
3
- import { writeConfig } from '@capacitor/cli/dist/config'
4
- import * as p from '@clack/prompts'
5
- import { createRSA } from './api/crypto'
6
- import { baseKey, baseKeyPub, getConfig } from './utils'
7
- import { checkLatest } from './api/update'
8
-
9
- interface saveOptions {
10
- key?: string
11
- keyData?: string
12
- }
13
- interface Options {
14
- force?: boolean
15
- }
16
-
17
- export async function saveKey(options: saveOptions, log = true) {
18
- if (log)
19
- p.intro(`Save keys 🔑`)
20
-
21
- const config = await getConfig()
22
- const { extConfig } = config.app
23
-
24
- // const keyPath = options.key || baseKey
25
- const keyPath = options.key || baseKeyPub
26
- // check if publicKey exist
27
-
28
- let publicKey = options.keyData || ''
29
-
30
- if (!existsSync(keyPath) && !publicKey) {
31
- if (log) {
32
- p.log.error(`Cannot find a public key at ${keyPath} or as keyData option or in ${config.app.extConfigFilePath}`)
33
- program.error('')
34
- }
35
- else {
36
- return false
37
- }
38
- }
39
- else if (existsSync(keyPath)) {
40
- // open with fs publicKey path
41
- const keyFile = readFileSync(keyPath)
42
- publicKey = keyFile.toString()
43
- }
44
-
45
- // let's doublecheck and make sure the key we are saving is the right type based on the decryption strategy
46
- if (publicKey) {
47
- if (!publicKey.startsWith('-----BEGIN RSA PUBLIC KEY-----')) {
48
- if (log) {
49
- p.log.error(`the public key provided is not a valid RSA Public key`)
50
- program.error('')
51
- }
52
- else {
53
- return false
54
- }
55
- }
56
- }
57
-
58
- if (extConfig) {
59
- if (!extConfig.plugins) {
60
- extConfig.plugins = {
61
- extConfig: {},
62
- CapacitorUpdater: {},
63
- }
64
- }
65
- if (!extConfig.plugins.CapacitorUpdater)
66
- extConfig.plugins.CapacitorUpdater = {}
67
-
68
- // TODO: this might be a breaking change if user has other code looking at the specific value in the config file
69
- if (extConfig.plugins.CapacitorUpdater.privateKey)
70
- delete extConfig.plugins.CapacitorUpdater.privateKey
71
- extConfig.plugins.CapacitorUpdater.publicKey = publicKey
72
-
73
- // console.log('extConfig', extConfig)
74
- writeConfig(extConfig, config.app.extConfigFilePath)
75
- }
76
- if (log) {
77
- p.log.success(`public key saved into ${config.app.extConfigFilePath} file in local directory`)
78
- p.log.success(`your app will decode the zip archive with this key`)
79
- }
80
- return true
81
- }
82
- export async function saveKeyCommand(options: saveOptions) {
83
- p.intro(`Save keys 🔑`)
84
- await checkLatest()
85
- await saveKey(options)
86
- }
87
-
88
- export async function createKey(options: Options, log = true) {
89
- // write in file .capgo the apikey in home directory
90
- if (log)
91
- p.intro(`Create keys 🔑`)
92
-
93
- const { publicKey, privateKey } = createRSA()
94
-
95
- // check if baseName already exist
96
- if (existsSync(baseKeyPub) && !options.force) {
97
- if (log) {
98
- p.log.error('Public Key already exists, use --force to overwrite')
99
- program.error('')
100
- }
101
- else {
102
- return false
103
- }
104
- }
105
- writeFileSync(baseKeyPub, publicKey)
106
- if (existsSync(baseKey) && !options.force) {
107
- if (log) {
108
- p.log.error('Private Key already exists, use --force to overwrite')
109
- program.error('')
110
- }
111
- else {
112
- return false
113
- }
114
- }
115
- writeFileSync(baseKey, privateKey)
116
-
117
- const config = await getConfig()
118
- const { extConfig } = config.app
119
-
120
- if (extConfig) {
121
- if (!extConfig.plugins) {
122
- extConfig.plugins = {
123
- extConfig: {},
124
- CapacitorUpdater: {},
125
- }
126
- }
127
-
128
- if (!extConfig.plugins.CapacitorUpdater)
129
- extConfig.plugins.CapacitorUpdater = {}
130
-
131
- // TODO: this might be a breaking change if user has other code looking at the specific value in the config file
132
- if (extConfig.plugins.CapacitorUpdater.privateKey)
133
- delete extConfig.plugins.CapacitorUpdater.privateKey
134
- extConfig.plugins.CapacitorUpdater.publicKey = publicKey
135
-
136
- // console.log('extConfig', extConfig)
137
- writeConfig(extConfig, config.app.extConfigFilePath)
138
- }
139
-
140
- if (log) {
141
- p.log.success('Your RSA key has been generated')
142
- p.log.success(`Private key saved in ${baseKey}`)
143
- p.log.success('This key will be use to encrypt your bundle before sending it to Capgo')
144
- p.log.success('Keep it safe')
145
- p.log.success('Than make it unreadable by Capgo and unmodifiable by anyone')
146
- p.log.success(`Public key saved in ${config.app.extConfigFilePath}`)
147
- p.log.success('Your app will be the only one having it')
148
- p.log.success('Only your users can decrypt your update')
149
- p.log.success('Only your key can send them an update')
150
- p.outro(`Done ✅`)
151
- }
152
- return true
153
- }
154
-
155
- export async function createKeyCommand(options: Options) {
156
- await checkLatest()
157
- await createKey(options)
158
- }
package/src/login.ts DELETED
@@ -1,66 +0,0 @@
1
- import { appendFileSync, existsSync, writeFileSync } from 'node:fs'
2
- import { homedir } from 'node:os'
3
- import process from 'node:process'
4
- import { program } from 'commander'
5
- import * as p from '@clack/prompts'
6
- import { createSupabaseClient, useLogSnag, verifyUser } from './utils'
7
- import { checkLatest } from './api/update'
8
-
9
- interface Options {
10
- local: boolean
11
- }
12
-
13
- export async function login(apikey: string, options: Options, shouldExit = true) {
14
- if (shouldExit)
15
- p.intro(`Login to Capgo`)
16
-
17
- if (!apikey) {
18
- if (shouldExit) {
19
- p.log.error('Missing API key, you need to provide a API key to upload your bundle')
20
- program.error('')
21
- }
22
- return false
23
- }
24
- await checkLatest()
25
- // write in file .capgo the apikey in home directory
26
- try {
27
- const { local } = options
28
- const snag = useLogSnag()
29
-
30
- if (local) {
31
- if (!existsSync('.git')) {
32
- p.log.error('To use local you should be in a git repository')
33
- program.error('')
34
- }
35
- writeFileSync('.capgo', `${apikey}\n`)
36
- appendFileSync('.gitignore', '.capgo\n')
37
- }
38
- else {
39
- const userHomeDir = homedir()
40
- writeFileSync(`${userHomeDir}/.capgo`, `${apikey}\n`)
41
- }
42
- const supabase = await createSupabaseClient(apikey)
43
- const userId = await verifyUser(supabase, apikey, ['write', 'all', 'upload'])
44
- await snag.track({
45
- channel: 'user-login',
46
- event: 'User CLI login',
47
- icon: '✅',
48
- user_id: userId,
49
- notify: false,
50
- }).catch()
51
- p.log.success(`login saved into .capgo file in ${local ? 'local' : 'home'} directory`)
52
- }
53
- catch (e) {
54
- p.log.error(`Error while saving login`)
55
- process.exit(1)
56
- }
57
- if (shouldExit) {
58
- p.outro('Done ✅')
59
- process.exit()
60
- }
61
- return true
62
- }
63
-
64
- export async function loginCommand(apikey: string, options: Options) {
65
- login(apikey, options, true)
66
- }
@@ -1,6 +0,0 @@
1
- // CapacitorConfig
2
-
3
- declare module '@capacitor/cli/dist/config' {
4
- export function loadConfig(): CapacitorConfig
5
- export function writeConfig(extConfig: CapacitorConfig, extConfigFilePath: string): void
6
- };