@capgo/cli 4.0.12 → 4.0.14

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/src/bundle/zip.ts CHANGED
@@ -1,133 +1,137 @@
1
- import { randomUUID } from 'node:crypto';
2
- import { writeFileSync } from 'node:fs';
3
- import AdmZip from 'adm-zip';
4
- import { program } from 'commander';
5
- import * as p from '@clack/prompts';
6
- import { checksum as getChecksum } from '@tomasklaen/checksum';
7
- import { checkLatest } from '../api/update';
1
+ import { randomUUID } from 'node:crypto'
2
+ import { writeFileSync } from 'node:fs'
3
+ import process from 'node:process'
4
+ import AdmZip from 'adm-zip'
5
+ import { program } from 'commander'
6
+ import * as p from '@clack/prompts'
7
+ import { checksum as getChecksum } from '@tomasklaen/checksum'
8
+ import { checkLatest } from '../api/update'
9
+ import type {
10
+ OptionsBase,
11
+ } from '../utils'
8
12
  import {
9
- OptionsBase,
10
- getConfig,
11
- useLogSnag,
12
- regexSemver,
13
- } from '../utils';
14
- import { checkIndexPosition, searchInDirectory } from './check';
13
+ formatError,
14
+ getConfig,
15
+ regexSemver,
16
+ useLogSnag,
17
+ } from '../utils'
18
+ import { checkIndexPosition, searchInDirectory } from './check'
15
19
 
16
- const alertMb = 20;
20
+ const alertMb = 20
17
21
 
18
22
  interface Options extends OptionsBase {
19
- bundle?: string
20
- path?: string
21
- codeCheck?: boolean
22
- name?: string
23
- json?: boolean
23
+ bundle?: string
24
+ path?: string
25
+ codeCheck?: boolean
26
+ name?: string
27
+ json?: boolean
24
28
  }
25
29
 
