@capgo/cli 4.12.12 → 4.12.14-beta.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.
- package/.github/workflows/autofix.yml +25 -0
- package/CHANGELOG.md +9 -0
- package/bun.lockb +0 -0
- package/bunfig.toml +2 -0
- package/dist/index.js +40559 -50832
- package/package.json +22 -24
- 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 +13 -13
- package/src/bundle/list.ts +11 -12
- package/src/bundle/unlink.ts +15 -13
- package/src/bundle/upload.ts +93 -86
- 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/index.ts +6 -3
- 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
- package/vercel-ncc.js +18 -0
|
@@ -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/index.ts
CHANGED
|
@@ -114,10 +114,13 @@ bundle
|
|
|
114
114
|
.option('-c, --channel <channel>', 'channel to link to')
|
|
115
115
|
.option('-e, --external <url>', 'link to external url intead of upload to Capgo Cloud')
|
|
116
116
|
.option('--iv-session-key <key>', 'Set the iv and session key for bundle url external')
|
|
117
|
-
.option('--s3-region <region>', 'Region for your
|
|
118
|
-
.option('--s3-apikey <apikey>', '
|
|
119
|
-
.option('--s3-apisecret <apisecret>', '
|
|
117
|
+
.option('--s3-region <region>', 'Region for your S3 bucket')
|
|
118
|
+
.option('--s3-apikey <apikey>', 'Apikey for your S3 endpoint')
|
|
119
|
+
.option('--s3-apisecret <apisecret>', 'Api secret for your S3 endpoint')
|
|
120
|
+
.option('--s3-endoint <s3Endpoint>', 'Url of S3 endpoint')
|
|
120
121
|
.option('--s3-bucket-name <bucketName>', 'Name for your AWS S3 bucket')
|
|
122
|
+
.option('--s3-port <port>', 'Port for your S3 endpoint')
|
|
123
|
+
.option('--no-s3-ssl', 'Disable SSL for S3 upload')
|
|
121
124
|
.option('--key <key>', 'custom path for public signing key')
|
|
122
125
|
.option('--key-data <keyData>', 'base64 public signing key')
|
|
123
126
|
.option('--bundle-url', 'prints bundle url into stdout')
|