shoplazza-cli 0.0.4 → 0.0.6

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/README.md CHANGED
@@ -9,6 +9,11 @@
9
9
  ---
10
10
 
11
11
  Shoplazza CLI is a cross-platform command line tool that you can use to build Shoplazza themes.
12
+
13
+ ## Node Version
14
+
15
+ use 14.18.2
16
+
12
17
  ## Features
13
18
 
14
19
  Shoplazza CLI accelerates your theme development process with the following features:
@@ -22,7 +27,8 @@ Shoplazza CLI accelerates your theme development process with the following feat
22
27
  - Work on Linux, macOS, and Windows.
23
28
 
24
29
  ## Installation [shoplazza-cli](https://www.npmjs.com/package/shoplazza-themekit)
25
- ``` terminal
30
+
31
+ ```terminal
26
32
  $ npm install shoplazza-cli -g
27
33
  ```
28
34
 
@@ -37,150 +43,235 @@ Before you start using Shoplazza CLI to develop themes, make sure that you do th
37
43
  - Make sure that you're connected to the internet. Most Shoplazza CLI commands need an internet connection to run.
38
44
 
39
45
  ## Getting started
46
+
40
47
  ### Authenticate
41
- ``` terminal
48
+
49
+ ```terminal
42
50
  $ shoplazza login --store developer.myshoplaza.com
43
51
  ```
52
+
44
53
  > In your browser window, log into the account that's attached to the store that you want to use for development.
45
54
 
46
55
  ### Create a new theme
47
- ``` terminal
56
+
57
+ ```terminal
48
58
  $ shoplazza theme init
49
59
  ```
60
+
50
61
  > Use `shoplazza theme init` to create a new theme on your local machine. This command clones a Git repository to your local machine to use as the starting point for building a theme.
51
62
 
52
63
  ### Connect to existing theme
53
- ``` terminal
64
+
65
+ ```terminal
54
66
  $ shoplazza theme pull
55
67
  ```
68
+
56
69
  > Pull the theme onto your local machine using `shoplazza theme pull`. You're prompted to select a theme from the list of themes on the store.
57
70
 
58
71
  ### Preview, test, and share your theme
59
- ``` terminal
72
+
73
+ ```terminal
60
74
  $ shoplazza theme serve
61
75
  ```
76
+
62
77
  > After you create or navigate to your theme, you can run `shoplazza theme serve` to interact with the theme in a browser.
63
78
 
64
79
  ### Push your theme to your store
65
- ``` terminal
80
+
81
+ ```terminal
66
82
  $ shoplazza theme push
67
83
  ```
84
+
68
85
  > Use `shoplazza theme push` to upload your local theme files to Shoplazza, overwriting the remote versions.
69
86
 
70
87
  ### Publish your theme
71
- ``` terminal
88
+
89
+ ```terminal
72
90
  $ shoplazza theme publish
73
91
  ```
92
+
74
93
  > Use `shoplazza theme publish` to select and publish an unpublished theme from your theme library. If you want to publish your local theme, then you need to run `shoplazza theme push` first.
75
94
 
76
95
  ### Find your theme ID
77
- ``` terminal
96
+
97
+ ```terminal
78
98
  $ shoplazza theme list
79
99
  ```
80
- > You might want to use a theme's ID to pull, push, publish, or delete a theme using Shoplazza CLI.
81
100
 
101
+ > You might want to use a theme's ID to pull, push, publish, or delete a theme using Shoplazza CLI.
82
102
 
83
103
  ## Core commands
84
104
 
85
105
  ### help
86
- ``` terminal
106
+
107
+ ```terminal
87
108
  $ shoplazza help
88
109
  ```
110
+
89
111
  > Lists the available commands and describes what they do.
90
112
 
91
113
  ### login
92
- ``` terminal
114
+
115
+ ```terminal
93
116
  $ shoplazza login --store developer.myshoplaza.com
117
+
118
+ $ shoplazza login --partner
94
119
  ```
95
- > Authenticates and logs you into the specified store with Shoplazza CLI.
120
+
121
+ > --store: Authenticates and logs you into the specified store with Shoplazza CLI.
122
+ > --partner: Authenticates and logs you into the partner with Shoplazza CLI.
96
123
 
97
124
  ### logout
98
- ``` terminal
125
+
126
+ ```terminal
99
127
  $ shoplazza logout