26
- export const zipBundle = async (appId: string, options: Options) => {
27
- let { bundle, path } = options;
28
- const { json } = options
29
- const snag = useLogSnag()
30
- if (!json)
31
- await checkLatest();
30
+ export async function zipBundle(appId: string, options: Options) {
31
+ let { bundle, path } = options
32
+ const { json } = options
33
+ const snag = useLogSnag()
34
+ if (!json)
35
+ await checkLatest()
32
36
 
33
- const config = await getConfig();
34
- appId = appId || config?.app?.appId
35
- // create bundle name format : 1.0.0-beta.x where x is a uuid
36
- const uuid = randomUUID().split('-')[0];
37
- bundle = bundle || config?.app?.package?.version || `0.0.1-beta.${uuid}`
38
- if (!json)
39
- p.intro(`Zipping ${appId}@${bundle}`);
40
- // check if bundle is valid
41
- if (!regexSemver.test(bundle)) {
42
- if (!json)
43
- p.log.error(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`);
44
- else
45
- console.error(JSON.stringify({ error: 'invalid_semver' }))
46
- program.error('');
47
- }
48
- path = path || config?.app?.webDir
49
- if (!appId || !bundle || !path) {
50
- if (!json)
51
- p.log.error("Missing argument, you need to provide a appId and a bundle and a path, or be in a capacitor project");
52
- else
53
- console.error(JSON.stringify({ error: 'missing_argument' }))
54
- program.error('');
55
- }
56
- if (!json)
57
- p.log.info(`Started from path "${path}"`);
58
- const checkNotifyAppReady = options.codeCheck
59
- if (typeof checkNotifyAppReady === 'undefined' || checkNotifyAppReady) {
60
- const isPluginConfigured = searchInDirectory(path, 'notifyAppReady')
61
- if (!isPluginConfigured) {
62
- if (!json)
63
- p.log.error(`notifyAppReady() is missing in the source code. see: https://capgo.app/docs/plugin/api/#notifyappready`);
64
- else
65
- console.error(JSON.stringify({ error: 'notifyAppReady_not_in_source_code' }))
66
- program.error('');
67
- }
68
- const foundIndex = checkIndexPosition(path);
69
- if (!foundIndex) {
70
- if (!json)
71
- p.log.error(`index.html is missing in the root folder or in the only folder in the root folder`);
72
- else
73
- console.error(JSON.stringify({ error: 'index_html_not_found' }))
74
- program.error('');
75
- }
76
- }
77
- const zip = new AdmZip();
78
- zip.addLocalFolder(path);
79
- const zipped = zip.toBuffer();
80
- if (!json)
81
- p.log.info(`Zipped ${zipped.byteLength} bytes`);
82
- const s = p.spinner()
37
+ const config = await getConfig()
38
+ appId = appId || config?.app?.appId
39
+ // create bundle name format : 1.0.0-beta.x where x is a uuid
40
+ const uuid = randomUUID().split('-')[0]
41
+ bundle = bundle || config?.app?.package?.version || `0.0.1-beta.${uuid}`
42
+ if (!json)
43
+ p.intro(`Zipping ${appId}@${bundle}`)
44
+ // check if bundle is valid
45
+ if (!regexSemver.test(bundle)) {
83
46
  if (!json)
84
- s.start(`Calculating checksum`);
85
- const checksum = await getChecksum(zipped, 'crc32');
47
+ p.log.error(`Your bundle name ${bundle}, is not valid it should follow semver convention : https://semver.org/`)
48
+ else
49
+ console.error(formatError({ error: 'invalid_semver' }))
50
+ program.error('')
51
+ }
52
+ path = path || config?.app?.webDir
53
+ if (!appId || !bundle || !path) {
86
54
  if (!json)
87
- s.stop(`Checksum: ${checksum}`);
88
- const mbSize = Math.floor(zipped.byteLength / 1024 / 1024);
89
- // We do not issue this warning for json
90
- if (mbSize > alertMb && !json) {
91
- p.log.warn(`WARNING !!\nThe app size is ${mbSize} Mb, this may take a while to download for users\n`);
92
- p.log.warn(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`);
93
- await snag.track({
94
- channel: 'app-error',
95
- event: 'App Too Large',
96
- icon: '🚛',
97
- tags: {
98
- 'app-id': appId,
99
- },
100
- notify: false,
101
- }).catch()
55
+ p.log.error('Missing argument, you need to provide a appId and a bundle and a path, or be in a capacitor project')
56
+ else
57
+ console.error(formatError({ error: 'missing_argument' }))
58
+ program.error('')
59
+ }
60
+ if (!json)
61
+ p.log.info(`Started from path "${path}"`)
62
+ const checkNotifyAppReady = options.codeCheck
63
+ if (typeof checkNotifyAppReady === 'undefined' || checkNotifyAppReady) {
64
+ const isPluginConfigured = searchInDirectory(path, 'notifyAppReady')
65
+ if (!isPluginConfigured) {
66
+ if (!json)
67
+ p.log.error(`notifyAppReady() is missing in the source code. see: https://capgo.app/docs/plugin/api/#notifyappready`)
68
+ else
69
+ console.error(formatError({ error: 'notifyAppReady_not_in_source_code' }))
70
+ program.error('')
102
71
  }
103
- const s2 = p.spinner()
104
- const name = options.name || `${appId}_${bundle}.zip`
105
- if (!json)
106
- s2.start(`Saving to ${name}`);
107
- writeFileSync(name, zipped);
108
- if (!json)
109
- s2.stop(`Saved to ${name}`);
110
-
111
- if (options.json) {
112
- const output = {
113
- bundle,
114
- filename: name,
115
- checksum,
116
- };
117
- console.log(JSON.stringify(output));
72
+ const foundIndex = checkIndexPosition(path)
73
+ if (!foundIndex) {
74
+ if (!json)
75
+ p.log.error(`index.html is missing in the root folder or in the only folder in the root folder`)
76
+ else
77
+ console.error(formatError({ error: 'index_html_not_found' }))
78
+ program.error('')
118
79
  }
119
-
80
+ }
81
+ const zip = new AdmZip()
82
+ zip.addLocalFolder(path)
83
+ const zipped = zip.toBuffer()
84
+ if (!json)
85
+ p.log.info(`Zipped ${zipped.byteLength} bytes`)
86
+ const s = p.spinner()
87
+ if (!json)
88
+ s.start(`Calculating checksum`)
89
+ const checksum = await getChecksum(zipped, 'crc32')
90
+ if (!json)
91
+ s.stop(`Checksum: ${checksum}`)
92
+ const mbSize = Math.floor(zipped.byteLength / 1024 / 1024)
93
+ // We do not issue this warning for json
94
+ if (mbSize > alertMb && !json) {
95
+ p.log.warn(`WARNING !!\nThe app size is ${mbSize} Mb, this may take a while to download for users\n`)
96
+ p.log.warn(`Learn how to optimize your assets https://capgo.app/blog/optimise-your-images-for-updates/\n`)
120
97
  await snag.track({
121
- channel: 'app',
122
- event: 'App zip',
123
- icon: '',
124
- tags: {
125
- 'app-id': appId,
126
- },
127
- notify: false,
98
+ channel: 'app-error',
99
+ event: 'App Too Large',
100
+ icon: '🚛',
101
+ tags: {
102
+ 'app-id': appId,
103
+ },
104
+ notify: false,
128
105
  }).catch()
106
+ }
107
+ const s2 = p.spinner()
108
+ const name = options.name || `${appId}_${bundle}.zip`
109
+ if (!json)
110
+ s2.start(`Saving to ${name}`)
111
+ writeFileSync(name, zipped)
112
+ if (!json)
113
+ s2.stop(`Saved to ${name}`)
129
114
 
130
- if (!json)
131
- p.outro(`Done ✅`);
132
- process.exit()
115
+ if (options.json) {
116
+ const output = {
117
+ bundle,
118
+ filename: name,
119
+ checksum,
120
+ }
121
+ p.log.info(formatError(output))
122
+ }
123
+
124
+ await snag.track({
125
+ channel: 'app',
126
+ event: 'App zip',
127
+ icon: '⏫',
128
+ tags: {
129
+ 'app-id': appId,
130
+ },
131
+ notify: false,
132
+ }).catch()
133
+
134
+ if (!json)
135
+ p.outro(`Done ✅`)
136
+ process.exit()
133
137
  }
@@ -1,71 +1,73 @@
1
- import { program } from "commander";
2
- import * as p from '@clack/prompts';
3
- import { checkAppExistsAndHasPermissionErr } from "../api/app";
4
- import { createChannel, findUnknownVersion } from "../api/channels";
5
- import { OptionsBase, findSavedKey, getConfig, useLogSnag, createSupabaseClient, verifyUser } from "../utils";
1
+ import process from 'node:process'
2
+ import { program } from 'commander'
3
+ import * as p from '@clack/prompts'
4
+ import { checkAppExistsAndHasPermissionErr } from '../api/app'
5
+ import { createChannel, findUnknownVersion } from '../api/channels'
6
+ import type { OptionsBase } from '../utils'
7
+ import { createSupabaseClient, findSavedKey, getConfig, useLogSnag, verifyUser } from '../utils'
6
8
 
7
9
  interface Options extends OptionsBase {
8
- default?: boolean;
10
+ default?: boolean
9
11
  }
10
12
 
11
- export const addChannel = async (channelId: string, appId: string, options: Options, shouldExit = true) => {
12
- p.intro(`Create channel`);
13
- options.apikey = options.apikey || findSavedKey()
14
- const config = await getConfig();
15
- appId = appId || config?.app?.appId
16
- const snag = useLogSnag()
13
+ export async function addChannel(channelId: string, appId: string, options: Options, shouldExit = true) {
14
+ p.intro(`Create channel`)
15
+ options.apikey = options.apikey || findSavedKey()
16
+ const config = await getConfig()
17
+ appId = appId || config?.app?.appId
18
+ const snag = useLogSnag()
17
19
 
18
- if (!options.apikey) {
19
- p.log.error("Missing API key, you need to provide a API key to upload your bundle");
20
- program.error('');
21
- }
22
- if (!appId) {
23
- p.log.error("Missing argument, you need to provide a appId, or be in a capacitor project");
24
- program.error('');
25
- }
26
- const supabase = await createSupabaseClient(options.apikey)
20
+ if (!options.apikey) {
21
+ p.log.error('Missing API key, you need to provide a API key to upload your bundle')
22
+ program.error('')
23
+ }
24
+ if (!appId) {
25
+ p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
26
+ program.error('')
27
+ }
28
+ const supabase = await createSupabaseClient(options.apikey)
27
29
 
28
- const userId = await verifyUser(supabase, options.apikey, ['write', 'all']);
29
- // Check we have app access to this appId
30
- await checkAppExistsAndHasPermissionErr(supabase, options.apikey, appId);
30
+ const userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
31
+ // Check we have app access to this appId
32
+ await checkAppExistsAndHasPermissionErr(supabase, options.apikey, appId)
31
33
 
32
- p.log.info(`Creating channel ${appId}#${channelId} to Capgo`);
33
- try {
34
- const data = await findUnknownVersion(supabase, appId)
35
- if (!data) {
36
- p.log.error(`Cannot find default version for channel creation, please contact Capgo support 🤨`);
37
- program.error('');
38
- }
39
- await createChannel(supabase, {
40
- name: channelId,
41
- app_id: appId,
42
- version: data.id,
43
- created_by: userId
44
- });
45
- p.log.success(`Channel created ✅`);
46
- await snag.track({
47
- channel: 'channel',
48
- event: 'Create channel',
49
- icon: '✅',
50
- user_id: userId,
51
- tags: {
52
- 'app-id': appId,
53
- 'channel': channelId,
54
- },
55
- notify: false,
56
- }).catch()
57
- } catch (error) {
58
- p.log.error(`Cannot create Channel 🙀`);
59
- return false
34
+ p.log.info(`Creating channel ${appId}#${channelId} to Capgo`)
35
+ try {
36
+ const data = await findUnknownVersion(supabase, appId)
37
+ if (!data) {
38
+ p.log.error(`Cannot find default version for channel creation, please contact Capgo support 🤨`)
39
+ program.error('')
60
40
  }
61
- if (shouldExit) {
62
- p.outro(`Done ✅`);
63
- process.exit()
64
- }
65
- return true
41
+ await createChannel(supabase, {
42
+ name: channelId,
43
+ app_id: appId,
44
+ version: data.id,
45
+ created_by: userId,
46
+ })
47
+ p.log.success(`Channel created ✅`)
48
+ await snag.track({
49
+ channel: 'channel',
50
+ event: 'Create channel',
51
+ icon: '✅',
52
+ user_id: userId,
53
+ tags: {
54
+ 'app-id': appId,
55
+ 'channel': channelId,
56
+ },
57
+ notify: false,
58
+ }).catch()
59
+ }
60
+ catch (error) {
61
+ p.log.error(`Cannot create Channel 🙀`)
62
+ return false
63
+ }
64
+ if (shouldExit) {
65
+ p.outro(`Done ✅`)
66
+ process.exit()
67
+ }
68
+ return true
66
69
  }
67
70
 
68
-
69
- export const addChannelCommand = async (apikey: string, appId: string, options: Options) => {
70
- addChannel(apikey, appId, options, true)
71
+ export async function addChannelCommand(apikey: string, appId: string, options: Options) {
72
+ addChannel(apikey, appId, options, true)
71
73
  }
@@ -1,73 +1,73 @@
1
- import { program } from "commander";
2
- import * as p from '@clack/prompts';
3
- import { checkAppExistsAndHasPermissionErr } from "../api/app";
4
- import { OptionsBase, createSupabaseClient, findSavedKey, getConfig, verifyUser } from "../utils";
5
-
1
+ import process from 'node:process'
2
+ import { program } from 'commander'
3
+ import * as p from '@clack/prompts'
4
+ import { checkAppExistsAndHasPermissionErr } from '../api/app'
5
+ import type { OptionsBase } from '../utils'
6
+ import { createSupabaseClient, findSavedKey, getConfig, verifyUser } from '../utils'
6
7
 
7
8
  interface Options extends OptionsBase {
8
- channel?: string,
9
- quiet?: boolean
9
+ channel?: string
10
+ quiet?: boolean
10
11
  }
11
12
 
12
13
  interface Channel {
13
- version: {
14
- name: string;
15
- }
14
+ version: {
15
+ name: string
16
+ }
16
17
  }
17
18
 
18
- export const currentBundle = async (channel: string, appId: string, options: Options) => {
19
- const { quiet } = options
19
+ export async function currentBundle(channel: string, appId: string, options: Options) {
20
+ const { quiet } = options
21
+
22
+ if (!quiet)
23
+ p.intro(`List current bundle`)
20
24
 
21
- if (!quiet)
22
- p.intro(`List current bundle`);
25
+ options.apikey = options.apikey || findSavedKey(quiet)
26
+ const config = await getConfig()
27
+ appId = appId || config?.app?.appId
23
28
 
29
+ if (!options.apikey) {
30
+ p.log.error('Missing API key, you need to provide a API key to upload your bundle')
31
+ program.error('')
32
+ }
33
+ if (!appId) {
34
+ p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
35
+ program.error('')
36
+ }
37
+ const supabase = await createSupabaseClient(options.apikey)
24
38
 
25
- options.apikey = options.apikey || findSavedKey(quiet)
26
- const config = await getConfig();
27
- appId = appId || config?.app?.appId
28
-
29
- if (!options.apikey) {
30
- p.log.error("Missing API key, you need to provide a API key to upload your bundle");
31
- program.error('');
32
- }
33
- if (!appId) {
34
- p.log.error("Missing argument, you need to provide a appId, or be in a capacitor project");
35
- program.error('');
36
- }
37
- const supabase = await createSupabaseClient(options.apikey)
38
-
39
- const userId = await verifyUser(supabase, options.apikey, ['write', 'all', 'read']);
40
- // Check we have app access to this appId
41
- await checkAppExistsAndHasPermissionErr(supabase, options.apikey, appId);
39
+ const userId = await verifyUser(supabase, options.apikey, ['write', 'all', 'read'])
40
+ // Check we have app access to this appId
41
+ await checkAppExistsAndHasPermissionErr(supabase, options.apikey, appId)
42
42
 
43
- if (!channel) {
44
- p.log.error(`Please provide a channel to get the bundle from.`);
45
- program.error('');
46
- }
43
+ if (!channel) {
44
+ p.log.error(`Please provide a channel to get the bundle from.`)
45
+ program.error('')
46
+ }
47
47
 
48
- const { data: supabaseChannel, error } = await supabase
49
- .from('channels')
50
- .select('version ( name )')
51
- .eq('name', channel)
52
- .eq('app_id', appId)
53
- .eq('created_by', userId)
54
- .limit(1)
48
+ const { data: supabaseChannel, error } = await supabase
49
+ .from('channels')
50
+ .select('version ( name )')
51
+ .eq('name', channel)
52
+ .eq('app_id', appId)
53
+ .eq('created_by', userId)
54
+ .limit(1)
55
55
 
56
- if (error || supabaseChannel.length === 0) {
57
- p.log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`);
58
- program.error('');
59
- }
56
+ if (error || supabaseChannel.length === 0) {
57
+ p.log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`)
58
+ program.error('')
59
+ }
60
60
 
61
- const { version } = supabaseChannel[0] as any as Channel
62
- if (!version) {
63
- p.log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`);
64
- program.error('');
65
- }
61
+ const { version } = supabaseChannel[0] as any as Channel
62
+ if (!version) {
63
+ p.log.error(`Error retrieving channel ${channel} for app ${appId}. Perhaps the channel does not exists?`)
64
+ program.error('')
65
+ }
66
66
 
67
- if (!quiet)
68
- p.log.info(`Current bundle for channel ${channel} is ${version.name}`)
69
- else
70
- console.log(version.name)
67
+ if (!quiet)
68
+ p.log.info(`Current bundle for channel ${channel} is ${version.name}`)
69
+ else
70
+ p.log.info(version.name)
71
71
 
72
- process.exit()
72
+ process.exit()
73
73
  }
@@ -1,48 +1,51 @@
1
- import { program } from "commander";
2
- import * as p from '@clack/prompts';
3
- import { checkAppExistsAndHasPermissionErr } from "../api/app";
4
- import { delChannel } from "../api/channels";
5
- import { OptionsBase, findSavedKey, getConfig, useLogSnag, createSupabaseClient, verifyUser } from "../utils";
1
+ import process from 'node:process'
2
+ import { program } from 'commander'
3
+ import * as p from '@clack/prompts'
4
+ import { checkAppExistsAndHasPermissionErr } from '../api/app'
5
+ import { delChannel } from '../api/channels'
6
+ import type { OptionsBase } from '../utils'
7
+ import { createSupabaseClient, findSavedKey, getConfig, useLogSnag, verifyUser } from '../utils'
6
8
 
7
- export const deleteChannel = async (channelId: string, appId: string, options: OptionsBase) => {
8
- p.intro(`Delete channel`);
9
- options.apikey = options.apikey || findSavedKey()
10
- const config = await getConfig();
11
- appId = appId || config?.app?.appId
12
- const snag = useLogSnag()
9
+ export async function deleteChannel(channelId: string, appId: string, options: OptionsBase) {
10
+ p.intro(`Delete channel`)
11
+ options.apikey = options.apikey || findSavedKey()
12
+ const config = await getConfig()
13
+ appId = appId || config?.app?.appId
14
+ const snag = useLogSnag()
13
15
 
14
- if (!options.apikey) {
15
- p.log.error("Missing API key, you need to provide a API key to upload your bundle");
16
- program.error('');
17
- }
18
- if (!appId) {
19
- p.log.error("Missing argument, you need to provide a appId, or be in a capacitor project");
20
- program.error('');
21
- }
22
- const supabase = await createSupabaseClient(options.apikey)
16
+ if (!options.apikey) {
17
+ p.log.error('Missing API key, you need to provide a API key to upload your bundle')
18
+ program.error('')
19
+ }
20
+ if (!appId) {
21
+ p.log.error('Missing argument, you need to provide a appId, or be in a capacitor project')
22
+ program.error('')
23
+ }
24
+ const supabase = await createSupabaseClient(options.apikey)
23
25
 
24
- const userId = await verifyUser(supabase, options.apikey, ['write', 'all']);
25
- // Check we have app access to this appId
26
- await checkAppExistsAndHasPermissionErr(supabase, options.apikey, appId);
26
+ const userId = await verifyUser(supabase, options.apikey, ['write', 'all'])
27
+ // Check we have app access to this appId
28
+ await checkAppExistsAndHasPermissionErr(supabase, options.apikey, appId)
27
29
 
28
- p.log.info(`Deleting channel ${appId}#${channelId} from Capgo`);
29
- try {
30
- await delChannel(supabase, channelId, appId, userId);
31
- p.log.success(`Channel deleted`);
32
- await snag.track({
33
- channel: 'channel',
34
- event: 'Delete channel',
35
- icon: '✅',
36
- tags: {
37
- 'user-id': userId,
38
- 'app-id': appId,
39
- 'channel': channelId,
40
- },
41
- notify: false,
42
- }).catch()
43
- } catch (error) {
44
- p.log.error(`Cannot delete Channel 🙀`);
45
- }
46
- p.outro(`Done ✅`);
47
- process.exit()
30
+ p.log.info(`Deleting channel ${appId}#${channelId} from Capgo`)
31
+ try {
32
+ await delChannel(supabase, channelId, appId, userId)
33
+ p.log.success(`Channel deleted`)
34
+ await snag.track({
35
+ channel: 'channel',
36
+ event: 'Delete channel',
37
+ icon: '✅',
38
+ tags: {
39
+ 'user-id': userId,
40
+ 'app-id': appId,
41
+ 'channel': channelId,
42
+ },
43
+ notify: false,
44
+ }).catch()
45
+ }
46
+ catch (error) {
47
+ p.log.error(`Cannot delete Channel 🙀`)
48
+ }
49
+ p.outro(`Done ✅`)
50
+ process.exit()
48
51
  }