@swell/cli 1.0.2 → 2.0.1-alpha.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/LICENSE.md +21 -0
- package/README.md +1 -1
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +7 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +7 -0
- package/dist/app-command.d.ts +22 -0
- package/dist/app-command.js +45 -0
- package/dist/commands/app/_pull.d.ts +8 -0
- package/dist/commands/app/_pull.js +45 -0
- package/dist/commands/app/_uninstall.d.ts +12 -0
- package/dist/commands/app/_uninstall.js +45 -0
- package/dist/commands/app/info.d.ts +15 -0
- package/dist/commands/app/info.js +92 -0
- package/dist/commands/app/install.d.ts +17 -0
- package/dist/commands/app/install.js +177 -0
- package/dist/commands/app/push.d.ts +17 -0
- package/dist/commands/app/push.js +158 -0
- package/dist/commands/app/version.d.ts +18 -0
- package/dist/commands/app/version.js +156 -0
- package/dist/commands/app/watch.d.ts +15 -0
- package/dist/commands/app/watch.js +110 -0
- package/dist/commands/create/app.d.ts +21 -0
- package/dist/commands/create/app.js +210 -0
- package/dist/commands/create/content.d.ts +19 -0
- package/dist/commands/create/content.js +87 -0
- package/dist/commands/create/function.d.ts +25 -0
- package/dist/commands/create/function.js +191 -0
- package/dist/commands/create/index.d.ts +6 -0
- package/dist/commands/create/index.js +34 -0
- package/dist/commands/create/model.d.ts +19 -0
- package/dist/commands/create/model.js +87 -0
- package/dist/commands/create/notification.d.ts +25 -0
- package/dist/commands/create/notification.js +170 -0
- package/dist/commands/login.d.ts +22 -0
- package/dist/commands/login.js +77 -0
- package/dist/commands/logout.d.ts +8 -0
- package/dist/commands/logout.js +45 -0
- package/dist/commands/logs.d.ts +23 -0
- package/dist/commands/logs.js +207 -0
- package/dist/commands/switch.d.ts +10 -0
- package/dist/commands/switch.js +41 -0
- package/dist/commands/whoami.d.ts +7 -0
- package/dist/commands/whoami.js +24 -0
- package/dist/create-collection-command.d.ts +26 -0
- package/dist/create-collection-command.js +97 -0
- package/dist/create-command.d.ts +16 -0
- package/dist/create-command.js +59 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/api.d.ts +13 -0
- package/dist/lib/api.js +117 -0
- package/dist/lib/app-config.d.ts +29 -0
- package/dist/lib/app-config.js +127 -0
- package/dist/lib/apps/app-config.d.ts +42 -0
- package/dist/lib/apps/app-config.js +167 -0
- package/dist/lib/apps/index.d.ts +67 -0
- package/dist/lib/apps/index.js +167 -0
- package/dist/lib/apps/paths.d.ts +18 -0
- package/dist/lib/apps/paths.js +54 -0
- package/dist/lib/bundle.d.ts +1 -0
- package/dist/lib/bundle.js +40 -0
- package/dist/lib/config.d.ts +37 -0
- package/dist/lib/config.js +131 -0
- package/dist/lib/constants.d.ts +5 -0
- package/dist/lib/constants.js +17 -0
- package/dist/lib/create/content.d.ts +23 -0
- package/dist/lib/create/content.js +40 -0
- package/dist/lib/create/function.d.ts +17 -0
- package/dist/lib/create/function.js +36 -0
- package/dist/lib/create/index.d.ts +13 -0
- package/dist/lib/create/index.js +20 -0
- package/dist/lib/create/model.d.ts +22 -0
- package/dist/lib/create/model.js +30 -0
- package/dist/lib/create/notification.d.ts +25 -0
- package/dist/lib/create/notification.js +1 -0
- package/dist/lib/info.d.ts +38 -0
- package/dist/lib/info.js +91 -0
- package/dist/lib/logs/index.d.ts +23 -0
- package/dist/lib/logs/index.js +88 -0
- package/dist/lib/logs/line-output.d.ts +5 -0
- package/dist/lib/logs/line-output.js +13 -0
- package/dist/lib/logs/output.d.ts +7 -0
- package/dist/lib/logs/output.js +8 -0
- package/dist/lib/logs/table-output.d.ts +11 -0
- package/dist/lib/logs/table-output.js +128 -0
- package/dist/lib/sessions.d.ts +9 -0
- package/dist/lib/sessions.js +110 -0
- package/dist/lib/stores.d.ts +63 -0
- package/dist/lib/stores.js +173 -0
- package/dist/lib/style.d.ts +24 -0
- package/dist/lib/style.js +29 -0
- package/dist/lib/swell-function-wrapper.d.ts +88 -0
- package/dist/lib/swell-function-wrapper.js +302 -0
- package/dist/remote-app-command.d.ts +36 -0
- package/dist/remote-app-command.js +367 -0
- package/dist/swell-command.d.ts +21 -0
- package/dist/swell-command.js +43 -0
- package/oclif.manifest.json +1085 -0
- package/package.json +89 -42
- package/LICENSE +0 -674
- package/dist/bin/multipart-parser-85ef9626.js +0 -456
- package/dist/bin/swell-0f71f07d.js +0 -30670
- package/dist/bin/swell.js +0 -24
- package/dist/man/swell.1 +0 -85
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Swell Commerce Corp.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -138,4 +138,4 @@ swell model pull -m theme.origin.page --content -f models/page.json --store my-t
|
|
|
138
138
|
|
|
139
139
|
swell model push -f models/orders.json --store my-test-store --secret-key my-dev-secret-key
|
|
140
140
|
swell model push -c -f models/page.json --store my-test-store --secret-key my-dev-secret-key
|
|
141
|
-
```
|
|
141
|
+
```
|
package/bin/dev.cmd
ADDED
package/bin/dev.js
ADDED
package/bin/run.cmd
ADDED
package/bin/run.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Config } from '@oclif/core';
|
|
2
|
+
import { SwellCommand } from './swell-command.js';
|
|
3
|
+
/**
|
|
4
|
+
* A base class for Swell CLI commands that do not require an app to be saved
|
|
5
|
+
* to the API. If your command requires an app to be saved to the API, extend
|
|
6
|
+
* the `RemoteAppCommand` class instead.
|
|
7
|
+
*
|
|
8
|
+
* This class extends the `SwellCommand` class and adds:
|
|
9
|
+
*
|
|
10
|
+
* - a config instance
|
|
11
|
+
* - a shorthand attribute for the app directory
|
|
12
|
+
*/
|
|
13
|
+
export declare abstract class AppCommand extends SwellCommand {
|
|
14
|
+
static baseFlags: {
|
|
15
|
+
'app-path': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
16
|
+
};
|
|
17
|
+
appConfig: any;
|
|
18
|
+
appPath: string;
|
|
19
|
+
constructor(argv: string[], config: Config);
|
|
20
|
+
init(): Promise<void>;
|
|
21
|
+
protected initBaseFlags(): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { default as appConfig } from './lib/app-config.js';
|
|
3
|
+
import { SwellCommand } from './swell-command.js';
|
|
4
|
+
/**
|
|
5
|
+
* A base class for Swell CLI commands that do not require an app to be saved
|
|
6
|
+
* to the API. If your command requires an app to be saved to the API, extend
|
|
7
|
+
* the `RemoteAppCommand` class instead.
|
|
8
|
+
*
|
|
9
|
+
* This class extends the `SwellCommand` class and adds:
|
|
10
|
+
*
|
|
11
|
+
* - a config instance
|
|
12
|
+
* - a shorthand attribute for the app directory
|
|
13
|
+
*/
|
|
14
|
+
export class AppCommand extends SwellCommand {
|
|
15
|
+
// base flags for all AppCommands
|
|
16
|
+
// needs to be in a ES2021 format: https://github.com/oclif/oclif/issues/1100
|
|
17
|
+
static baseFlags = {
|
|
18
|
+
'app-path': Flags.string({
|
|
19
|
+
description: 'path to the app directory',
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
// local app configuration, what developers define
|
|
23
|
+
appConfig;
|
|
24
|
+
// the local path to the app directory
|
|
25
|
+
appPath = '';
|
|
26
|
+
constructor(argv, config) {
|
|
27
|
+
super(argv, config);
|
|
28
|
+
this.appConfig = appConfig();
|
|
29
|
+
this.appPath = this.appConfig.swDirPath;
|
|
30
|
+
}
|
|
31
|
+
async init() {
|
|
32
|
+
await super.init();
|
|
33
|
+
await this.initBaseFlags();
|
|
34
|
+
}
|
|
35
|
+
async initBaseFlags() {
|
|
36
|
+
const klass = super.ctor;
|
|
37
|
+
// ensure the base flags are available
|
|
38
|
+
klass.flags = { ...klass.flags, ...AppCommand.baseFlags };
|
|
39
|
+
const { flags } = await this.parse(klass);
|
|
40
|
+
// if the user passed an app path, use it: reload the app config
|
|
41
|
+
this.appConfig = appConfig(flags['app-path']);
|
|
42
|
+
this.appPath = this.appConfig.swDirPath;
|
|
43
|
+
this.debug(`config path loaded: ${this.appConfig.path}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
2
|
+
export default class AppPull extends RemoteAppCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
appId: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static summary: string;
|
|
7
|
+
run(): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
|
+
import { dasherize, pluralize, underscore } from 'inflection';
|
|
3
|
+
import snakeCase from 'lodash/snakeCase.js';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { newConfig } from '../../lib/app-config.js';
|
|
6
|
+
import { ConfigPaths, writeJsonFile, } from '../../lib/apps/index.js';
|
|
7
|
+
import style from '../../lib/style.js';
|
|
8
|
+
import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
9
|
+
export default class AppPull extends RemoteAppCommand {
|
|
10
|
+
static args = {
|
|
11
|
+
appId: Args.string({ description: 'app id to pull' }),
|
|
12
|
+
};
|
|
13
|
+
static summary = 'Pull app configuration from remote.';
|
|
14
|
+
async run() {
|
|
15
|
+
const { args } = await this.parse(AppPull);
|
|
16
|
+
const { appId } = args;
|
|
17
|
+
this.appPath = process.cwd();
|
|
18
|
+
if (!appId) {
|
|
19
|
+
this.error('You must provide an app id to pull');
|
|
20
|
+
}
|
|
21
|
+
this.log(`Will pull app configuration for ${style.appConfigValue(appId)} in ${this.appPath}`);
|
|
22
|
+
this.app = await this.getApp(appId);
|
|
23
|
+
this.debug('run: app', this.app);
|
|
24
|
+
// save app definition
|
|
25
|
+
const config = newConfig();
|
|
26
|
+
const appConfigJson = {
|
|
27
|
+
description: this.app.description || '',
|
|
28
|
+
id: snakeCase(this.app.name),
|
|
29
|
+
name: this.app.name,
|
|
30
|
+
version: this.app.version,
|
|
31
|
+
};
|
|
32
|
+
config.clear();
|
|
33
|
+
config.set(appConfigJson);
|
|
34
|
+
// save app configs
|
|
35
|
+
for (const config of this.app?.configs || []) {
|
|
36
|
+
if (!config)
|
|
37
|
+
continue;
|
|
38
|
+
const configType = config.type;
|
|
39
|
+
const typeDirName = ConfigPaths[configType];
|
|
40
|
+
const fileName = pluralize(dasherize(underscore(config.name)).toLowerCase());
|
|
41
|
+
const filePath = path.join(this.appPath, typeDirName, `${fileName}.json`);
|
|
42
|
+
writeJsonFile(filePath, config.values);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
2
|
+
export default class AppUninstall extends RemoteAppCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
5
|
+
env: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
6
|
+
store: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
7
|
+
};
|
|
8
|
+
static orientation: {
|
|
9
|
+
env: string;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { confirm } from '@inquirer/prompts';
|
|
2
|
+
import { Flags } from '@oclif/core';
|
|
3
|
+
import config from '../../lib/config.js';
|
|
4
|
+
import style from '../../lib/style.js';
|
|
5
|
+
import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
6
|
+
export default class AppUninstall extends RemoteAppCommand {
|
|
7
|
+
static description = 'Uninstalls an existing App';
|
|
8
|
+
static flags = {
|
|
9
|
+
env: Flags.string({
|
|
10
|
+
default: 'test',
|
|
11
|
+
description: 'environment to uninstall the app from',
|
|
12
|
+
}),
|
|
13
|
+
store: Flags.string({
|
|
14
|
+
char: 's',
|
|
15
|
+
description: 'store id to uninstall the app from',
|
|
16
|
+
}),
|
|
17
|
+
};
|
|
18
|
+
static orientation = {
|
|
19
|
+
env: 'test',
|
|
20
|
+
};
|
|
21
|
+
async run() {
|
|
22
|
+
const { flags } = await this.parse(AppUninstall);
|
|
23
|
+
const { env, store } = flags;
|
|
24
|
+
this.app = await this.getAppWithConfig();
|
|
25
|
+
const storeToUninstall = store || config.getDefaultStore();
|
|
26
|
+
config.ensureLoggedIn(storeToUninstall);
|
|
27
|
+
const uninstallMsg = `${style.appConfigValue(this.app.name)} from store ${style.storeId(storeToUninstall)} with version ${style.appConfigValue(this.app.version)}`;
|
|
28
|
+
const continueUninstall = await confirm({
|
|
29
|
+
message: `Uninstalling ${uninstallMsg}. Ok to continue?`,
|
|
30
|
+
});
|
|
31
|
+
if (!continueUninstall) {
|
|
32
|
+
this.logError('Uninstall cancelled');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const clientApp = await this.api.get({ adminPath: `/client/apps/${this.app.id}` }, { query: { env } });
|
|
36
|
+
if (!clientApp) {
|
|
37
|
+
this.error(`Error uninstalling ${uninstallMsg}: app not installed on store.`);
|
|
38
|
+
}
|
|
39
|
+
if (clientApp.uninstalled) {
|
|
40
|
+
this.error(`Error uninstalling ${uninstallMsg}: app already uninstalled on store.`);
|
|
41
|
+
}
|
|
42
|
+
await this.api.put({ adminPath: `/client/apps/${this.app.id}` }, { body: { env, uninstalled: true } });
|
|
43
|
+
this.log(`${style.appConfigValue(this.app.name)} app uninstalled from store ${style.storeId(storeToUninstall)}.`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
2
|
+
export default class AppInfo extends RemoteAppCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
versions: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: {
|
|
8
|
+
command: string;
|
|
9
|
+
description: string;
|
|
10
|
+
}[];
|
|
11
|
+
static summary: string;
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
private showAppInfo;
|
|
14
|
+
private showVersions;
|
|
15
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Args } from '@oclif/core';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { getBorderCharacters, table } from 'table';
|
|
4
|
+
import { FieldDefinitions } from '../../lib/info.js';
|
|
5
|
+
import style from '../../lib/style.js';
|
|
6
|
+
import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
7
|
+
// The app fields will be displayed in the order they are defined here
|
|
8
|
+
const FIELDS_TO_DISPLAY = [
|
|
9
|
+
'name',
|
|
10
|
+
'description',
|
|
11
|
+
'version',
|
|
12
|
+
'public_id',
|
|
13
|
+
'published',
|
|
14
|
+
'date_created',
|
|
15
|
+
'date_updated',
|
|
16
|
+
'installed',
|
|
17
|
+
'client',
|
|
18
|
+
];
|
|
19
|
+
export default class AppInfo extends RemoteAppCommand {
|
|
20
|
+
static args = {
|
|
21
|
+
versions: Args.string({ description: 'list all app versions' }),
|
|
22
|
+
};
|
|
23
|
+
static description = `The command shows the latest information about your app including the name,
|
|
24
|
+
description, version, public ID, test store, and more. If the ${style.command('versions')} argument is passed,
|
|
25
|
+
it will output all versions of the app instead.`;
|
|
26
|
+
static examples = [
|
|
27
|
+
{
|
|
28
|
+
command: 'swell app info',
|
|
29
|
+
description: 'Show general information about the app.',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
command: 'swell app info versions',
|
|
33
|
+
description: 'Show all established versions of the app.',
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
static summary = 'Show information about your Swell app.';
|
|
37
|
+
async run() {
|
|
38
|
+
const { args } = await this.parse(AppInfo);
|
|
39
|
+
const { versions } = args;
|
|
40
|
+
if (versions) {
|
|
41
|
+
await this.showVersions();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
await this.showAppInfo();
|
|
45
|
+
}
|
|
46
|
+
async showAppInfo() {
|
|
47
|
+
const spinner = ora();
|
|
48
|
+
spinner.start('Retrieving app info...');
|
|
49
|
+
this.app = await this.getAppWithConfig().catch((error) => {
|
|
50
|
+
spinner.fail(`Error fetching app info: ${JSON.stringify(this.app)}`);
|
|
51
|
+
throw error;
|
|
52
|
+
});
|
|
53
|
+
if (spinner.isSpinning) {
|
|
54
|
+
spinner.stop();
|
|
55
|
+
}
|
|
56
|
+
// we pull versions to make sure that what is displayed is the latest
|
|
57
|
+
const versions = await this.getVersions();
|
|
58
|
+
// Output the app field information, e.g.:
|
|
59
|
+
// Name: My App
|
|
60
|
+
const fieldDefinitions = new FieldDefinitions(this.app, versions);
|
|
61
|
+
for (const field of FIELDS_TO_DISPLAY) {
|
|
62
|
+
const fieldDef = fieldDefinitions.getFieldDefinition(field);
|
|
63
|
+
this.log(style.appConfigName(`${fieldDef.label}:`), fieldDef.value);
|
|
64
|
+
}
|
|
65
|
+
// dashboard url is built, not part of the app object
|
|
66
|
+
// we display it at the end for better readability and simplicity
|
|
67
|
+
this.log(style.appConfigName('Dashboard:'), style.link(this.dashboardUrl({}, this.app.client_id)));
|
|
68
|
+
}
|
|
69
|
+
async showVersions() {
|
|
70
|
+
const spinner = ora();
|
|
71
|
+
spinner.start('Retrieving app versions...');
|
|
72
|
+
this.app = await this.getAppWithConfig().catch((error) => {
|
|
73
|
+
spinner.fail(`Error fetching app info: ${JSON.stringify(this.app)}`);
|
|
74
|
+
throw error;
|
|
75
|
+
});
|
|
76
|
+
const versions = await this.handleRequestErrors(async () => this.getVersions(), () => spinner.fail('Error fetching app versions'));
|
|
77
|
+
if (versions.length === 0) {
|
|
78
|
+
spinner.warn('No versions found');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
spinner.succeed(`${this.app.name} app versions:\n`);
|
|
82
|
+
const displayData = versions.map((appVersion) => [
|
|
83
|
+
style.appConfigValue(appVersion.version),
|
|
84
|
+
appVersion.notes,
|
|
85
|
+
new Date(appVersion.date_created).toUTCString(),
|
|
86
|
+
]);
|
|
87
|
+
this.log(table(displayData, {
|
|
88
|
+
border: getBorderCharacters('void'),
|
|
89
|
+
drawHorizontalLine: () => false,
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
2
|
+
export default class AppInstall extends RemoteAppCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
env: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
7
|
+
store: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
|
+
version: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
9
|
+
};
|
|
10
|
+
static summary: string;
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
private installApp;
|
|
13
|
+
private installConfirmationMessage;
|
|
14
|
+
private updateApp;
|
|
15
|
+
private getInstalledApp;
|
|
16
|
+
private confirmRemoveDevelopmentApp;
|
|
17
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { confirm } from '@inquirer/prompts';
|
|
2
|
+
import { Flags } from '@oclif/core';
|
|
3
|
+
import * as semver from 'semver';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import config from '../../lib/config.js';
|
|
6
|
+
import style from '../../lib/style.js';
|
|
7
|
+
import { selectLoggedInStoreId, selectEnvironmentId, } from '../../lib/stores.js';
|
|
8
|
+
import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
9
|
+
export default class AppInstall extends RemoteAppCommand {
|
|
10
|
+
static description = `Without a version specified, install the latest version of the app.
|
|
11
|
+
|
|
12
|
+
Without a store specified, install the app in the Live environment of the current store.`;
|
|
13
|
+
static examples = [
|
|
14
|
+
'swell install',
|
|
15
|
+
'swell install -v 3.2.7',
|
|
16
|
+
'swell install -v 1.0.0 -s my-store-id',
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
env: Flags.string({
|
|
20
|
+
char: 'e',
|
|
21
|
+
description: 'target environment to install the app, defaults to live',
|
|
22
|
+
}),
|
|
23
|
+
store: Flags.string({
|
|
24
|
+
char: 's',
|
|
25
|
+
description: 'target store id to install the app, defaults to the current store',
|
|
26
|
+
}),
|
|
27
|
+
version: Flags.string({
|
|
28
|
+
char: 'v',
|
|
29
|
+
description: 'version of the app to install, defaults to latest',
|
|
30
|
+
async parse(input) {
|
|
31
|
+
if (!semver.valid(input)) {
|
|
32
|
+
throw new Error(`Invalid version: ${input}`);
|
|
33
|
+
}
|
|
34
|
+
return input;
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
static summary = 'Install an existing app in another store environment.';
|
|
39
|
+
async run() {
|
|
40
|
+
const { flags } = await this.parse(AppInstall);
|
|
41
|
+
const { env, store, version } = flags;
|
|
42
|
+
this.app = await this.getAppWithConfig();
|
|
43
|
+
const versions = await this.getVersions(version);
|
|
44
|
+
if (versions.length === 0) {
|
|
45
|
+
this.error(`${version ? `Version ${version} not found` : 'No versions found'} for ${style.appConfigValue(this.app.name)} app. ${version
|
|
46
|
+
? ''
|
|
47
|
+
: `Create a version with the ${style.command('swell version')} command first.`}`);
|
|
48
|
+
}
|
|
49
|
+
const versionToInstall = version
|
|
50
|
+
? versions.find((ver) => ver.version === version)?.version
|
|
51
|
+
: versions[0].version;
|
|
52
|
+
let storeToInstall = store;
|
|
53
|
+
if (!storeToInstall) {
|
|
54
|
+
const loggedInStores = config.get('stores') || [];
|
|
55
|
+
if (loggedInStores.length > 0) {
|
|
56
|
+
storeToInstall = await selectLoggedInStoreId('Which store do you want to install the app in?');
|
|
57
|
+
}
|
|
58
|
+
if (!storeToInstall) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
let envToInstall = env;
|
|
63
|
+
if (!envToInstall) {
|
|
64
|
+
envToInstall = await selectEnvironmentId();
|
|
65
|
+
}
|
|
66
|
+
if (envToInstall === 'live') {
|
|
67
|
+
envToInstall = undefined;
|
|
68
|
+
}
|
|
69
|
+
if (envToInstall && envToInstall !== 'test') {
|
|
70
|
+
this.error(`${envToInstall} is not a valid environment.`);
|
|
71
|
+
}
|
|
72
|
+
if (envToInstall === 'test' && storeToInstall === this.app.client_id) {
|
|
73
|
+
this.error(`You cannot install an app in the test environment of the same store.`);
|
|
74
|
+
}
|
|
75
|
+
config.ensureLoggedIn(storeToInstall);
|
|
76
|
+
this.api.setStoreEnv(storeToInstall, envToInstall);
|
|
77
|
+
// check if app is already installed on store
|
|
78
|
+
const existingInstalledApp = await this.getInstalledApp();
|
|
79
|
+
const existingVersion = existingInstalledApp?.id
|
|
80
|
+
? existingInstalledApp.version
|
|
81
|
+
: null;
|
|
82
|
+
// confirm install unless params were passed explicitly
|
|
83
|
+
let extraMessage = '';
|
|
84
|
+
if (!store || !env) {
|
|
85
|
+
const continueInstall = await confirm({
|
|
86
|
+
message: this.installConfirmationMessage(storeToInstall, envToInstall, versionToInstall, existingVersion),
|
|
87
|
+
});
|
|
88
|
+
if (!continueInstall) {
|
|
89
|
+
// we don't want to throw an error as it is a valid exit
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
extraMessage = ` ${style.appConfigValue(this.app.name)} v${versionToInstall} on ${style.storeId(storeToInstall)} [${envToInstall ? envToInstall : 'live'}]`;
|
|
95
|
+
}
|
|
96
|
+
const shouldContinue = await this.confirmRemoveDevelopmentApp(storeToInstall);
|
|
97
|
+
if (!shouldContinue) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const spinner = ora();
|
|
101
|
+
// install or update app depending on whether it's already installed
|
|
102
|
+
let request;
|
|
103
|
+
if (existingInstalledApp?.id) {
|
|
104
|
+
spinner.start(`Updating${extraMessage}...`);
|
|
105
|
+
request = async () => this.updateApp(versionToInstall, envToInstall);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
spinner.start(`Installing${extraMessage}...`);
|
|
109
|
+
request = async () => this.installApp(versionToInstall, envToInstall);
|
|
110
|
+
}
|
|
111
|
+
const response = await this.handleRequestErrors(request, () => spinner.fail('Something went wrong.'));
|
|
112
|
+
if (response?.errors) {
|
|
113
|
+
spinner.fail(`Error installing app: ${JSON.stringify(response.errors)}`);
|
|
114
|
+
}
|
|
115
|
+
spinner.succeed(`Install complete. Manage app settings in your dashboard at ${style.link(this.dashboardUrl({ flags: { env: envToInstall } }, storeToInstall))}.`);
|
|
116
|
+
}
|
|
117
|
+
async installApp(version, env) {
|
|
118
|
+
return this.api.post({ adminPath: `/client/apps` },
|
|
119
|
+
// eslint-disable-next-line camelcase
|
|
120
|
+
{ body: { app_id: this.app.id, version } });
|
|
121
|
+
}
|
|
122
|
+
installConfirmationMessage(storeToInstall, envToInstall, versionToInstall, existingVersion) {
|
|
123
|
+
const message = [];
|
|
124
|
+
if (existingVersion && semver.gt(versionToInstall, existingVersion)) {
|
|
125
|
+
message.push('Updating');
|
|
126
|
+
}
|
|
127
|
+
else if (existingVersion &&
|
|
128
|
+
semver.lt(versionToInstall, existingVersion)) {
|
|
129
|
+
message.push('Rolling back');
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
message.push('Install');
|
|
133
|
+
}
|
|
134
|
+
let versionToShow = versionToInstall;
|
|
135
|
+
versionToShow += versionToInstall === this.app.version ? ' (latest)' : '';
|
|
136
|
+
message.push(style.appConfigValue(this.app.name), `v${versionToShow}`, `on ${style.storeId(storeToInstall)} [${envToInstall ? envToInstall : 'live'}]?`);
|
|
137
|
+
return message.join(' ');
|
|
138
|
+
}
|
|
139
|
+
async updateApp(version, env) {
|
|
140
|
+
await this.api.put({ adminPath: `/client/apps/${this.app.id}` }, { body: { version } });
|
|
141
|
+
}
|
|
142
|
+
async getInstalledApp() {
|
|
143
|
+
// check if app is already installed on store
|
|
144
|
+
return await this.api.get({ adminPath: `/client/apps/${this.app.id}` }, {});
|
|
145
|
+
}
|
|
146
|
+
async confirmRemoveDevelopmentApp(storeToInstall) {
|
|
147
|
+
const existingSourcedApps = await this.api.get({ adminPath: `/client/apps` }, {
|
|
148
|
+
query: { source_id: this.app.id },
|
|
149
|
+
});
|
|
150
|
+
if (existingSourcedApps.results[0]?.id) {
|
|
151
|
+
const continueInstall = await confirm({
|
|
152
|
+
message: `${style.storeId(storeToInstall)} has a development version of ${style.appConfigValue(this.app.name)}. Do you want to replace it with an installed instance?`,
|
|
153
|
+
default: false,
|
|
154
|
+
});
|
|
155
|
+
if (!continueInstall) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
// uninstall the existing app
|
|
159
|
+
const spinner = ora();
|
|
160
|
+
spinner.start('Removing development app...');
|
|
161
|
+
try {
|
|
162
|
+
await this.api.put({
|
|
163
|
+
adminPath: `/client/apps/${existingSourcedApps.results[0].app_id}`,
|
|
164
|
+
}, {
|
|
165
|
+
body: { uninstalled: true },
|
|
166
|
+
});
|
|
167
|
+
spinner.succeed('Development app removed.');
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
spinner.fail(`Error removing development app from ${style.storeId(storeToInstall)}.`);
|
|
171
|
+
this.logError(err.message);
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
2
|
+
export default class AppPush extends RemoteAppCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
file: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
7
|
+
};
|
|
8
|
+
static orientation: {
|
|
9
|
+
env: string;
|
|
10
|
+
};
|
|
11
|
+
static summary: string;
|
|
12
|
+
run(): Promise<void>;
|
|
13
|
+
private logDashboardUrl;
|
|
14
|
+
private pushConfigPath;
|
|
15
|
+
private pushFile;
|
|
16
|
+
private confirmRemoveInstalledApp;
|
|
17
|
+
}
|