128
+
129
+ $ shoplazza logout --partner
100
130
  ```
101
- > Logs you out of the Shoplazza account and store, the logout command clears credentials. You need to reauthenticate the next time that you connect to a store.
131
+
132
+ > Default to logout your store, logs you out of the Shoplazza account and store, the logout command clears credentials. You need to reauthenticate the next time that you connect to a store.
133
+ > --partner: Clear your partner info, and you need to reauthenticate the next time.
134
+
102
135
 
103
136
  ### switch
104
- ``` terminal
137
+
138
+ ```terminal
105
139
  $ shoplazza switch --store developer.myshoplaza.com
106
140
  ```
141
+
107
142
  > Switches between stores without logging out and logging in again.
108
143
 
109
144
  ### store
110
- ``` terminal
145
+
146
+ ```terminal
111
147
  $ shoplazza store
112
148
  ```
149
+
113
150
  > Displays the store that you're currently connected to.
114
151
 
115
152
  ### version
116
- ``` terminal
153
+
154
+ ```terminal
117
155
  $ shoplazza version
118
156
  ```
157
+
119
158
  > Displays the version of Shoplazza CLI that you're running.
120
159
 
121
160
  ## Theme commands
122
161
 
123
162
  ### init
124
- ``` terminal
163
+
164
+ ```terminal
125
165
  $ shoplazza theme init [--name]
126
166
  ```
167
+
127
168
  > Clones a Git repository to your local machine to use as the starting point for building a theme.
128
169
 
129
170
  ### serve
130
- ``` terminal
171
+
172
+ ```terminal
131
173
  $ shoplazza theme serve [--theme]
132
174
  ```
175
+
133
176
  > Uploads the current theme to the store that you're connected to, and returns the preview link.
134
177
 
135
178
  ### list
136
- ``` terminal
179
+
180
+ ```terminal
137
181
  $ shoplazza theme list
138
182
  ```
183
+
139
184
  > Lists the themes in your store, along with their IDs and statuses.
140
185
 
141
186
  ### pull
142
- ``` terminal
187
+
188
+ ```terminal
143
189
  $ shoplazza theme pull [--theme]
144
190
  ```
191
+
145
192
  > Retrieves theme files from Shoplazza, if no theme is specified, then you're prompted to select the theme to pull from the list of the themes in your store.
146
193
 
147
194
  ### push
148
- ``` terminal
195
+
196
+ ```terminal
149
197
  $ shoplazza theme push [--theme]
150
198
  ```
199
+
151
200
  > Uploads your local theme files to Shoplazza, overwriting the remote theme if specified, if no theme is specified, then you're prompted to select the theme to overwrite from the list of the themes in your store.
152
201
 
153
202
  ### share
154
- ``` terminal
203
+
204
+ ```terminal
155
205
  $ shoplazza theme share
156
206
  ```
207
+
157
208
  > Uploads your theme as a new, unpublished theme in your theme library. The command return a preview link that you can share with others.
158
209
 
159
210
  ### publish
160
- ``` terminal
211
+
212
+ ```terminal
161
213
  $ shoplazza theme publish [--theme]
162
214
  ```
215
+
163
216
  > Publishes an unpublished theme from your theme library, if no theme ID is specified, then you're prompted to select the theme that you want to publish from the list of themes in your store.
164
217
 
165
218
  ### package
166
- ``` terminal
219
+
220
+ ```terminal
167
221
  $ shoplazza theme package
168
222
  ```
223
+
169
224
  > Packages your local theme files into a ZIP file that can be uploaded to Shoplazza. The ZIP file uses the name theme_name-theme_version.zip, based on parameters in your `settings_schema.json` file.
170
225
 
171
226
  ### delete
172
- ``` terminal
227
+
228
+ ```terminal
173
229
  $ shoplazza theme delete [--theme]
