@capgo/cli 0.7.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/src/bin/add.ts ADDED
@@ -0,0 +1,54 @@
1
+ import { loadConfig } from '@capacitor/cli/dist/config';
2
+ import axios from 'axios';
3
+ import prettyjson from 'prettyjson';
4
+ import { program } from 'commander';
5
+ import { readFileSync } from 'fs';
6
+ import { existsSync } from 'fs-extra';
7
+ import { getType } from 'mime';
8
+ import { host } from './utils';
9
+
10
+ export const addApp = async (appid: string, options: any) => {
11
+ let { name, icon } = options;
12
+ const { apikey } = options;
13
+ let config;
14
+ try {
15
+ config = await loadConfig();
16
+ } catch (err) {
17
+ program.error("No capacitor config file found, run `cap init` first");
18
+ }
19
+ appid = appid || config?.app?.appId
20
+ name = name || config?.app?.appName || 'Unknown'
21
+ icon = icon || "resources/icon.png" // default path for capacitor app
22
+ if (!apikey) {
23
+ program.error("Missing API key, you need to provide a API key to add your app");
24
+ }
25
+ if(!appid || !name) {
26
+ program.error("Missing argument, you need to provide a appid and a name, or be in a capacitor project");
27
+ }
28
+ console.log(`Add ${appid} to Capgo`);
29
+ let res;
30
+ try {
31
+ console.log('Adding...');
32
+ const data: any = { appid, name }
33
+ if(icon && existsSync(icon)) {
34
+ const iconBuff = readFileSync(icon);
35
+ const contentType = getType(icon);
36
+ data.icon = iconBuff.toString('base64');
37
+ data.iconType = contentType;
38
+ }
39
+ res = await axios({
40
+ method: 'POST',
41
+ url:`${host}/api/add`,
42
+ data,
43
+ validateStatus: () => true,
44
+ headers: {
45
+ 'authorization': apikey
46
+ }})
47
+ } catch (err) {
48
+ program.error(`Network Error \n${prettyjson.render(err.response.data)}`);
49
+ }
50
+ if (!res || res.status !== 200) {
51
+ program.error(`Server Error \n${prettyjson.render(res.data)}`);
52
+ }
53
+ console.log("App added to server, you can upload a version now")
54
+ }
@@ -0,0 +1,41 @@
1
+ import { loadConfig } from '@capacitor/cli/dist/config';
2
+ import axios from 'axios';
3
+ import prettyjson from 'prettyjson';
4
+ import { program } from 'commander';
5
+ import { host } from './utils';
6
+
7
+ export const deleteApp = async (appid: string, options: any) => {
8
+ const { apikey, version } = options;
9
+ let config;
10
+ try {
11
+ config = await loadConfig();
12
+ } catch (err) {
13
+ program.error("No capacitor config file found, run `cap init` first");
14
+ }
15
+ appid = appid || config?.app?.appId
16
+ if (!apikey) {
17
+ program.error('Missing API key, you need to provide an API key to delete your app');
18
+ }
19
+ if(!appid) {
20
+ program.error('Missing argument, you need to provide a appid, or be in a capacitor project');
21
+ }
22
+ console.log(`Delete ${appid} to Capgo`);
23
+ let res;
24
+ try {
25
+ console.log('Deleting...');
26
+ res = await axios({
27
+ method: 'POST',
28
+ url: `${host}/api/delete`,
29
+ data: { appid, version },
30
+ validateStatus: () => true,
31
+ headers: {
32
+ 'authorization': apikey
33
+ }})
34
+ } catch (err) {
35
+ program.error(`Network Error \n${prettyjson.render(err.response.data)}`);
36
+ }
37
+ if (!res || res.status !== 200) {
38
+ program.error(`Server Error \n${prettyjson.render(res.data)}`);
39
+ }
40
+ console.log("App deleted to server")
41
+ }
@@ -0,0 +1,38 @@
1
+ import { program } from 'commander';
2
+ import { addApp } from './add';
3
+ import { deleteApp } from './delete';
4
+ import { setChannel } from './set';
5
+ import { uploadVersion } from './upload';
6
+
7
+ program
8
+ .command('add [appid]').alias('a')
9
+ .action(addApp)
10
+ .option('-n, --name <name>', 'app name')
11
+ .option('-i, --icon <icon>', 'app icon path')
12
+ .option('-a, --apikey <apikey>', 'apikey to link to your account');
13
+
14
+ program
15
+ .command('upload [appid]').alias('u')
16
+ .action(uploadVersion)
17
+ .option('-a, --apikey <apikey>', 'apikey to link to your account')
18
+ .option('-p, --path <path>', 'path of the file to upload')
19
+ .option('-c, --channel <channel>', 'channel to link to')
20
+ .option('-e, --externa <url>', 'link to external url intead of upload to capgo cloud')
21
+ .option('-v, --version <version>', 'version number of the file to upload');
22
+
23
+ program
24
+ .command('set [appid]').alias('s')
25
+ .action(setChannel)
26
+ .option('-c, --channel <channel>', 'channel to link to')
27
+ .option('-v, --version <version>', 'version number of the file to upload')
28
+ .option('-s, --state <state>', 'set the state of the channel, public or private')
29
+ .option('-a, --apikey <apikey>', 'apikey to link to your account');
30
+
31
+ program
32
+ .description('Manage package and version in capgo Cloud')
33
+ .command('delete [appid]').alias('a')
34
+ .action(deleteApp)
35
+ .option('-a, --apikey <apikey>', 'apikey to link to your account')
36
+ .option('-v, --version <version>', 'version number of the app to delete');
37
+
38
+ program.parse(process.argv);
package/src/bin/set.ts ADDED
@@ -0,0 +1,59 @@
1
+ import { loadConfig } from '@capacitor/cli/dist/config';
2
+ import axios from 'axios';
3
+ import prettyjson from 'prettyjson';
4
+ import { program } from 'commander';
5
+ import { host } from './utils';
6
+
7
+ export const setChannel = async (appid, options) => {
8
+ const { apikey, version, state, channel = 'dev' } = options;
9
+ let config;
10
+ let res;
11
+ try {
12
+ config = await loadConfig();
13
+ } catch (err) {
14
+ program.error("No capacitor config file found, run `cap init` first");
15
+ }
16
+ appid = appid || config?.app?.appId
17
+ let parsedState
18
+ if (state === 'public' || state === 'private')
19
+ parsedState = state === 'public'
20
+ if (!apikey) {
21
+ program.error("Missing API key, you need to provide a API key to add your app");
22
+ }
23
+ if(!appid) {
24
+ program.error("Missing argument, you need to provide a appid, or be in a capacitor project");
25
+ }
26
+ if(!version && !parsedState) {
27
+ program.error("Missing argument, you need to provide a state or a version");
28
+ }
29
+ if (version) {
30
+ console.log(`Set ${channel} to @${version} in ${appid}`);
31
+ } else {
32
+ console.log(`Set${channel} to @${state} in ${appid}`);
33
+ }
34
+ try {
35
+ res = await axios({
36
+ method: 'POST',
37
+ url:`${host}/api/channel`,
38
+ data: {
39
+ version,
40
+ public: parsedState,
41
+ appid,
42
+ channel,
43
+ },
44
+ validateStatus: () => true,
45
+ headers: {
46
+ 'authorization': apikey
47
+ }})
48
+ } catch (err) {
49
+ program.error(`Network Error \n${prettyjson.render(err.response.data)}`);
50
+ }
51
+ if (!res || res.status !== 200) {
52
+ program.error(`Server Error \n${prettyjson.render(res.data)}`);
53
+ }
54
+ if (version) {
55
+ console.log(`Done ✅`);
56
+ } else {
57
+ console.log(`You can use now is channel in your app with the url: ${host}/api/latest?appid=${appid}&channel=${channel}`);
58
+ }
59
+ }
@@ -0,0 +1,124 @@
1
+ import { loadConfig } from '@capacitor/cli/dist/config';
2
+ import AdmZip from 'adm-zip';
3
+ import axios from 'axios';
4
+ import prettyjson from 'prettyjson';
5
+ import { program } from 'commander';
6
+ import cliProgress from 'cli-progress';
7
+ import { host, hostWeb, hostUpload, supaAnon } from './utils';
8
+
9
+ const oneMb = 1048576; // size of one mb
10
+ const maxMb = 30;
11
+ const limitMb = oneMb * maxMb; // size of 1/2 mb
12
+ const formatType = 'binary';
13
+
14
+ export const uploadVersion = async (appid, options) => {
15
+ let { version, path, channel } = options;
16
+ const { apikey, external } = options;
17
+ channel = channel || 'dev';
18
+ let config;
19
+ try {
20
+ config = await loadConfig();
21
+ } catch (err) {
22
+ program.error("No capacitor config file found, run `cap init` first");
23
+ }
24
+ appid = appid || config?.app?.appId
25
+ version = version || config?.app?.package?.version
26
+ path = path || config?.app?.webDir
27
+ if (!apikey) {
28
+ program.error("Missing API key, you need to provide a API key to add your app");
29
+ }
30
+ if(!appid || !version || !path) {
31
+ program.error("Missing argument, you need to provide a appid and a version and a path, or be in a capacitor project");
32
+ }
33
+ console.log(`Upload ${appid}@${version} started from path "${path}" to Capgo cloud`);
34
+ if (external) {
35
+ try {
36
+ const res = await axios({
37
+ method: 'POST',
38
+ url: hostUpload,
39
+ data: {
40
+ version,
41
+ appid,
42
+ channel,
43
+ external,
44
+ },
45
+ validateStatus: () => true,
46
+ headers: {
47
+ 'Content-Type': 'application/json',
48
+ 'apikey': apikey,
49
+ authorization : `Bearer ${supaAnon}`
50
+ }})
51
+ if (res.status !== 200) {
52
+ program.error(`Server Error \n${prettyjson.render(res?.data || "")}`);
53
+ }
54
+ } catch (err) {
55
+ if (err.response) {
56
+ program.error(`Network Error \n${prettyjson.render(err.response?.data || "")}`)
57
+ } else {
58
+ program.error(`Network Error \n${prettyjson.render(err || "")}`)
59
+ }
60
+ }
61
+ } else {
62
+ const b1 = new cliProgress.SingleBar({}, cliProgress.Presets.shades_grey);
63
+ try {
64
+ const zip = new AdmZip();
65
+ zip.addLocalFolder(path);
66
+ console.log('Uploading:');
67
+ const appData = zip.toBuffer().toString(formatType);
68
+ // split appData in chunks and send them sequentially with axios
69
+ const chunkSize = oneMb;
70
+ if (appData.length > limitMb) {
71
+ program.error(`The app is too big, the limit is ${maxMb} Mb`);
72
+ }
73
+ const chunks = [];
74
+ for (let i = 0; i < appData.length; i += chunkSize) {
75
+ chunks.push(appData.slice(i, i + chunkSize));
76
+ }
77
+ b1.start(chunks.length, 0, {
78
+ speed: "N/A"
79
+ });
80
+ let fileName
81
+ for (let i = 0; i < chunks.length; i +=1) {
82
+ const res = await axios({
83
+ method: 'POST',
84
+ url: hostUpload,
85
+ data: {
86
+ version,
87
+ appid,
88
+ fileName,
89
+ channel,
90
+ format: formatType,
91
+ app: chunks[i],
92
+ isMultipart: chunks.length > 1,
93
+ chunk: i + 1,
94
+ totalChunks: chunks.length,
95
+ },
96
+ validateStatus: () => true,
97
+ headers: {
98
+ 'Content-Type': 'application/json',
99
+ 'apikey': apikey,
100
+ authorization : `Bearer ${supaAnon}`
101
+ }})
102
+ if (res.status !== 200) {
103
+ b1.stop();
104
+ program.error(`Server Error \n${prettyjson.render(res?.data || "")}`);
105
+ }
106
+ b1.update(i+1)
107
+ fileName = res.data.fileName
108
+ }
109
+ b1.stop();
110
+ } catch (err) {
111
+ b1.stop();
112
+ if (err.response) {
113
+ program.error(`Network Error \n${prettyjson.render(err.response?.data || "")}`)
114
+ } else {
115
+ program.error(`Network Error \n${prettyjson.render(err || "")}`)
116
+ }
117
+ }
118
+ }
119
+
120
+ console.log("App uploaded to server")
121
+ console.log(`Try it in mobile app: ${host}/app_mobile`)
122
+ console.log(`Or set the channel ${channel} as public here: ${hostWeb}/app/package/${appid}`)
123
+ console.log("To use with live update in your own app")
124
+ }
@@ -0,0 +1,12 @@
1
+ /* eslint-disable */
2
+ export const host = 'https://capgo.app';
3
+ export const hostWeb = 'https://web.capgo.app';
4
+ export const hostUpload = 'https://xvwzpoazmxkqosrdewyv.functions.supabase.co/upload';
5
+ // For local test purposes
6
+ // export const host = 'http://localhost:3334';
7
+ // export const hostUpload = 'http://localhost:54321/functions/v1/upload';
8
+ /* eslint-enable */
9
+
10
+ /* eslint-disable */
11
+ export const supaAnon = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYzNzgwNTAwOSwiZXhwIjoxOTUzMzgxMDA5fQ.8tgID1d4jodPwuo_fz4KHN4o1XKB9fnqyt0_GaJSj-w'
12
+ /* eslint-enable */
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "esModuleInterop": true,
5
+ "target": "es6",
6
+ "moduleResolution": "node",
7
+ "sourceMap": true,
8
+ "outDir": "dist",
9
+ "baseUrl": "src",
10
+ "lib": ["es2016", "dom"]
11
+ },
12
+ "include": ["src/**/*"],
13
+ "exclude": ["node_modules", "__tests__", "**/*.spec.ts"]
14
+ }
@@ -0,0 +1,30 @@
1
+ const path = require('path');
2
+ const webpack = require('webpack');
3
+ const nodeExternals = require('webpack-node-externals');
4
+ const { CheckerPlugin } = require('awesome-typescript-loader');
5
+ console.log(process.env.NODE_ENV || 'production');
6
+ module.exports = {
7
+ mode: process.env.NODE_ENV || 'production',
8
+ target: 'node',
9
+ externals: [nodeExternals()],
10
+ entry: './src/bin/index.ts',
11
+ module: {
12
+ rules: [
13
+ {
14
+ test: /\.ts$/,
15
+ use: 'awesome-typescript-loader',
16
+ exclude: /node_modules/,
17
+ },
18
+ ],
19
+ },
20
+ devtool: process.env.NODE_ENV === 'development' ? 'eval-source-map' : undefined,
21
+ watch: process.env.NODE_ENV === 'development',
22
+ plugins: [new CheckerPlugin(), new webpack.BannerPlugin({ banner: '#!/usr/bin/env node', raw: true })],
23
+ resolve: {
24
+ extensions: ['.ts', '.js'],
25
+ },
26
+ output: {
27
+ filename: 'index.js',
28
+ path: path.resolve(__dirname, 'dist'),
29
+ },
30
+ };