@capgo/cli 4.12.14 → 4.13.2
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 +36 -0
- package/build.mjs +3 -5
- package/bun.lockb +0 -0
- package/dist/index.js +183 -92196
- package/package.json +20 -22
- package/src/api/app.ts +5 -5
- package/src/api/channels.ts +12 -12
- package/src/api/devices_override.ts +8 -8
- package/src/api/update.ts +2 -2
- package/src/api/versions.ts +10 -9
- package/src/app/add.ts +21 -22
- package/src/app/debug.ts +53 -54
- package/src/app/delete.ts +20 -20
- package/src/app/info.ts +34 -24
- package/src/app/list.ts +11 -11
- package/src/app/set.ts +16 -16
- package/src/bundle/check.ts +10 -10
- package/src/bundle/cleanup.ts +27 -27
- package/src/bundle/compatibility.ts +8 -8
- package/src/bundle/decrypt.ts +8 -8
- package/src/bundle/delete.ts +14 -15
- package/src/bundle/encrypt.ts +12 -12
- package/src/bundle/list.ts +11 -12
- package/src/bundle/unlink.ts +15 -13
- package/src/bundle/upload.ts +65 -64
- package/src/bundle/zip.ts +23 -21
- package/src/channel/add.ts +14 -14
- package/src/channel/currentBundle.ts +13 -13
- package/src/channel/delete.ts +13 -13
- package/src/channel/list.ts +11 -11
- package/src/channel/set.ts +28 -26
- package/src/config/index.ts +156 -0
- package/src/init.ts +22 -22
- package/src/key.ts +45 -46
- package/src/login.ts +10 -10
- package/src/user/account.ts +3 -3
- package/src/utils.ts +119 -150
- package/tsconfig.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { exit } from 'node:process'
|
|
2
2
|
import { program } from 'commander'
|
|
3
|
-
import
|
|
3
|
+
import { intro, log } from '@clack/prompts'
|
|
4
4
|
import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
|
|
5
5
|
import type { OptionsBase } from '../utils'
|
|
6
6
|
import { OrganizationPerm, createSupabaseClient, findSavedKey, getConfig, verifyUser } from '../utils'
|
|
@@ -20,18 +20,18 @@ export async function currentBundle(channel: string, appId: string, options: Opt
|
|
|
20
20
|
const { quiet } = options
|
|
21
21
|
|
|
22
22
|
if (!quiet)
|
|
23
|
-
|
|
23
|
+
intro(`List current bundle`)
|
|
24
24
|
|
|
25
25
|
options.apikey = options.apikey || findSavedKey(quiet)
|
|
26
|
-
const
|
|
27
|
-
appId = appId || config?.
|
|
26
|
+
const extConfig = await getConfig()
|
|
27
|
+
appId = appId || extConfig?.config?.appId
|
|
28
28
|
|
|
29
29
|
if (!options.apikey) {
|
|
30
|
-
|
|
30
|
+
log.error('Missing API key, you need to provide a API key to upload your bundle')
|
|
31
31
|
program.error('')
|
|
32
32
|
}
|
|
33
33
|
if (!appId) {
|
|
34
|
-
|
|
34
|
+
log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
|
|
35
35
|
program.error('')
|
|
36
36
|
}
|
|
37
37
|
const supabase = await createSupabaseClient(options.apikey)
|
|
@@ -41,7 +41,7 @@ export async function currentBundle(channel: string, appId: string, options: Opt
|
|
|
41
41
|
await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.read)
|
|
42
42
|
|
|
43
43
|
if (!channel) {
|
|
44
|
-
|
|
44
|
+
log.error(`Please provide a channel to get the bundle from.`)
|
|
45
45
|
program.error('')
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -53,20 +53,20 @@ export async function currentBundle(channel: string, appId: string, options: Opt
|
|
|
53
53
|
.limit(1)
|
|
54
54
|
|
|
55
55
|
if (error || supabaseChannel.length === 0) {
|
|
56
|
-
|
|
56
|
+
log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`)
|
|
57
57
|
program.error('')
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
const { version } = supabaseChannel[0] as any as Channel
|
|
61
61
|
if (!version) {
|
|
62
|
-
|
|
62
|
+
log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`)
|
|
63
63
|
program.error('')
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
if (!quiet)
|
|
67
|
-
|
|
67
|
+
log.info(`Current bundle for channel ${channel} is ${version.name}`)
|
|
68
68
|
else
|
|
69
|
-
|
|
69
|
+
log.info(version.name)
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
exit()
|
|
72
72
|
}
|
package/src/channel/delete.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { exit } from 'node:process'
|
|
2
2
|
import { program } from 'commander'
|
|
3
|
-
import
|
|
3
|
+
import { intro, log, outro } from '@clack/prompts'
|
|
4
4
|
import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
|
|
5
5
|
import { delChannel } from '../api/channels'
|
|
6
6
|
import type { OptionsBase } from '../utils'
|
|
7
7
|
import { OrganizationPerm, createSupabaseClient, findSavedKey, formatError, getConfig, getOrganizationId, useLogSnag, verifyUser } from '../utils'
|
|
8
8
|
|
|
9
9
|
export async function deleteChannel(channelId: string, appId: string, options: OptionsBase) {
|
|
10
|
-
|
|
10
|
+
intro(`Delete channel`)
|
|
11
11
|
options.apikey = options.apikey || findSavedKey()
|
|
12
|
-
const
|
|
13
|
-
appId = appId || config?.
|
|
12
|
+
const extConfig = await getConfig()
|
|
13
|
+
appId = appId || extConfig?.config?.appId
|
|
14
14
|
const snag = useLogSnag()
|
|
15
15
|
|
|
16
16
|
if (!options.apikey) {
|
|
17
|
-
|
|
17
|
+
log.error('Missing API key, you need to provide a API key to upload your bundle')
|
|
18
18
|
program.error('')
|
|
19
19
|
}
|
|
20
20
|
if (!appId) {
|
|
21
|
-
|
|
21
|
+
log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
|
|
22
22
|
program.error('')
|
|
23
23
|
}
|
|
24
24
|
const supabase = await createSupabaseClient(options.apikey)
|
|
@@ -27,15 +27,15 @@ export async function deleteChannel(channelId: string, appId: string, options: O
|
|
|
27
27
|
// Check we have app access to this appId
|
|
28
28
|
await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.admin)
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
log.info(`Deleting channel ${appId}#${channelId} from Capgo`)
|
|
31
31
|
try {
|
|
32
32
|
const deleteStatus = await delChannel(supabase, channelId, appId, userId)
|
|
33
33
|
if (deleteStatus.error) {
|
|
34
|
-
|
|
34
|
+
log.error(`Cannot delete Channel 🙀 ${formatError(deleteStatus.error)}`)
|
|
35
35
|
program.error('')
|
|
36
36
|
}
|
|
37
37
|
const orgId = await getOrganizationId(supabase, appId)
|
|
38
|
-
|
|
38
|
+
log.success(`Channel deleted`)
|
|
39
39
|
await snag.track({
|
|
40
40
|
channel: 'channel',
|
|
41
41
|
event: 'Delete channel',
|
|
@@ -50,8 +50,8 @@ export async function deleteChannel(channelId: string, appId: string, options: O
|
|
|
50
50
|
}).catch()
|
|
51
51
|
}
|
|
52
52
|
catch (error) {
|
|
53
|
-
|
|
53
|
+
log.error(`Cannot delete Channel 🙀`)
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
outro(`Done ✅`)
|
|
56
|
+
exit()
|
|
57
57
|
}
|
package/src/channel/list.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { exit } from 'node:process'
|
|
2
2
|
import { program } from 'commander'
|
|
3
|
-
import
|
|
3
|
+
import { intro, log, outro } from '@clack/prompts'
|
|
4
4
|
import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
|
|
5
5
|
import { displayChannels, getActiveChannels } from '../api/channels'
|
|
6
6
|
import type { OptionsBase } from '../utils'
|
|
7
7
|
import { OrganizationPerm, createSupabaseClient, findSavedKey, getConfig, useLogSnag, verifyUser } from '../utils'
|
|
8
8
|
|
|
9
9
|
export async function listChannels(appId: string, options: OptionsBase) {
|
|
10
|
-
|
|
10
|
+
intro(`List channels`)
|
|
11
11
|
options.apikey = options.apikey || findSavedKey()
|
|
12
|
-
const
|
|
13
|
-
appId = appId || config?.
|
|
12
|
+
const extConfig = await getConfig()
|
|
13
|
+
appId = appId || extConfig?.config?.appId
|
|
14
14
|
const snag = useLogSnag()
|
|
15
15
|
|
|
16
16
|
if (!options.apikey)
|
|
17
|
-
|
|
17
|
+
log.error('Missing API key, you need to provide a API key to upload your bundle')
|
|
18
18
|
|
|
19
19
|
if (!appId) {
|
|
20
|
-
|
|
20
|
+
log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
|
|
21
21
|
program.error('')
|
|
22
22
|
}
|
|
23
23
|
const supabase = await createSupabaseClient(options.apikey)
|
|
@@ -26,12 +26,12 @@ export async function listChannels(appId: string, options: OptionsBase) {
|
|
|
26
26
|
// Check we have app access to this appId
|
|
27
27
|
await checkAppExistsAndHasPermissionOrgErr(supabase, options.apikey, appId, OrganizationPerm.read)
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
log.info(`Querying available channels in Capgo`)
|
|
30
30
|
|
|
31
31
|
// Get all active app versions we might possibly be able to cleanup
|
|
32
32
|
const allVersions = await getActiveChannels(supabase, appId)
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
log.info(`Active channels in Capgo: ${allVersions?.length}`)
|
|
35
35
|
|
|
36
36
|
displayChannels(allVersions)
|
|
37
37
|
await snag.track({
|
|
@@ -44,6 +44,6 @@ export async function listChannels(appId: string, options: OptionsBase) {
|
|
|
44
44
|
},
|
|
45
45
|
notify: false,
|
|
46
46
|
}).catch()
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
outro(`Done ✅`)
|
|
48
|
+
exit()
|
|
49
49
|
}
|
package/src/channel/set.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { exit } from 'node:process'
|
|
2
2
|
import { program } from 'commander'
|
|
3
|
-
import
|
|
3
|
+
import { intro, log, outro } from '@clack/prompts'
|
|
4
4
|
import type { Database } from '../types/supabase.types'
|
|
5
5
|
import { checkAppExistsAndHasPermissionOrgErr } from '../api/app'
|
|
6
6
|
import type {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
formatError,
|
|
15
15
|
getConfig,
|
|
16
16
|
getOrganizationId,
|
|
17
|
+
readPackageJson,
|
|
17
18
|
updateOrCreateChannel,
|
|
18
19
|
useLogSnag,
|
|
19
20
|
verifyUser,
|
|
@@ -36,18 +37,18 @@ interface Options extends OptionsBase {
|
|
|
36
37
|
const disableAutoUpdatesPossibleOptions = ['major', 'minor', 'metadata', 'patch', 'none']
|
|
37
38
|
|
|
38
39
|
export async function setChannel(channel: string, appId: string, options: Options) {
|
|
39
|
-
|
|
40
|
+
intro(`Set channel`)
|
|
40
41
|
options.apikey = options.apikey || findSavedKey()
|
|
41
|
-
const
|
|
42
|
-
appId = appId || config?.
|
|
42
|
+
const extConfig = await getConfig()
|
|
43
|
+
appId = appId || extConfig?.config?.appId
|
|
43
44
|
const snag = useLogSnag()
|
|
44
45
|
|
|
45
46
|
if (!options.apikey) {
|
|
46
|
-
|
|
47
|
+
log.error('Missing API key, you need to provide a API key to upload your bundle')
|
|
47
48
|
program.error('')
|
|
48
49
|
}
|
|
49
50
|
if (!appId) {
|
|
50
|
-
|
|
51
|
+
log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
|
|
51
52
|
program.error('')
|
|
52
53
|
}
|
|
53
54
|
const supabase = await createSupabaseClient(options.apikey)
|
|
@@ -59,11 +60,11 @@ export async function setChannel(channel: string, appId: string, options: Option
|
|
|
59
60
|
|
|
60
61
|
const { bundle, state, downgrade, latest, upgrade, ios, android, selfAssign, disableAutoUpdate, dev, emulator } = options
|
|
61
62
|
if (!channel) {
|
|
62
|
-
|
|
63
|
+
log.error('Missing argument, you need to provide a channel')
|
|
63
64
|
program.error('')
|
|
64
65
|
}
|
|
65
66
|
if (latest && bundle) {
|
|
66
|
-
|
|
67
|
+
log.error('Cannot set latest and bundle at the same time')
|
|
67
68
|
program.error('')
|
|
68
69
|
}
|
|
69
70
|
if (bundle == null
|
|
@@ -77,7 +78,7 @@ export async function setChannel(channel: string, appId: string, options: Option
|
|
|
77
78
|
&& dev == null
|
|
78
79
|
&& emulator == null
|
|
79
80
|
&& disableAutoUpdate == null) {
|
|
80
|
-
|
|
81
|
+
log.error('Missing argument, you need to provide a option to set')
|
|
81
82
|
program.error('')
|
|
82
83
|
}
|
|
83
84
|
try {
|
|
@@ -89,7 +90,8 @@ export async function setChannel(channel: string, appId: string, options: Option
|
|
|
89
90
|
owner_org: orgId,
|
|
90
91
|
version: undefined as any,
|
|
91
92
|
}
|
|
92
|
-
const
|
|
93
|
+
const pack = await readPackageJson()
|
|
94
|
+
const bundleVersion = latest ? pack?.version : bundle
|
|
93
95
|
if (bundleVersion != null) {
|
|
94
96
|
const { data, error: vError } = await supabase
|
|
95
97
|
.from('app_versions')
|
|
@@ -100,33 +102,33 @@ export async function setChannel(channel: string, appId: string, options: Option
|
|
|
100
102
|
.eq('deleted', false)
|
|
101
103
|
.single()
|
|
102
104
|
if (vError || !data) {
|
|
103
|
-
|
|
105
|
+
log.error(`Cannot find version ${bundleVersion}`)
|
|
104
106
|
program.error('')
|
|
105
107
|
}
|
|
106
|
-
|
|
108
|
+
log.info(`Set ${appId} channel: ${channel} to @${bundleVersion}`)
|
|
107
109
|
channelPayload.version = data.id
|
|
108
110
|
}
|
|
109
111
|
if (state != null) {
|
|
110
112
|
if (state === 'public' || state === 'private')
|
|
111
|
-
|
|
113
|
+
log.info(`Set ${appId} channel: ${channel} to public or private is deprecated, use default or normal instead`)
|
|
112
114
|
|
|
113
|
-
|
|
115
|
+
log.info(`Set ${appId} channel: ${channel} to ${state === 'public' || state === 'default' ? 'default' : 'normal'}`)
|
|
114
116
|
channelPayload.public = state === 'public' || state === 'default'
|
|
115
117
|
}
|
|
116
118
|
if (downgrade != null) {
|
|
117
|
-
|
|
119
|
+
log.info(`Set ${appId} channel: ${channel} to ${downgrade ? 'allow' : 'disallow'} downgrade`)
|
|
118
120
|
channelPayload.disableAutoUpdateUnderNative = !downgrade
|
|
119
121
|
}
|
|
120
122
|
if (ios != null) {
|
|
121
|
-
|
|
123
|
+
log.info(`Set ${appId} channel: ${channel} to ${ios ? 'allow' : 'disallow'} ios update`)
|
|
122
124
|
channelPayload.ios = !!ios
|
|
123
125
|
}
|
|
124
126
|
if (android != null) {
|
|
125
|
-
|
|
127
|
+
log.info(`Set ${appId} channel: ${channel} to ${android ? 'allow' : 'disallow'} android update`)
|
|
126
128
|
channelPayload.android = !!android
|
|
127
129
|
}
|
|
128
130
|
if (selfAssign != null) {
|
|
129
|
-
|
|
131
|
+
log.info(`Set ${appId} channel: ${channel} to ${selfAssign ? 'allow' : 'disallow'} self assign to this channel`)
|
|
130
132
|
channelPayload.allow_device_self_set = !!selfAssign
|
|
131
133
|
}
|
|
132
134
|
if (disableAutoUpdate != null) {
|
|
@@ -134,7 +136,7 @@ export async function setChannel(channel: string, appId: string, options: Option
|
|
|
134
136
|
|
|
135
137
|
// The user passed an unimplemented strategy
|
|
136
138
|
if (!disableAutoUpdatesPossibleOptions.includes(finalDisableAutoUpdate)) {
|
|
137
|
-
|
|
139
|
+
log.error(`Channel strategy ${finalDisableAutoUpdate} is not known. The possible values are: ${disableAutoUpdatesPossibleOptions.join(', ')}.`)
|
|
138
140
|
program.error('')
|
|
139
141
|
}
|
|
140
142
|
|
|
@@ -144,17 +146,17 @@ export async function setChannel(channel: string, appId: string, options: Option
|
|
|
144
146
|
|
|
145
147
|
// This cast is safe, look above
|
|
146
148
|
channelPayload.disableAutoUpdate = finalDisableAutoUpdate as any
|
|
147
|
-
|
|
149
|
+
log.info(`Set ${appId} channel: ${channel} to ${finalDisableAutoUpdate} disable update strategy to this channel`)
|
|
148
150
|
}
|
|
149
151
|
try {
|
|
150
152
|
const { error: dbError } = await updateOrCreateChannel(supabase, channelPayload)
|
|
151
153
|
if (dbError) {
|
|
152
|
-
|
|
154
|
+
log.error(`Cannot set channel the upload key is not allowed to do that, use the "all" for this.`)
|
|
153
155
|
program.error('')
|
|
154
156
|
}
|
|
155
157
|
}
|
|
156
158
|
catch (e) {
|
|
157
|
-
|
|
159
|
+
log.error(`Cannot set channel the upload key is not allowed to do that, use the "all" for this.`)
|
|
158
160
|
program.error('')
|
|
159
161
|
}
|
|
160
162
|
await snag.track({
|
|
@@ -169,9 +171,9 @@ export async function setChannel(channel: string, appId: string, options: Option
|
|
|
169
171
|
}).catch()
|
|
170
172
|
}
|
|
171
173
|
catch (err) {
|
|
172
|
-
|
|
174
|
+
log.error(`Unknow error ${formatError(err)}`)
|
|
173
175
|
program.error('')
|
|
174
176
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
+
outro(`Done ✅`)
|
|
178
|
+
exit()
|
|
177
179
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { resolve } from 'node:path'
|
|
2
|
+
import { cwd } from 'node:process'
|
|
3
|
+
import { accessSync, constants, readFileSync, writeFileSync } from 'node:fs'
|
|
4
|
+
|
|
5
|
+
export const CONFIG_FILE_NAME_TS = 'capacitor.config.ts'
|
|
6
|
+
export const CONFIG_FILE_NAME_JSON = 'capacitor.config.json'
|
|
7
|
+
|
|
8
|
+
export interface CapacitorConfig {
|
|
9
|
+
appId: string
|
|
10
|
+
appName: string
|
|
11
|
+
webDir: string
|
|
12
|
+
plugins?: Record<string, any>
|
|
13
|
+
android?: Record<string, any>
|
|
14
|
+
[key: string]: any
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ExtConfigPairs {
|
|
18
|
+
config: CapacitorConfig
|
|
19
|
+
path: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function parseConfigObject(configString: string): CapacitorConfig {
|
|
23
|
+
// Remove comments
|
|
24
|
+
const noComments = configString.replace(/\/\/.*|\/\*[\s\S]*?\*\//g, '')
|
|
25
|
+
|
|
26
|
+
// Parse the object
|
|
27
|
+
// eslint-disable-next-line no-new-func
|
|
28
|
+
const configObject = Function(`return ${noComments.trim()}`)()
|
|
29
|
+
|
|
30
|
+
return configObject as CapacitorConfig
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function loadConfigTs(content: string): CapacitorConfig {
|
|
34
|
+
const configRegex = /const\s+config\s*:\s*CapacitorConfig\s*=\s*(\{[\s\S]*?\n\})/
|
|
35
|
+
const match = content.match(configRegex)
|
|
36
|
+
|
|
37
|
+
if (!match) {
|
|
38
|
+
throw new Error('Unable to find config object in TypeScript file')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return parseConfigObject(match[1])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function loadConfigJson(content: string): CapacitorConfig {
|
|
45
|
+
return JSON.parse(content) as CapacitorConfig
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function loadConfig(): Promise<ExtConfigPairs | undefined> {
|
|
49
|
+
const appRootDir = cwd()
|
|
50
|
+
const extConfigFilePathTS = resolve(appRootDir, CONFIG_FILE_NAME_TS)
|
|
51
|
+
const extConfigFilePathJSON = resolve(appRootDir, CONFIG_FILE_NAME_JSON)
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
accessSync(extConfigFilePathTS, constants.R_OK)
|
|
55
|
+
const configContentTS = readFileSync(extConfigFilePathTS, 'utf-8')
|
|
56
|
+
return {
|
|
57
|
+
config: loadConfigTs(configContentTS),
|
|
58
|
+
path: extConfigFilePathTS,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
try {
|
|
63
|
+
accessSync(extConfigFilePathJSON, constants.R_OK)
|
|
64
|
+
const configContentJSON = readFileSync(extConfigFilePathJSON, 'utf-8')
|
|
65
|
+
return {
|
|
66
|
+
config: loadConfigJson(configContentJSON),
|
|
67
|
+
path: extConfigFilePathJSON,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
console.error('Cannot find capacitor.config.ts or capacitor.config.json')
|
|
72
|
+
return undefined
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function writeConfig(config: ExtConfigPairs): Promise<void> {
|
|
78
|
+
const { config: newConfig, path } = config
|
|
79
|
+
const content = readFileSync(path, 'utf-8')
|
|
80
|
+
|
|
81
|
+
const updatedContent = path.endsWith('.json')
|
|
82
|
+
? updateJsonContent(content, newConfig)
|
|
83
|
+
: updateTsContent(content, newConfig)
|
|
84
|
+
|
|
85
|
+
writeFileSync(path, updatedContent)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function updateJsonContent(content: string, newConfig: CapacitorConfig): string {
|
|
89
|
+
const jsonObj = JSON.parse(content)
|
|
90
|
+
if (!jsonObj.plugins)
|
|
91
|
+
jsonObj.plugins = {}
|
|
92
|
+
if (!jsonObj.plugins.CapacitorUpdater)
|
|
93
|
+
jsonObj.plugins.CapacitorUpdater = {}
|
|
94
|
+
|
|
95
|
+
Object.assign(jsonObj.plugins.CapacitorUpdater, newConfig.plugins?.CapacitorUpdater)
|
|
96
|
+
|
|
97
|
+
return JSON.stringify(jsonObj, null, detectIndentation(content))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function updateTsContent(content: string, newConfig: CapacitorConfig): string {
|
|
101
|
+
const capUpdaterRegex = /(\bCapacitorUpdater\s*:\s*\{[^}]*\})/g
|
|
102
|
+
|
|
103
|
+
return content.replace(capUpdaterRegex, (match) => {
|
|
104
|
+
const updatedSection = updateCapacitorUpdaterSection(match, newConfig.plugins?.CapacitorUpdater || {})
|
|
105
|
+
return updatedSection
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function updateCapacitorUpdaterSection(section: string, newConfig: Record<string, any>): string {
|
|
110
|
+
const lines = section.split('\n')
|
|
111
|
+
const updatedLines = lines.map((line) => {
|
|
112
|
+
// eslint-disable-next-line regexp/no-super-linear-backtracking
|
|
113
|
+
const keyValueMatch = line.match(/^\s*(\w+)\s*:\s*(.+?),?\s*$/)
|
|
114
|
+
if (keyValueMatch) {
|
|
115
|
+
const [, key] = keyValueMatch
|
|
116
|
+
if (key in newConfig) {
|
|
117
|
+
const newValue = formatValue(newConfig[key])
|
|
118
|
+
return line.replace(/:\s*.+/, `: ${newValue},`)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return line
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
// Add new properties
|
|
125
|
+
Object.entries(newConfig).forEach(([key, value]) => {
|
|
126
|
+
if (!updatedLines.some(line => line.includes(`${key}:`))) {
|
|
127
|
+
const indent = detectIndentation(section)
|
|
128
|
+
updatedLines.splice(-1, 0, `${' '.repeat(indent)}${key}: ${formatValue(value)},`)
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
return updatedLines.join('\n')
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function formatValue(value: any): string {
|
|
136
|
+
if (typeof value === 'string') {
|
|
137
|
+
// Convert multiline strings (like RSA keys) to single line
|
|
138
|
+
if (value.includes('\n')) {
|
|
139
|
+
return `'${value.replace(/\n/g, '\\n')}'`
|
|
140
|
+
}
|
|
141
|
+
return `'${value}'`
|
|
142
|
+
}
|
|
143
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
144
|
+
return String(value)
|
|
145
|
+
if (Array.isArray(value))
|
|
146
|
+
return `[${value.map(formatValue).join(', ')}]`
|
|
147
|
+
if (typeof value === 'object' && value !== null) {
|
|
148
|
+
return `{ ${Object.entries(value).map(([k, v]) => `${k}: ${formatValue(v)}`).join(', ')} }`
|
|
149
|
+
}
|
|
150
|
+
return 'null'
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function detectIndentation(content: string): number {
|
|
154
|
+
const match = content.match(/^( +)/m)
|
|
155
|
+
return match ? match[1].length : 2
|
|
156
|
+
}
|
package/src/init.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
|
-
import path from 'node:path'
|
|
3
2
|
import type { ExecSyncOptions } from 'node:child_process'
|
|
4
3
|
import { execSync, spawnSync } from 'node:child_process'
|
|
5
|
-
import
|
|
4
|
+
import { exit } from 'node:process'
|
|
5
|
+
import { join } from 'node:path'
|
|
6
6
|
import * as p from '@clack/prompts'
|
|
7
7
|
import type LogSnag from 'logsnag'
|
|
8
8
|
import semver from 'semver'
|
|
@@ -16,7 +16,7 @@ import { addAppInternal } from './app/add'
|
|
|
16
16
|
import { checkLatest } from './api/update'
|
|
17
17
|
import type { Options } from './api/app'
|
|
18
18
|
import type { Organization } from './utils'
|
|
19
|
-
import { convertAppName, createSupabaseClient, findBuildCommandForProjectType, findMainFile, findMainFileForProjectType, findProjectType, findSavedKey, getConfig, getOrganization, getPMAndCommand, useLogSnag, verifyUser } from './utils'
|
|
19
|
+
import { convertAppName, createSupabaseClient, findBuildCommandForProjectType, findMainFile, findMainFileForProjectType, findProjectType, findSavedKey, getConfig, getOrganization, getPMAndCommand, readPackageJson, useLogSnag, verifyUser } from './utils'
|
|
20
20
|
|
|
21
21
|
interface SuperOptions extends Options {
|
|
22
22
|
local: boolean
|
|
@@ -88,7 +88,7 @@ function cleanupStepsDone() {
|
|
|
88
88
|
async function cancelCommand(command: boolean | symbol, orgId: string, snag: LogSnag) {
|
|
89
89
|
if (p.isCancel(command)) {
|
|
90
90
|
await markSnag('onboarding-v2', orgId, snag, 'canceled', '🤷')
|
|
91
|
-
|
|
91
|
+
exit()
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
@@ -148,20 +148,20 @@ async function step4(orgId: string, snag: LogSnag, apikey: string, appId: string
|
|
|
148
148
|
const s = p.spinner()
|
|
149
149
|
s.start(`Checking if @capgo/capacitor-updater is installed`)
|
|
150
150
|
let versionToInstall = 'latest'
|
|
151
|
-
const pack =
|
|
151
|
+
const pack = await readPackageJson()
|
|
152
152
|
let coreVersion = pack.dependencies['@capacitor/core'] || pack.devDependencies['@capacitor/core']
|
|
153
153
|
coreVersion = coreVersion?.replace('^', '').replace('~', '')
|
|
154
154
|
if (!coreVersion) {
|
|
155
155
|
s.stop('Error')
|
|
156
156
|
p.log.warn(`Cannot find @capacitor/core in package.json, please run \`capgo init\` in a capacitor project`)
|
|
157
157
|
p.outro(`Bye 👋`)
|
|
158
|
-
|
|
158
|
+
exit()
|
|
159
159
|
}
|
|
160
160
|
else if (semver.lt(coreVersion, '5.0.0')) {
|
|
161
161
|
s.stop('Error')
|
|
162
162
|
p.log.warn(`@capacitor/core version is ${coreVersion}, please update to Capacitor v5 first: ${urlMigrateV5}`)
|
|
163
163
|
p.outro(`Bye 👋`)
|
|
164
|
-
|
|
164
|
+
exit()
|
|
165
165
|
}
|
|
166
166
|
else if (semver.lt(coreVersion, '6.0.0')) {
|
|
167
167
|
s.stop(`@capacitor/core version is ${coreVersion}, please update to Capacitor v6: ${urlMigrateV6} to access the best features of Capgo`)
|
|
@@ -171,7 +171,7 @@ async function step4(orgId: string, snag: LogSnag, apikey: string, appId: string
|
|
|
171
171
|
s.stop('Error')
|
|
172
172
|
p.log.warn(`Cannot reconize package manager, please run \`capgo init\` in a capacitor project with npm, pnpm, bun or yarn`)
|
|
173
173
|
p.outro(`Bye 👋`)
|
|
174
|
-
|
|
174
|
+
exit()
|
|
175
175
|
}
|
|
176
176
|
// // use pm to install capgo
|
|
177
177
|
// // run command pm install @capgo/capacitor-updater@latest
|
|
@@ -201,16 +201,16 @@ async function step5(orgId: string, snag: LogSnag, apikey: string, appId: string
|
|
|
201
201
|
const projectType = await findProjectType()
|
|
202
202
|
if (projectType === 'nuxtjs-js' || projectType === 'nuxtjs-ts') {
|
|
203
203
|
// Nuxt.js specific logic
|
|
204
|
-
const nuxtDir =
|
|
204
|
+
const nuxtDir = join('plugins')
|
|
205
205
|
if (!existsSync(nuxtDir)) {
|
|
206
206
|
mkdirSync(nuxtDir, { recursive: true })
|
|
207
207
|
}
|
|
208
208
|
let nuxtFilePath
|
|
209
209
|
if (projectType === 'nuxtjs-ts') {
|
|
210
|
-
nuxtFilePath =
|
|
210
|
+
nuxtFilePath = join(nuxtDir, 'capacitorUpdater.client.ts')
|
|
211
211
|
}
|
|
212
212
|
else {
|
|
213
|
-
nuxtFilePath =
|
|
213
|
+
nuxtFilePath = join(nuxtDir, 'capacitorUpdater.client.js')
|
|
214
214
|
}
|
|
215
215
|
const nuxtFileContent = `
|
|
216
216
|
import { CapacitorUpdater } from '@capgo/capacitor-updater'
|
|
@@ -257,7 +257,7 @@ async function step5(orgId: string, snag: LogSnag, apikey: string, appId: string
|
|
|
257
257
|
p.log.warn(`Cannot find the latest version of ${projectType}, you might need to upgrade to the latest version of ${projectType}`)
|
|
258
258
|
}
|
|
259
259
|
p.outro(`Bye 👋`)
|
|
260
|
-
|
|
260
|
+
exit()
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
// Open main file and inject codeInject
|
|
@@ -270,7 +270,7 @@ async function step5(orgId: string, snag: LogSnag, apikey: string, appId: string
|
|
|
270
270
|
s.stop('Error')
|
|
271
271
|
p.log.warn(`Cannot find import line in main file, use manual installation: https://capgo.app/docs/plugin/installation/`)
|
|
272
272
|
p.outro(`Bye 👋`)
|
|
273
|
-
|
|
273
|
+
exit()
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
if (mainFileContent.includes(codeInject)) {
|
|
@@ -302,7 +302,7 @@ async function step6(orgId: string, snag: LogSnag, apikey: string, appId: string
|
|
|
302
302
|
s.stop('Error')
|
|
303
303
|
p.log.warn(`Cannot create key ❌`)
|
|
304
304
|
p.outro(`Bye 👋`)
|
|
305
|
-
|
|
305
|
+
exit(1)
|
|
306
306
|
}
|
|
307
307
|
else {
|
|
308
308
|
s.stop(`key created 🔑`)
|
|
@@ -321,13 +321,13 @@ async function step7(orgId: string, snag: LogSnag, apikey: string, appId: string
|
|
|
321
321
|
const projectType = await findProjectType()
|
|
322
322
|
const buildCommand = await findBuildCommandForProjectType(projectType)
|
|
323
323
|
s.start(`Running: ${pm.pm} run ${buildCommand} && ${pm.runner} cap sync`)
|
|
324
|
-
const pack =
|
|
324
|
+
const pack = await readPackageJson()
|
|
325
325
|
// check in script build exist
|
|
326
326
|
if (!pack.scripts[buildCommand]) {
|
|
327
327
|
s.stop('Error')
|
|
328
328
|
p.log.warn(`Cannot find ${buildCommand} script in package.json, please add it and run \`capgo init\` again`)
|
|
329
329
|
p.outro(`Bye 👋`)
|
|
330
|
-
|
|
330
|
+
exit()
|
|
331
331
|
}
|
|
332
332
|
execSync(`${pm.pm} run ${buildCommand} && ${pm.runner} cap sync`, execOption as ExecSyncOptions)
|
|
333
333
|
s.stop(`Build & Sync Done ✅`)
|
|
@@ -353,7 +353,7 @@ async function step8(orgId: string, snag: LogSnag, apikey: string, appId: string
|
|
|
353
353
|
s.stop('Error')
|
|
354
354
|
p.log.warn(`Upload failed ❌`)
|
|
355
355
|
p.outro(`Bye 👋`)
|
|
356
|
-
|
|
356
|
+
exit()
|
|
357
357
|
}
|
|
358
358
|
else {
|
|
359
359
|
s.stop(`Upload Done ✅`)
|
|
@@ -379,7 +379,7 @@ async function step9(orgId: string, snag: LogSnag) {
|
|
|
379
379
|
})
|
|
380
380
|
if (p.isCancel(plaformType)) {
|
|
381
381
|
p.outro(`Bye 👋`)
|
|
382
|
-
|
|
382
|
+
exit()
|
|
383
383
|
}
|
|
384
384
|
|
|
385
385
|
const platform = plaformType as 'ios' | 'android'
|
|
@@ -413,8 +413,8 @@ export async function initApp(apikeyCommand: string, appId: string, options: Sup
|
|
|
413
413
|
p.intro(`Capgo onboarding 🛫`)
|
|
414
414
|
await checkLatest()
|
|
415
415
|
const snag = useLogSnag()
|
|
416
|
-
const
|
|
417
|
-
appId = appId || config?.
|
|
416
|
+
const extConfig = await getConfig()
|
|
417
|
+
appId = appId || extConfig?.config?.appId
|
|
418
418
|
const apikey = apikeyCommand || findSavedKey()
|
|
419
419
|
|
|
420
420
|
const log = p.spinner()
|
|
@@ -483,7 +483,7 @@ export async function initApp(apikeyCommand: string, appId: string, options: Sup
|
|
|
483
483
|
catch (e) {
|
|
484
484
|
console.error(e)
|
|
485
485
|
p.log.error(`Error during onboarding, please try again later`)
|
|
486
|
-
|
|
486
|
+
exit(1)
|
|
487
487
|
}
|
|
488
488
|
|
|
489
489
|
p.log.info(`Welcome onboard ✈️!`)
|
|
@@ -491,5 +491,5 @@ export async function initApp(apikeyCommand: string, appId: string, options: Sup
|
|
|
491
491
|
p.log.info(`Next time use \`${pm.runner} @capgo/cli@latest bundle upload\` to only upload your bundle`)
|
|
492
492
|
p.log.info(`If you have any issue try to use the debug command \`${pm.runner} @capgo/cli@latest app debug\``)
|
|
493
493
|
p.outro(`Bye 👋`)
|
|
494
|
-
|
|
494
|
+
exit()
|
|
495
495
|
}
|