174
230
  ```
231
+
175
232
  > Deletes a theme from your store, if no theme is specified, then you're prompted to select the theme that you want to delete from the list of themes in your store.
176
233
 
234
+ ## app
235
+
236
+ ### generate
237
+
238
+ ```terminal
239
+ $ shoplazza app generate extension
240
+ ```
241
+
242
+ > Choose Your app type and download template.
243
+
244
+ ### build
245
+
246
+ ```terminal
247
+ $ shoplazza app build
248
+ ```
249
+
250
+ > Minify css / js files and generate manifest.json and zip.
251
+
252
+ ### deploy
253
+
254
+ ```terminal
255
+ $ shoplazza app deploy extension
256
+ ```
257
+
258
+ > Deploy your zip to oss for publish.
259
+
260
+ ### pupblish
261
+
262
+ ```terminal
263
+ $ shoplazza app publish extension
264
+ ```
265
+
266
+ > Choose your extension version and publish to your store.
177
267
 
178
268
  ## Theme Directory
179
269
 
180
270
  You can run certain theme commands, such as shoplazza theme serve, only if the directory you're using matches the default Shoplazza theme directory structure. This structure represents a buildless theme, or a theme that has already gone through any necessary file transformations. If you use build tools to generate theme files, then you might need to run commands from the directory where the generated files are stored.
181
271
 
182
272
  The default Shoplazza theme directory structure is as follows:
183
- ``` terminal
273
+
274
+ ```terminal
184
275
  └── project
185
276
  ├── assets
186
277
  ├── config
package/bin/shoplazza CHANGED
@@ -8,6 +8,8 @@ const pkg = require('../package.json');
8
8
  const report = require('../lib/report');
9
9
  require('../lib/tracing');
10
10
 
11
+ const { generateExtension, deployExtension, publishExtension, buildExtension, retry } = require('../lib/app');
12
+
11
13
  Sentry.init({
12
14
  dsn: 'https://89964605acaf4db8839f2d5237396d6c@sentry.shoplazza.com/730',
13
15
  integrations: [
@@ -32,11 +34,14 @@ program
32
34
  .command('login')
33
35
  .description('Authenticates and logs you into the specified store with Shoplazza CLI.')
34
36
  .option('-s, --store <store>', 'The store domain (Eg: developer.myshoplaza.com)')
37
+ .option('-p, --partner', 'Log into partner app')
35
38
  .action(require('../lib/commands/login').login);
36
39
 
37
40
  program
38
41
  .command('logout')
39
- .description('Logs you out of the Shoplazza account.')
42
+ .description('Logs you out of the Shoplazza account, logs out your store account without any options.')
43
+ .option('-s, --store', 'Logs out your store account')
44
+ .option('-p, --partner', 'Logs out your partner account')
40
45
  .action(require('../lib/commands/logout'));
41
46
 
42
47
  program
@@ -104,6 +109,19 @@ theme
104
109
  .option('-t, --theme <theme>', 'The ID of the theme that you want to delete.')
105
110
  .action(require('../lib/commands/theme/delete'));
106
111
 
112
+ const app = program.command('app').description('Shoplazza app extensions cli');
113
+
114
+ app.command('generate').command('extension').description('Generate your extension').action(generateExtension);
115
+ app.command('deploy').command('extension').description('Deploy your extension to cdn').action(deployExtension);
116
+ app.command('publish').command('extension').description('Publish your extension').action(publishExtension);
117
+ app.command('build').description('Build your extension').action(buildExtension);
118
+ app
119
+ .command('retry')
120
+ .description('Retry to do some tasks.')
121
+ .option('-p, --partner', 'Retry get and choose your partner list.')
122
+ .option('-a, --app', 'Retry get and choose your app list.')
123
+ .action(retry);
124
+
107
125
  program.parse(process.argv);
108
126
  !program.args.length && program.help();
109
127
 
@@ -0,0 +1,66 @@
1
+ const chalk = require('chalk');
2
+ const ora = require('ora');
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+
6
+ const { zipTheme } = require('../../utils');
7
+ const { line, done } = require('../log');
8
+ const { buildThemeAssets } = require('../extensions/theme-app');
9
+
10
+ const isFileExist = (path) => {
11
+ return fs.existsSync(path);
12
+ };
13
+
14
+ const clearManifest = async () => {
15
+ try {
16
+ if (isFileExist('./assets-manifest.json') && isFileExist('./assets')) {
17
+ const mainfest = JSON.parse(fs.readFileSync('./assets-manifest.json', { encoding: 'utf-8' }));
18
+ await Promise.all(
19
+ Object.values(mainfest).map((path) => {
20
+ if (isFileExist(path)) {
21
+ return fs.unlink(path);
22
+ }
23
+ return Promise.resolve(true);
24
+ })
25
+ );
26
+ await fs.unlink('./assets-manifest.json');
27
+ }
28
+ return true;
29
+ } catch (e) {
30
+ console.log(e);
31
+ }
32
+ };
33
+
34
+ const getZipName = () => {
35
+ return path.basename(process.cwd());
36
+ };
37
+
38
+ const generateZip = async (name) => {
39
+ const zipPath = `${process.cwd()}/${name}.zip`;
40
+ isFileExist(zipPath) && (await fs.unlink(zipPath));
41
+ zipTheme(process.cwd(), name);
42
+ };
43
+
44
+ const zipApp = async () => {
45
+ const spinner = ora(chalk.cyan('Building your app and generate zip package ...')).start();
46
+ try {
47
+ const zipName = getZipName();
48
+ await generateZip(zipName);
49
+ spinner.succeed(chalk.cyan(`Success to build your app and generate zip package: ${chalk.green(`${zipName}.zip`)}`));
50
+ return true;
51
+ } catch (e) {
52
+ spinner.fail(e.message);
53
+ }
54
+ };
55
+
56
+ const build = async () => {
57
+ line();
58
+ (await clearManifest()) && (await buildThemeAssets('./assets')) && (await zipApp());
59
+ done();
60
+ line();
61
+ };
62
+
63
+ module.exports = {
64
+ build,
65
+ getZipName
66
+ };
@@ -0,0 +1,213 @@
1
+ const axios = require('axios');
2
+ const ora = require('ora');
3
+ const chalk = require('chalk');
4
+ const fs = require('fs-extra');
5
+ const SparkMD5 = require('spark-md5');
6
+ const FormData = require('form-data');
7
+ const path = require('path');
8
+
9
+ const { PARNTER_URL } = require('../constants');
10
+ const { getValue, PARTNER_KEYS, getApp, set } = require('../db/partner');
11
+ const { getZipName } = require('./build');
12
+ const { inputVersion } = require('../inquirers/version');
13
+ const { line, done } = require('../log');
14
+
15
+ const getZipPath = () => {
16
+ const spinner = ora(chalk.cyan('Finding your built zip ...')).start();
17
+ try {
18
+ const zipName = getZipName();
19
+ const filename = fs.readdirSync(process.cwd()).find((name) => {
20
+ const extname = path.extname(name).replace('.', '');
21
+ return extname === 'zip' && name.startsWith(zipName);
22
+ });
23
+
24
+ if (!filename) {
25
+ throw new Error('not zip');
26
+ }
27
+
28
+ const fileInfo = fs.statSync(`${process.cwd()}/${filename}`);
29
+
30
+ spinner.succeed(
31
+ chalk.cyan(
32
+ `Success to find your zip: ${chalk.green(`${filename} ${(fileInfo.size / (1024 * 1024)).toFixed(2)}M`)}`
33
+ )
34
+ );
35
+ return path.resolve(process.cwd(), filename);
36
+ } catch (e) {
37
+ spinner.fail(chalk.red('Failed to find your zip, be sure you have built app in root dir!'));
38
+ }
39
+ };
40
+
41
+ const getBufferAndMd5 = async (filePath) => {
42
+ const spinner = ora(chalk.cyan('Begin to read zip and generate md5 code ...')).start();
43
+
44
+ try {
45
+ return await new Promise((resolve, reject) => {
46
+ const spark = new SparkMD5.ArrayBuffer();
47
+ let buffers = [];
48
+
49
+ const rs = fs.createReadStream(filePath, { autoClose: true });
50
+ rs.on('data', (data) => {
51
+ buffers.push(data);
52
+ spark.append(data);
53
+ });
54
+
55
+ rs.on('end', () => {
56
+ const md5 = spark.end();
57
+ const completedBuffer = Buffer.concat(buffers);
58
+ spinner.succeed(chalk.cyan('Success to analyse zip'));
59
+ resolve([completedBuffer, md5 + '.zip']);
60
+ });
61
+
62
+ rs.on('error', (err) => {
63
+ reject(err);
64
+ });
65
+ });
66
+ } catch (e) {
67
+ spinner.fail(chalk.red('Failed to analyse zip, please try again!'));
68
+ }
69
+ };
70
+
71
+ const getSign = async () => {
72
+ const spinner = ora(chalk.cyan('Waiting get file sign ...')).start();
73
+
74
+ try {
75
+ const app = getApp();
76
+ const partnerId = getValue(PARTNER_KEYS.PARTNER_ID);
77
+ const sessionId = getValue(PARTNER_KEYS.SESSION_ID);
78
+
79
+ const url = `${PARNTER_URL}/api/partner/apps/${app.uid}/theme_extensions/file/sign`;
80
+ const res = await axios.get(url, {
81
+ headers: {
82
+ Cookie: `awesomev2=${sessionId}`,
83
+ 'x-shoplazza-partner-id': partnerId
84
+ }
85
+ });
86
+ spinner.succeed(chalk.cyan('Success to get file sign'));
87
+ return res.data;
88
+ } catch (e) {
89
+ spinner.fail(chalk.red(e.message || e));
90
+ }
91
+ };
92
+
93
+ const deployOss = async () => {
94
+ const path = getZipPath();
95
+ if (path) {
96
+ const [buffer, md5] = await getBufferAndMd5(path);
97
+ const signData = await getSign();
98
+ if (!signData) {
99
+ return;
100
+ }
101
+
102
+ const spinner = ora(chalk.cyan('Deploying your zip to cdn ...')).start();
103
+
104
+ try {
105
+ const formData = new FormData();
106
+ formData.append('policy', signData.policy);
107
+ formData.append('OSSAccessKeyId', signData.access_id);
108
+ formData.append('success_action_status', 200);
109
+ formData.append('signature', signData.sign);
110
+ formData.append('key', md5);
111
+ formData.append('file', buffer);
112
+
113
+ const url = `https:${signData.write_host}/`;
114
+ const res = await axios.post(url, formData, {
115
+ global: true,
116
+ maxContentLength: 100000000,
117
+ maxBodyLength: 1000000000
118
+ });
119
+
120
+ if (res.status !== 200) {
121
+ throw new Error(`${res.status} ${resstatusText}`);
122
+ }
123
+ spinner.succeed();
124
+ return md5;
125
+ } catch (e) {
126
+ spinner.fail();
127
+ console.log(chalk.red(e.message || e));
128
+ }
129
+ }
130
+ };
131
+
132
+ const deployPartner = async () => {
133
+ const md5 = await deployOss();
134
+ if (!md5) {
135
+ return;
136
+ }
137
+
138
+ const spinner = ora(chalk.cyan('Deploying your zip to PARTNER ...')).start();
139
+ try {
140
+ const app = getApp();
141
+ if (!app) {
142
+ spinner.fail(chalk.red('Please choose your partner first!'));
143
+ return;
144
+ }
145
+
146
+ const partnerId = getValue(PARTNER_KEYS.PARTNER_ID);
147
+ const url = `${PARNTER_URL}/api/partner/apps/${app.uid}/theme_extensions`;
148
+ const res = await axios.put(
149
+ url,
150
+ {
151
+ title: getZipName(),
152
+ name: getZipName(),
153
+ file_name: md5
154
+ },
155
+ {
156
+ headers: {
157
+ cookie: `awesomev2=${getValue(PARTNER_KEYS.SESSION_ID)};`,
158
+ 'x-shoplazza-partner-id': partnerId
159
+ }
160
+ }
161
+ );
162
+
163
+ set({ [PARTNER_KEYS.EXTENSION_ID]: res.data.extension_id });
164
+ spinner.succeed();
165
+ return true;
166
+ } catch (e) {
167
+ spinner.fail();
168
+ console.log(e.message || e);
169
+ console.log(chalk.red(JSON.stringify(e.response.data)));
170
+ }
171
+ };
172
+
173
+ const createVersion = async (version) => {
174
+ const spinner = ora(chalk.cyan('Creating your version task ...')).start();
175
+
176
+ try {
177
+ const app = getApp();
178
+ const extensionId = getValue(PARTNER_KEYS.EXTENSION_ID);
179
+ const partnerId = getValue(PARTNER_KEYS.PARTNER_ID);
180
+
181
+ const url = `${PARNTER_URL}/api/partner/apps/${app.uid}/theme_extensions/${extensionId}/version_tasks`;
182
+ await axios.post(
183
+ url,
184
+ {
185
+ version
186
+ },
187
+ {
188
+ headers: {
189
+ cookie: `awesomev2=${getValue(PARTNER_KEYS.SESSION_ID)};`,
190
+ 'x-shoplazza-partner-id': partnerId
191
+ }
192
+ }
193
+ );
194
+ spinner.succeed();
195
+ return true;
196
+ } catch (e) {
197
+ spinner.fail();
198
+ console.log(chalk.red(e.message || e));
199
+ console.log(chalk.red(JSON.stringify(e.response?.data)));
200
+ }
201
+ };
202
+
203
+ const deploy = async () => {
204
+ line();
205
+ ora(chalk.cyan('Deploy Begin')).succeed();
206
+ let version;
207
+ (await deployPartner()) && (version = await inputVersion()) && (await createVersion(version)) && done();
208
+ line();
209
+ };
210
+
211
+ module.exports = {
212
+ deploy
213
+ };
@@ -0,0 +1,50 @@
1
+ const inquirer = require('inquirer');
2
+ const { set, PARTNER_KEYS } = require('../db/partner');
3
+ const { themeAppHandler, checkoutUIHandler } = require('../extensions');
4
+ const { line } = require('../log');
5
+
6
+ const EXTENSION_TYPES = {
7
+ CHECKOUT_UI: 'checkout_ui',
8
+ THEME_APP: 'theme_app'
9
+ };
10
+
11
+ const generate = async () => {
12
+ line();
13
+ const answer = await inquirer.prompt([
14
+ {
15
+ type: 'list',
16
+ name: 'extensionType',
17
+ message: 'Choose your extension type ↓',
18
+ default: EXTENSION_TYPES.THEME_APP,
19
+ prefix: '*',
20
+ choices: [
21
+ {
22
+ name: 'Theme App Extension',
23
+ value: EXTENSION_TYPES.THEME_APP
24
+ }
25
+ ]
26
+ },
27
+ {
28
+ type: 'input',
29
+ name: 'extensionName',
30
+ message: 'Input your extesion name: ',
31
+ default: 'my-extension',
32
+ prefix: '*'
33
+ }
34
+ ]);
35
+ line();
36
+
37
+ set({ [PARTNER_KEYS.EXTENSION_TYPE]: answer.extensionType });
38
+
39
+ if (answer.extensionType === EXTENSION_TYPES.THEME_APP) {
40
+ await themeAppHandler(answer.extensionName);
41
+ } else if (answer.extensionType === EXTENSION_TYPES.CHECKOUT_UI) {
42
+ await checkoutUIHandler(answer.extensionName);
43
+ }
44
+
45
+ line();
46
+ };
47
+
48
+ module.exports = {
49
+ generate
50
+ };
@@ -0,0 +1,52 @@
1
+ const { default: axios } = require('axios');
2
+ const { selectVersion } = require('../inquirers/version');
3
+ const { PARNTER_URL } = require('../constants');
4
+ const { get, getValue, PARTNER_KEYS } = require('../db/partner');
5
+ const ora = require('ora');
6
+ const chalk = require('chalk');
7
+ const { getZipName } = require('./build');
8
+ const { done, line } = require('../log');
9
+
10
+ const publishVersion = async ({ version, appId, versionId, extensionId }) => {
11
+ const spinner = ora(chalk.cyan('Waiting publish ...'));
12
+
13
+ try {
14
+ const partnerId = getValue(PARTNER_KEYS.PARTNER_ID);
15
+ if (!partnerId) {
16
+ spinner.fail(chalk.red('Please choose a partner first!'));
17
+ return;
18
+ }
19
+
20
+ const url = `${PARNTER_URL}/api/partner/apps/${appId}/theme_extensions/${extensionId}/publications`;
21
+ await axios.post(
22
+ url,
23
+ {
24
+ name: getZipName(),
25
+ version_id: versionId,
26
+ type: 'enable'
27
+ },
28
+ {
29
+ headers: {
30
+ cookie: `awesomev2=${getValue(PARTNER_KEYS.SESSION_ID)};`,
31
+ 'x-shoplazza-partner-id': partnerId
32
+ }
33
+ }
34
+ );
35
+
36
+ spinner.succeed();
37
+ return true;
38
+ } catch (e) {
39
+ spinner.fail(e.message || e);
40
+ }
41
+ };
42
+
43
+ const publish = async () => {
44
+ const versionData = await selectVersion();
45
+ await publishVersion(versionData);
46
+ done();
47
+ line();
48
+ };
49
+
50
+ module.exports = {
51
+ publish
52
+ };