@capgo/cli 4.13.2 → 4.13.3

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 (69) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/.github/FUNDING.yml +0 -1
  4. package/.github/workflows/autofix.yml +0 -25
  5. package/.github/workflows/build.yml +0 -46
  6. package/.github/workflows/bump_version.yml +0 -56
  7. package/.github/workflows/check_posix_paths.yml +0 -229
  8. package/.github/workflows/test.yml +0 -30
  9. package/.prettierignore +0 -6
  10. package/.vscode/launch.json +0 -23
  11. package/.vscode/settings.json +0 -46
  12. package/.vscode/tasks.json +0 -42
  13. package/CHANGELOG.md +0 -3392
  14. package/build.mjs +0 -21
  15. package/bun.lockb +0 -0
  16. package/bunfig.toml +0 -2
  17. package/capacitor.config.ts +0 -33
  18. package/crypto_explained.png +0 -0
  19. package/eslint.config.js +0 -10
  20. package/renovate.json +0 -23
  21. package/src/api/app.ts +0 -55
  22. package/src/api/channels.ts +0 -163
  23. package/src/api/crypto.ts +0 -116
  24. package/src/api/devices_override.ts +0 -41
  25. package/src/api/update.ts +0 -13
  26. package/src/api/versions.ts +0 -101
  27. package/src/app/add.ts +0 -157
  28. package/src/app/debug.ts +0 -258
  29. package/src/app/delete.ts +0 -110
  30. package/src/app/info.ts +0 -99
  31. package/src/app/list.ts +0 -67
  32. package/src/app/set.ts +0 -96
  33. package/src/bundle/check.ts +0 -42
  34. package/src/bundle/cleanup.ts +0 -123
  35. package/src/bundle/compatibility.ts +0 -70
  36. package/src/bundle/decrypt.ts +0 -54
  37. package/src/bundle/delete.ts +0 -52
  38. package/src/bundle/encrypt.ts +0 -60
  39. package/src/bundle/list.ts +0 -42
  40. package/src/bundle/unlink.ts +0 -88
  41. package/src/bundle/upload.ts +0 -552
  42. package/src/bundle/zip.ts +0 -145
  43. package/src/channel/add.ts +0 -80
  44. package/src/channel/currentBundle.ts +0 -72
  45. package/src/channel/delete.ts +0 -57
  46. package/src/channel/list.ts +0 -49
  47. package/src/channel/set.ts +0 -179
  48. package/src/config/index.ts +0 -156
  49. package/src/index.ts +0 -310
  50. package/src/init.ts +0 -495
  51. package/src/key.ts +0 -135
  52. package/src/login.ts +0 -70
  53. package/src/types/capacitor__cli.d.ts +0 -6
  54. package/src/types/supabase.types.ts +0 -2123
  55. package/src/user/account.ts +0 -11
  56. package/src/utils.ts +0 -1076
  57. package/test/VerifyZip.java +0 -83
  58. package/test/check-posix-paths.js +0 -21
  59. package/test/chunk_convert.ts +0 -28
  60. package/test/data.ts +0 -18769
  61. package/test/test_headers_rls.ts +0 -24
  62. package/test/test_semver.ts +0 -13
  63. package/test/test_upload/app.js +0 -3
  64. package/test/test_upload/assets/check-posix-paths.js +0 -21
  65. package/test/test_upload/index.html +0 -0
  66. package/test/test_zip_swift/Package.resolved +0 -24
  67. package/test/test_zip_swift/Package.swift +0 -29
  68. package/test/test_zip_swift/Sources/main.swift +0 -80
  69. package/tsconfig.json +0 -39
