@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.
@@ -1,6 +1,6 @@
1
- import process from 'node:process'
1
+ import { exit } from 'node:process'
2
2
  import { program } from 'commander'
3
- import * as p from '@clack/prompts'
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
- p.intro(`List current bundle`)
23
+ intro(`List current bundle`)
24
24
 
25
25
  options.apikey = options.apikey || findSavedKey(quiet)
26
- const config = await getConfig()
27
- appId = appId || config?.app?.appId
26
+ const extConfig = await getConfig()
27
+ appId = appId || extConfig?.config?.appId
28
28
 
29
29
  if (!options.apikey) {
30
- p.log.error('Missing API key, you need to provide a API key to upload your bundle')
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
- p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
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
- p.log.error(`Please provide a channel to get the bundle from.`)
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
- p.log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`)
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
- p.log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`)
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
- p.log.info(`Current bundle for channel ${channel} is ${version.name}`)
67
+ log.info(`Current bundle for channel ${channel} is ${version.name}`)
68
68
  else
69
- p.log.info(version.name)
69
+ log.info(version.name)
70
70
 
71
- process.exit()
71
+ exit()
72
72
  }
@@ -1,24 +1,24 @@
1
- import process from 'node:process'
1
+ import { exit } from 'node:process'
2
2
  import { program } from 'commander'
3
- import * as p from '@clack/prompts'
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
- p.intro(`Delete channel`)
10
+ intro(`Delete channel`)
11
11
  options.apikey = options.apikey || findSavedKey()
12
- const config = await getConfig()
13
- appId = appId || config?.app?.appId
12
+ const extConfig = await getConfig()
13
+ appId = appId || extConfig?.config?.appId
14
14
  const snag = useLogSnag()
15
15
 
16
16
  if (!options.apikey) {
17
- p.log.error('Missing API key, you need to provide a API key to upload your bundle')
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
- p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
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
- p.log.info(`Deleting channel ${appId}#${channelId} from Capgo`)
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
- p.log.error(`Cannot delete Channel 🙀 ${formatError(deleteStatus.error)}`)
34
+ log.error(`Cannot delete Channel 🙀 ${formatError(deleteStatus.error)}`)
35
35
  program.error('')
36
36
  }
37
37
  const orgId = await getOrganizationId(supabase, appId)
38
- p.log.success(`Channel deleted`)
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
- p.log.error(`Cannot delete Channel 🙀`)
53
+ log.error(`Cannot delete Channel 🙀`)
54
54
  }
55
- p.outro(`Done ✅`)
56
- process.exit()
55
+ outro(`Done ✅`)
56
+ exit()
57
57
  }
@@ -1,23 +1,23 @@
1
- import process from 'node:process'
1
+ import { exit } from 'node:process'
2
2
  import { program } from 'commander'
3
- import * as p from '@clack/prompts'
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
- p.intro(`List channels`)
10
+ intro(`List channels`)
11
11
  options.apikey = options.apikey || findSavedKey()
12
- const config = await getConfig()
13
- appId = appId || config?.app?.appId
12
+ const extConfig = await getConfig()
13
+ appId = appId || extConfig?.config?.appId
14
14
  const snag = useLogSnag()
15
15
 
16
16
  if (!options.apikey)
17
- p.log.error('Missing API key, you need to provide a API key to upload your bundle')
17
+ log.error('Missing API key, you need to provide a API key to upload your bundle')
18
18
 
19
19
  if (!appId) {
20
- p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
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
- p.log.info(`Querying available channels in Capgo`)
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
- p.log.info(`Active channels in Capgo: ${allVersions?.length}`)
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
- p.outro(`Done ✅`)
48
- process.exit()
47
+ outro(`Done ✅`)
48
+ exit()
49
49
  }
@@ -1,6 +1,6 @@
1
- import process from 'node:process'
1
+ import { exit } from 'node:process'
2
2
  import { program } from 'commander'
3
- import * as p from '@clack/prompts'
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
- p.intro(`Set channel`)
40
+ intro(`Set channel`)
40
41
  options.apikey = options.apikey || findSavedKey()
41
- const config = await getConfig()
42
- appId = appId || config?.app?.appId
42
+ const extConfig = await getConfig()
43
+ appId = appId || extConfig?.config?.appId
43
44
  const snag = useLogSnag()
44
45
 
45
46
  if (!options.apikey) {
46
- p.log.error('Missing API key, you need to provide a API key to upload your bundle')
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
- p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
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
- p.log.error('Missing argument, you need to provide a channel')
63
+ log.error('Missing argument, you need to provide a channel')
63
64
  program.error('')
64
65
  }
65
66
  if (latest && bundle) {
66
- p.log.error('Cannot set latest and bundle at the same time')
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
- p.log.error('Missing argument, you need to provide a option to set')
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 bundleVersion = latest ? config?.app?.package?.version : bundle
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
- p.log.error(`Cannot find version ${bundleVersion}`)
105
+ log.error(`Cannot find version ${bundleVersion}`)
104
106
  program.error('')
105
107
  }
106
- p.log.info(`Set ${appId} channel: ${channel} to @${bundleVersion}`)
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
- p.log.info(`Set ${appId} channel: ${channel} to public or private is deprecated, use default or normal instead`)
113
+ log.info(`Set ${appId} channel: ${channel} to public or private is deprecated, use default or normal instead`)
112
114
 
113
- p.log.info(`Set ${appId} channel: ${channel} to ${state === 'public' || state === 'default' ? 'default' : 'normal'}`)
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
- p.log.info(`Set ${appId} channel: ${channel} to ${downgrade ? 'allow' : 'disallow'} downgrade`)
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
- p.log.info(`Set ${appId} channel: ${channel} to ${ios ? 'allow' : 'disallow'} ios update`)
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
- p.log.info(`Set ${appId} channel: ${channel} to ${android ? 'allow' : 'disallow'} android update`)
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
- p.log.info(`Set ${appId} channel: ${channel} to ${selfAssign ? 'allow' : 'disallow'} self assign to this channel`)
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
- p.log.error(`Channel strategy ${finalDisableAutoUpdate} is not known. The possible values are: ${disableAutoUpdatesPossibleOptions.join(', ')}.`)
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
- p.log.info(`Set ${appId} channel: ${channel} to ${finalDisableAutoUpdate} disable update strategy to this channel`)
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
- p.log.error(`Cannot set channel the upload key is not allowed to do that, use the "all" for this.`)
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
- p.log.error(`Cannot set channel the upload key is not allowed to do that, use the "all" for this.`)
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
- p.log.error(`Unknow error ${formatError(err)}`)
174
+ log.error(`Unknow error ${formatError(err)}`)
173
175
  program.error('')
174
176
  }
175
- p.outro(`Done ✅`)
176
- process.exit()
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 AWS S3 bucket')
118
- .option('--s3-apikey <apikey>', 'apikey for your AWS S3 account')
119
- .option('--s3-apisecret <apisecret>', 'api secret for your AWS S3 account')
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')