package/src/app/set.ts DELETED
@@ -1,96 +0,0 @@
1
- import { randomUUID } from 'node:crypto'
2
- import { existsSync, readFileSync } from 'node:fs'
3
- import { exit } from 'node:process'
4
- import mime from 'mime'
5
- import { program } from 'commander'
6
- import { intro, log, outro } from '@clack/prompts'
7
- import type { Options } from '../api/app'
8
- import { checkAppExistsAndHasPermissionOrgErr, newIconPath } from '../api/app'
9
- import { OrganizationPerm, createSupabaseClient, findSavedKey, formatError, getConfig, getOrganization, verifyUser } from '../utils'
10
-
11
- export async function setApp(appId: string, options: Options) {
12
- intro(`Set app`)
13
- options.apikey = options.apikey || findSavedKey()
14
- const extConfig = await getConfig()
15
- appId = appId || extConfig?.config?.appId
16
-
17
- if (!options.apikey) {
18
- log.error(`Missing API key, you need to provide a API key to upload your bundle`)
19
- program.error(``)
20
- }
21
- if (!appId) {
22
- log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
23
- program.error(``)
24
- }
25
- const supabase = await createSupabaseClient(options.apikey)
26
- const organization = await getOrganization(supabase, ['admin', 'super_admin'])
27
- const organizationUid = organization.gid
28
-
29
- const userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
30
- // Check we have app access to this appId
31
- await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.admin)
32
-
33
- const { name, icon, retention } = options
34
-
35
- if (retention && Number.isNaN(Number(retention))) {
36
- log.error(`retention value must be a number`)
37
- program.error(``)
38
- }
39
- else if (retention && retention < 0) {
40
- log.error(`retention value cannot be less than 0`)
41
- program.error(``)
42
- }
43
-
44
- let iconBuff
45
- let iconType
46
- const fileName = `icon_${randomUUID()}`
47
- let signedURL = 'https://xvwzpoazmxkqosrdewyv.supabase.co/storage/v1/object/public/images/capgo.png'
48
-
49
- if (icon && existsSync(icon)) {
50
- iconBuff = readFileSync(icon)
51
- const contentType = mime.getType(icon)
52
- iconType = contentType || 'image/png'
53
- log.warn(`Found app icon ${icon}`)
54
- }
55
- else if (existsSync(newIconPath)) {
56
- iconBuff = readFileSync(newIconPath)
57
- const contentType = mime.getType(newIconPath)
58
- iconType = contentType || 'image/png'
59
- log.warn(`Found app icon ${newIconPath}`)
60
- }
61
- else {
62
- log.warn(`Cannot find app icon in any of the following locations: ${icon}, ${newIconPath}`)
63
- }
64
- if (iconBuff && iconType) {
65
- const { error } = await supabase.storage
66
- .from(`images/org/${organizationUid}/${appId}`)
67
- .upload(fileName, iconBuff, {
68
- contentType: iconType,
69
- })
70
- if (error) {
71
- log.error(`Could not set app ${formatError(error)}`)
72
- program.error(``)
73
- }
74
- const { data: signedURLData } = await supabase
75
- .storage
76
- .from(`images/org/${organizationUid}/${appId}`)
77
- .getPublicUrl(fileName)
78
- signedURL = signedURLData?.publicUrl || signedURL
79
- }
80
- // retention is in seconds in the database but received as days here
81
- const { error: dbError } = await supabase
82
- .from('apps')
83
- .update({
84
- icon_url: signedURL,
85
- name,
86
- retention: !retention ? undefined : retention * 24 * 60 * 60,
87
- })
88
- .eq('app_id', appId)
89
- .eq('user_id', userId)
90
- if (dbError) {
91
- log.error(`Could not set app ${formatError(dbError)}`)
92
- program.error(``)
93
- }
94
- outro(`Done ✅`)
95
- exit()
96
- }
@@ -1,42 +0,0 @@
1
- import { readFileSync, readdirSync, statSync } from 'node:fs'
2
- import { extname, join } from 'node:path'
3
-
4
- function searchInFile(filePath: string, searchString: string) {
5
- const content = readFileSync(filePath, 'utf8')
6
- return content.includes(searchString)
7
- }
8
-
9
- export function searchInDirectory(dirPath: string, searchString: string) {
10
- const files = readdirSync(dirPath)
11
- for (const file of files) {
12
- const filePath = join(dirPath, file)
13
- const stats = statSync(filePath)
14
-
15
- if (stats.isDirectory()) {
16
- if (searchInDirectory(filePath, searchString))
17
- return true
18
- }
19
- else if (stats.isFile() && extname(filePath) === '.js') {
20
- if (searchInFile(filePath, searchString))
21
- return true
22
- }
23
- }
24
-
25
- return false
26
- }
27
-
28
- export function checkIndexPosition(dirPath: string): boolean {
29
- // look for index.html in the root folder or if there only one folder in the root folder look for index.html in this folder
30
- const files = readdirSync(dirPath)
31
- if (files.length === 1) {
32
- const filePath = join(dirPath, files[0])
33
- const stats = statSync(filePath)
34
- if (stats.isDirectory())
35
- return checkIndexPosition(filePath)
36
- }
37
- const index = files.indexOf('index.html')
38
- if (index > -1)
39
- return true
40
-
41
- return false
42
- }
@@ -1,123 +0,0 @@
1
- import { exit } from 'node:process'
2
- import { program } from 'commander'
3
- import semver from 'semver/preload'
4
- import type { SupabaseClient } from '@supabase/supabase-js'
5
- import { confirm as confirmC, intro, isCancel, log, outro } from '@clack/prompts'
6
- import type { Database } from '../types/supabase.types'
7
- import type { OptionsBase } from '../utils'
8
- import { OrganizationPerm, createSupabaseClient, findSavedKey, getConfig, getHumanDate, verifyUser } from '../utils'
9
- import { deleteSpecificVersion, displayBundles, getActiveAppVersions, getChannelsVersion } from '../api/versions'
10
- import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
11
- import { checkLatest } from '../api/update'
12
-
13
- interface Options extends OptionsBase {
14
- version: string
15
- bundle: string
16
- keep: number
17
- force: boolean
18
- }
19
-
20
- async function removeVersions(toRemove: Database['public']['Tables']['app_versions']['Row'][], supabase: SupabaseClient<Database>, appid: string) {
21
- // call deleteSpecificVersion one by one from toRemove sync
22
- for await (const row of toRemove) {
23
- log.warn(`Removing ${row.name} created on ${(getHumanDate(row.created_at))}`)
24
- await deleteSpecificVersion(supabase, appid, row.name)
25
- }
26
- }
27
-
28
- function getRemovableVersionsInSemverRange(data: Database['public']['Tables']['app_versions']['Row'][], bundle: string, nextMajor: string) {
29
- const toRemove: Database['public']['Tables']['app_versions']['Row'][] = []
30
-
31
- data?.forEach((row) => {
32
- if (semver.gte(row.name, bundle) && semver.lt(row.name, `${nextMajor}`))
33
- toRemove.push(row)
34
- })
35
- return toRemove
36
- }
37
-
38
- export async function cleanupBundle(appId: string, options: Options) {
39
- intro(`Cleanup versions in Capgo`)
40
- await checkLatest()
41
- options.apikey = options.apikey || findSavedKey()
42
- const { bundle, keep = 4 } = options
43
- const force = options.force || false
44
-
45
- const extConfig = await getConfig()
46
- appId = appId || extConfig?.config?.appId
47
- if (!options.apikey) {
48
- log.error('Missing API key, you need to provide an API key to delete your app')
49
- program.error('')
50
- }
51
- if (!appId) {
52
- log.error('Missing argument, you need to provide a appid, or be in a capacitor project')
53
- program.error('')
54
- }
55
- const supabase = await createSupabaseClient(options.apikey)
56
-
57
- await verifyUser(supabase, options.apikey, ['write', 'all'])
58
-
59
- // Check we have app access to this appId
60
- await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.write)
61
- log.info(`Querying all available versions in Capgo`)
62
-
63
- // Get all active app versions we might possibly be able to cleanup
64
- let allVersions: (Database['public']['Tables']['app_versions']['Row'] & { keep?: string })[] = await getActiveAppVersions(supabase, appId)
65
-
66
- const versionInUse = await getChannelsVersion(supabase, appId)
67
-
68
- log.info(`Total active versions in Capgo: ${allVersions?.length}`)
69
- if (allVersions?.length === 0) {
70
- log.error('No versions found, aborting cleanup')
71
- return
72
- }
73
- if (bundle) {
74
- const nextMajor = `${semver.inc(bundle, 'major')}`
75
- log.info(`Querying available versions in Capgo between ${bundle} and ${nextMajor}`)
76
-
77
- // Get all app versions that are in the given range
78
- allVersions = getRemovableVersionsInSemverRange(allVersions, bundle, nextMajor) as (Database['public']['Tables']['app_versions']['Row'] & { keep: string })[]
79
-
80
- log.info(`Active versions in Capgo between ${bundle} and ${nextMajor}: ${allVersions?.length}`)
81
- }
82
-
83
- // Slice to keep and remove
84
-
85
- const toRemove: (Database['public']['Tables']['app_versions']['Row'] & { keep?: string })[] = []
86
- // Slice to keep and remove
87
- let kept = 0
88
- allVersions.forEach((v) => {
89
- const isInUse = versionInUse.find(vi => vi === v.id)
90
- if (kept < keep || isInUse) {
91
- if (isInUse)
92
- v.keep = '✅ (Linked to channel)'
93
- else
94
- v.keep = '✅'
95
-
96
- kept += 1
97
- }
98
- else {
99
- v.keep = '❌'
100
- toRemove.push(v)
101
- }
102
- })
103
-
104
- if (toRemove.length === 0) {
105
- log.warn('Nothing to be removed, aborting removal...')
106
- return
107
- }
108
- displayBundles(allVersions)
109
- // Check user wants to clean that all up
110
- if (!force) {
111
- const doDelete = await confirmC({ message: 'Do you want to continue removing the versions specified?' })
112
- if (isCancel(doDelete) || !doDelete) {
113
- log.warn('Not confirmed, aborting removal...')
114
- exit()
115
- }
116
- }
117
-
118
- // Yes, lets clean it up
119
- log.success('You have confirmed removal, removing versions now')
120
- await removeVersions(toRemove, supabase, appId)
121
- outro(`Done ✅`)
122
- exit()
123
- }
@@ -1,70 +0,0 @@
1
- import { program } from 'commander'
2
- import { Table } from 'console-table-printer'
3
- import { intro, log } from '@clack/prompts'
4
- import type { OptionsBase } from '../utils'
5
- import { OrganizationPerm, checkCompatibility, createSupabaseClient, findSavedKey, getConfig, verifyUser } from '../utils'
6
- import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
7
-
8
- interface Options extends OptionsBase {
9
- channel?: string
10
- text?: boolean
11
- }
12
-
13
- export async function checkCompatibilityCommand(appId: string, options: Options) {
14
- intro(`Check compatibility`)
15
- options.apikey = options.apikey || findSavedKey()
16
- const extConfig = await getConfig()
17
- appId = appId || extConfig?.config?.appId
18
-
19
- const { channel } = options
20
-
21
- if (!channel) {
22
- log.error('Missing argument, you need to provide a channel')
23
- program.error('')
24
- }
25
-
26
- if (!options.apikey) {
27
- log.error('Missing API key, you need to provide a API key to upload your bundle')
28
- program.error('')
29
- }
30
- if (!appId) {
31
- log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
32
- program.error('')
33
- }
34
-
35
- const supabase = await createSupabaseClient(options.apikey)
36
- await verifyUser(supabase, options.apikey, ['write', 'all', 'read', 'upload'])
37
-
38
- // Check we have app access to this appId
39
- await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.read)
40
-
41
- // const hashedLocalDependencies = new Map(dependenciesObject
42
- // .filter((a) => !!a.native && a.native !== undefined)
43
- // .map((a) => [a.name, a]))
44
-
45
- // const nativePackages = Array.from(hashedLocalDependencies, ([name, value]) => ({ name, version: value.version }))
46
- // await supabase.from('app_versions').update({ native_packages: nativePackages }).eq('id', '9654')
47
-
48
- const { finalCompatibility } = await checkCompatibility(supabase, appId, channel)
49
-
50
- const t = new Table({
51
- title: 'Compatibility',
52
- charLength: { '❌': 2, '✅': 2 },
53
- })
54
-
55
- const yesSymbol = options.text ? 'Yes' : '✅'
56
- const noSymbol = options.text ? 'No' : '❌'
57
-
58
- finalCompatibility.forEach((data) => {
59
- const { name, localVersion, remoteVersion } = data
60
-
61
- t.addRow({
62
- 'Package': name,
63
- 'Local version': localVersion ?? 'None',
64
- 'Remote version': remoteVersion ?? 'None',
65
- 'Compatible': remoteVersion === localVersion ? yesSymbol : noSymbol,
66
- })
67
- })
68
-
69
- log.success(t.render())
70
- }
@@ -1,54 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'node:fs'
2
- import { exit } from 'node:process'
3
- import { program } from 'commander'
4
- import { intro, log, outro } from '@clack/prompts'
5
- import { decryptSource } from '../api/crypto'
6
- import { baseKey, getConfig } from '../utils'
7
- import { checkLatest } from '../api/update'
8
-
9
- interface Options {
10
- key?: string
11
- keyData?: string
12
- }
13
-
14
- export async function decryptZip(zipPath: string, ivsessionKey: string, options: Options) {
15
- intro(`Decrypt zip file`)
16
- await checkLatest()
17
- // write in file .capgo the apikey in home directory
18
-
19
- if (!existsSync(zipPath)) {
20
- log.error(`Zip not found at the path ${zipPath}`)
21
- program.error('')
22
- }
23
-
24
- const config = await getConfig()
25
- const { extConfig } = config.app
26
-
27
- if (!options.key && !existsSync(baseKey) && !extConfig.plugins?.CapacitorUpdater?.privateKey) {
28
- log.error(`Private Key not found at the path ${baseKey} or in ${config.apextConfigFilePath}`)
29
- program.error('')
30
- }
31
- const keyPath = options.key || baseKey
32
- // check if publicKey exist
33
-
34
- let privateKey = extConfig?.plugins?.CapacitorUpdater?.privateKey
35
-
36
- if (!existsSync(keyPath) && !privateKey) {
37
- log.error(`Cannot find public key ${keyPath} or as keyData option or in ${config.apextConfigFilePath}`)
38
- program.error('')
39
- }
40
- else if (existsSync(keyPath)) {
41
- // open with fs publicKey path
42
- const keyFile = readFileSync(keyPath)
43
- privateKey = keyFile.toString()
44
- }
45
- // console.log('privateKey', privateKey)
46
-
47
- const zipFile = readFileSync(zipPath)
48
-
49
- const decodedZip = decryptSource(zipFile, ivsessionKey, options.keyData ?? privateKey ?? '')
50
- // write decodedZip in a file
51
- writeFileSync(`${zipPath}_decrypted.zip`, decodedZip)
52
- outro(`Decrypted zip file at ${zipPath}_decrypted.zip`)
53
- exit()
54
- }
@@ -1,52 +0,0 @@
1
- import { exit } from 'node:process'
2
- import { program } from 'commander'
3
- import { intro, log, outro } from '@clack/prompts'
4
- import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
5
- import type { OptionsBase } from '../utils'
6
- import { OrganizationPerm, createSupabaseClient, findSavedKey, getConfig, verifyUser } from '../utils'
7
- import { deleteSpecificVersion } from '../api/versions'
8
-
9
- interface Options extends OptionsBase {
10
- bundle: string
11
- }
12
-
13
- export async function deleteBundle(bundleId: string, appId: string, options: Options) {
14
- intro(`Delete bundle`)
15
- options.apikey = options.apikey || findSavedKey()
16
- const extConfig = await getConfig()
17
- appId = appId || extConfig?.config?.appId
18
-
19
- if (!options.apikey) {
20
- log.error('Missing API key, you need to provide a API key to upload your bundle')
21
- program.error('')
22
- }
23
- if (!appId) {
24
- log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
25
- program.error('')
26
- }
27
- const supabase = await createSupabaseClient(options.apikey)
28
-
29
- await verifyUser(supabase, options.apikey, ['write', 'all'])
30
- // Check we have app access to this appId
31
- await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.write)
32
-
33
- if (!options.apikey) {
34
- log.error('Missing API key, you need to provide an API key to delete your app')
35
- program.error('')
36
- }
37
- if (!bundleId) {
38
- log.error('Missing argument, you need to provide a bundleId, or be in a capacitor project')
39
- program.error('')
40
- }
41
- if (!appId) {
42
- log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
43
- program.error('')
44
- }
45
-
46
- log.info(`Deleting bundle ${appId}@${bundleId} from Capgo`)
47
-
48
- await deleteSpecificVersion(supabase, appId, bundleId)
49
- log.success(`Bundle ${appId}@${bundleId} deleted in Capgo`)
50
- outro(`Done`)
51
- exit()
52
- }
@@ -1,60 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from 'node:fs'
2
- import { exit } from 'node:process'
3
- import { program } from 'commander'
4
- import ciDetect from 'ci-info'
5
- import { confirm as confirmC, intro, log, outro } from '@clack/prompts'
6
- import { checkLatest } from '../api/update'
7
- import { encryptSource } from '../api/crypto'
8
- import { baseKeyPub, getLocalConfig } from '../utils'
9
-
10
- interface Options {
11
- key?: string
12
- keyData?: string
13
- }
14
-
15
- export async function encryptZip(zipPath: string, options: Options) {
16
- intro(`Encryption`)
17
-
18
- await checkLatest()
19
- const localConfig = await getLocalConfig()
20
-
21
- // write in file .capgo the apikey in home directory
22
-
23
- if (!existsSync(zipPath)) {
24
- log.error(`Error: Zip not found at the path ${zipPath}`)
25
- program.error('')
26
- }
27
-
28
- const keyPath = options.key || baseKeyPub
29
- // check if publicKey exist
30
-
31
- let publicKey = options.keyData || ''
32
-
33
- if (!existsSync(keyPath) && !publicKey) {
34
- log.warning(`Cannot find public key ${keyPath} or as keyData option`)
35
- if (ciDetect.isCI) {
36
- log.error(`Error: Missing public key`)
37
- program.error('')
38
- }
39
- const res = await confirmC({ message: 'Do you want to use our public key ?' })
40
- if (!res) {
41
- log.error(`Error: Missing public key`)
42
- program.error('')
43
- }
44
- publicKey = localConfig.signKey || ''
45
- }
46
- else if (existsSync(keyPath)) {
47
- // open with fs publicKey path
48
- const keyFile = readFileSync(keyPath)
49
- publicKey = keyFile.toString()
50
- }
51
-
52
- const zipFile = readFileSync(zipPath)
53
- const encodedZip = encryptSource(zipFile, publicKey)
54
- log.success(`ivSessionKey: ${encodedZip.ivSessionKey}`)
55
- // write decodedZip in a file
56
- writeFileSync(`${zipPath}_encrypted.zip`, encodedZip.encryptedData)
57
- log.success(`Encrypted zip saved at ${zipPath}_encrypted.zip`)
58
- outro(`Done ✅`)
59
- exit()
60
- }
@@ -1,42 +0,0 @@
1
- import { exit } from 'node:process'
2
- import { program } from 'commander'
3
- import { intro, log, outro } from '@clack/prompts'
4
- import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
5
- import { displayBundles, getActiveAppVersions } from '../api/versions'
6
- import type { OptionsBase } from '../utils'
7
- import { OrganizationPerm, createSupabaseClient, findSavedKey, getConfig, verifyUser } from '../utils'
8
- import { checkLatest } from '../api/update'
9
-
10
- export async function listBundle(appId: string, options: OptionsBase) {
11
- intro(`List bundles`)
12
- await checkLatest()
13
- options.apikey = options.apikey || findSavedKey()
14
- const extConfig = await getConfig()
15
- appId = appId || extConfig?.config?.appId
16
- if (!options.apikey) {
17
- log.error('Missing API key, you need to provide a API key to upload your bundle')
18
- program.error('')
19
- }
20
- if (!appId) {
21
- log.error('Missing argument, you need to provide a appid, or be in a capacitor project')
22
- program.error('')
23
- }
24
-
25
- const supabase = await createSupabaseClient(options.apikey)
26
-
27
- await verifyUser(supabase, options.apikey, ['write', 'all', 'read', 'upload'])
28
-
29
- log.info(`Querying available versions of: ${appId} in Capgo`)
30
-
31
- // Check we have app access to this appId
32
- await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.read)
33
-
34
- // Get all active app versions we might possibly be able to cleanup
35
- const allVersions = await getActiveAppVersions(supabase, appId)
36
-
37
- log.info(`Active versions in Capgo: ${allVersions?.length}`)
38
-
39
- displayBundles(allVersions)
40
- outro(`Done ✅`)
41
- exit()
42
- }
@@ -1,88 +0,0 @@
1
- import { exit } from 'node:process'
2
- import { program } from 'commander'
3
- import { intro, log, outro } from '@clack/prompts'
4
- import { getVersionData } from '../api/versions'
5
- import { checkVersionNotUsedInDeviceOverride } from '../api/devices_override'
6
- import { checkVersionNotUsedInChannel } from '../api/channels'
7
- import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
8
- import type {
9
- OptionsBase,
10
- } from '../utils'
11
- import {
12
- OrganizationPerm,
13
- checkPlanValid,
14
- createSupabaseClient,
15
- findSavedKey,
16
- formatError,
17
- getConfig,
18
- getOrganizationId,
19
- readPackageJson,
20
- useLogSnag,
21
- verifyUser,
22
- } from '../utils'
23
-
24
- interface Options extends OptionsBase {
25
- bundle?: string
26
- }
27
-
28
- export async function unlinkDevice(channel: string, appId: string, options: Options) {
29
- intro(`Unlink bundle ${options.apikey}`)
30
- options.apikey = options.apikey || findSavedKey()
31
- const extConfig = await getConfig()
32
- appId = appId || extConfig?.config?.appId
33
- const snag = useLogSnag()
34
- let { bundle } = options
35
-
36
- const pack = await readPackageJson()
37
- bundle = bundle || pack?.version
38
-
39
- if (!options.apikey) {
40
- log.error('Missing API key, you need to provide a API key to upload your bundle')
41
- program.error('')
42
- }
43
- if (!appId) {
44
- log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
45
- program.error('')
46
- }
47
- if (!bundle) {
48
- log.error('Missing argument, you need to provide a bundle, or be in a capacitor project')
49
- program.error('')
50
- }
51
- const supabase = await createSupabaseClient(options.apikey)
52
-
53
- const [userId, orgId] = await Promise.all([
54
- verifyUser(supabase, options.apikey, ['all', 'write']),
55
- getOrganizationId(supabase, appId),
56
- ])
57
-
58
- // Check we have app access to this appId
59
- await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.write)
60
-
61
- if (!channel) {
62
- log.error('Missing argument, you need to provide a channel')
63
- program.error('')
64
- }
65
- try {
66
- await checkPlanValid(supabase, orgId, options.apikey, appId)
67
-
68
- const versionData = await getVersionData(supabase, appId, bundle)
69
- await checkVersionNotUsedInChannel(supabase, appId, versionData)
70
- await checkVersionNotUsedInDeviceOverride(supabase, appId, versionData)
71
- await snag.track({
72
- channel: 'bundle',
73
- event: 'Unlink bundle',
74
- icon: '✅',
75
- user_id: userId,
76
- tags: {
77
- 'app-id': appId,
78
- },
79
- notify: false,
80
- }).catch()
81
- }
82
- catch (err) {
83
- log.error(`Unknow error ${formatError(err)}`)
84
- program.error('')
85
- }
86
- outro('Done ✅')
87
- exit()
88
- }