@swell/cli 2.0.1-alpha.13 → 2.0.1-alpha.17
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/dist/app-command.d.ts +1 -1
- package/dist/app-command.js +8 -8
- package/dist/commands/app/dev.d.ts +18 -0
- package/dist/commands/app/dev.js +31 -0
- package/dist/commands/app/frontend/deploy.d.ts +17 -0
- package/dist/commands/app/frontend/deploy.js +44 -0
- package/dist/commands/app/frontend/dev.d.ts +21 -0
- package/dist/commands/app/frontend/dev.js +111 -0
- package/dist/commands/app/info.js +15 -8
- package/dist/commands/app/init.d.ts +12 -0
- package/dist/commands/app/init.js +34 -0
- package/dist/commands/app/install.d.ts +4 -4
- package/dist/commands/app/install.js +37 -42
- package/dist/commands/app/pull.d.ts +15 -0
- package/dist/commands/app/pull.js +50 -0
- package/dist/commands/app/push.d.ts +11 -14
- package/dist/commands/app/push.js +71 -143
- package/dist/commands/app/release.d.ts +13 -0
- package/dist/commands/app/release.js +150 -0
- package/dist/commands/app/version.d.ts +7 -6
- package/dist/commands/app/version.js +64 -43
- package/dist/commands/app/watch.d.ts +2 -4
- package/dist/commands/app/watch.js +3 -83
- package/dist/commands/create/app.d.ts +35 -11
- package/dist/commands/create/app.js +150 -144
- package/dist/commands/create/content.d.ts +2 -2
- package/dist/commands/create/content.js +10 -10
- package/dist/commands/create/frontend.d.ts +12 -0
- package/dist/commands/create/frontend.js +41 -0
- package/dist/commands/create/function.d.ts +3 -3
- package/dist/commands/create/function.js +22 -22
- package/dist/commands/create/model.d.ts +2 -2
- package/dist/commands/create/model.js +11 -11
- package/dist/commands/create/notification.d.ts +4 -4
- package/dist/commands/create/notification.js +38 -40
- package/dist/commands/login.d.ts +1 -1
- package/dist/commands/login.js +12 -9
- package/dist/commands/logs.js +2 -1
- package/dist/commands/theme/dev.d.ts +23 -0
- package/dist/commands/theme/dev.js +167 -0
- package/dist/commands/theme/init.d.ts +9 -0
- package/dist/commands/theme/init.js +15 -0
- package/dist/commands/theme/pull.d.ts +20 -0
- package/dist/commands/theme/pull.js +65 -0
- package/dist/commands/theme/push.d.ts +19 -0
- package/dist/commands/theme/push.js +92 -0
- package/dist/create-app-command.d.ts +20 -0
- package/dist/create-app-command.js +254 -0
- package/dist/create-collection-command.d.ts +9 -9
- package/dist/create-collection-command.js +23 -23
- package/dist/{create-command.d.ts → create-config-command.d.ts} +1 -1
- package/dist/{create-command.js → create-config-command.js} +9 -7
- package/dist/lib/api.d.ts +6 -4
- package/dist/lib/api.js +58 -29
- package/dist/lib/app-config.d.ts +13 -5
- package/dist/lib/app-config.js +60 -23
- package/dist/lib/apps/app-config.d.ts +22 -8
- package/dist/lib/apps/app-config.js +202 -68
- package/dist/lib/apps/index.d.ts +93 -13
- package/dist/lib/apps/index.js +150 -50
- package/dist/lib/apps/paths.d.ts +14 -7
- package/dist/lib/apps/paths.js +73 -28
- package/dist/lib/bundle.js +4 -5
- package/dist/lib/config.d.ts +5 -1
- package/dist/lib/config.js +14 -0
- package/dist/lib/constants.d.ts +5 -0
- package/dist/lib/constants.js +22 -3
- package/dist/lib/create/content.d.ts +2 -2
- package/dist/lib/create/content.js +1 -1
- package/dist/lib/create/function.d.ts +2 -2
- package/dist/lib/create/function.js +2 -2
- package/dist/lib/create/index.d.ts +1 -1
- package/dist/lib/create/index.js +11 -7
- package/dist/lib/create/model.d.ts +2 -2
- package/dist/lib/create/notification.d.ts +2 -2
- package/dist/lib/info.js +2 -2
- package/dist/lib/sessions.js +2 -2
- package/dist/lib/stores.d.ts +5 -2
- package/dist/lib/stores.js +9 -6
- package/dist/lib/style.d.ts +2 -0
- package/dist/lib/style.js +2 -0
- package/dist/lib/swell-function-wrapper.d.ts +2 -2
- package/dist/lib/swell-function-wrapper.js +2 -2
- package/dist/push-app-command.d.ts +73 -0
- package/dist/push-app-command.js +765 -0
- package/dist/remote-app-command.d.ts +35 -9
- package/dist/remote-app-command.js +436 -164
- package/oclif.manifest.json +841 -125
- package/package.json +13 -1
- package/dist/commands/app/_pull.d.ts +0 -8
- package/dist/commands/app/_pull.js +0 -45
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class AppPush extends
|
|
3
|
-
static description: string;
|
|
4
|
-
static examples: string[];
|
|
1
|
+
import { PushAppCommand } from '../../push-app-command.js';
|
|
2
|
+
export default class AppPush extends PushAppCommand {
|
|
5
3
|
static args: {
|
|
6
4
|
file: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
7
5
|
};
|
|
8
|
-
static flags:
|
|
9
|
-
|
|
10
|
-
};
|
|
11
|
-
static orientation: {
|
|
12
|
-
env: string;
|
|
13
|
-
};
|
|
6
|
+
static flags: any;
|
|
7
|
+
static orientation: any;
|
|
14
8
|
static summary: string;
|
|
9
|
+
static description: string;
|
|
10
|
+
static examples: string[];
|
|
11
|
+
static delayOrientation: boolean;
|
|
15
12
|
run(): Promise<void>;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
resolvePushPaths(file: string): {
|
|
14
|
+
filePath: string;
|
|
15
|
+
relativePath: string;
|
|
16
|
+
};
|
|
20
17
|
}
|
|
@@ -1,171 +1,99 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Flags, Args } from '@oclif/core';
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
3
2
|
import * as path from 'node:path';
|
|
4
|
-
import
|
|
5
|
-
import { appConfigFileExists } from '../../lib/app-config.js';
|
|
6
|
-
import { ConfigPaths, allConfigFilesPathsByType, appConfigFromFile, getConfigTypeFromPath, getConfigTypeKeyFromValue, } from '../../lib/apps/index.js';
|
|
3
|
+
import { ConfigPaths, isPathDirectory, filePathExists, } from '../../lib/apps/index.js';
|
|
7
4
|
import style from '../../lib/style.js';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
export default class AppPush extends RemoteAppCommand {
|
|
11
|
-
static description = `Push all app configurations, a specific file, or a specific configuration
|
|
12
|
-
type to an app in your store's test environment. If the app does not exist yet,
|
|
13
|
-
it will be created and its global ID saved to a .swellrc file.
|
|
14
|
-
|
|
15
|
-
- If no file is specified, all configuration files will be pushed to the store.
|
|
16
|
-
This includes the app icon (assets/icon.png) and swell.json.
|
|
17
|
-
- If a file is specified, only that file will be pushed to the store.
|
|
18
|
-
- If a directory is specified, only files in that directory will be pushed.
|
|
19
|
-
|
|
20
|
-
App configuration directories:
|
|
21
|
-
${Object.values(ConfigPaths)
|
|
22
|
-
.map((dir) => style.path(`${dir}/`))
|
|
23
|
-
.join('\n')}`;
|
|
24
|
-
static examples = [
|
|
25
|
-
'swell app push',
|
|
26
|
-
'swell app push -f content',
|
|
27
|
-
'swell app push -f models/products.json',
|
|
28
|
-
];
|
|
5
|
+
import { PushAppCommand } from '../../push-app-command.js';
|
|
6
|
+
export default class AppPush extends PushAppCommand {
|
|
29
7
|
static args = {
|
|
30
8
|
file: Args.string({
|
|
31
9
|
char: 'f',
|
|
32
10
|
description: 'relative path to a configuration file or directory to push',
|
|
33
11
|
}),
|
|
34
12
|
};
|
|
35
|
-
// Deprecating the flags in favor of args
|
|
36
13
|
static flags = {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
14
|
+
force: Flags.boolean({
|
|
15
|
+
description: 'force push all configurations',
|
|
16
|
+
default: false,
|
|
17
|
+
}),
|
|
18
|
+
'no-deploy': Flags.boolean({
|
|
19
|
+
description: 'skip deploying app frontend',
|
|
40
20
|
}),
|
|
41
21
|
};
|
|
42
22
|
static orientation = {
|
|
43
23
|
env: 'test',
|
|
44
24
|
};
|
|
45
|
-
static summary = 'Push
|
|
25
|
+
static summary = 'Push local files to your Swell app.';
|
|
26
|
+
static description = `Push all app files, a specific file, or a specific configuration
|
|
27
|
+
type to an app in your store's test environment.
|
|
28
|
+
|
|
29
|
+
If the app does not exist, it will be created and its global ID saved to a .swellrc file.
|
|
30
|
+
|
|
31
|
+
- If no file is specified, all configuration files will be pushed to the store.
|
|
32
|
+
This includes the app icon (assets/icon.png) and swell.json.
|
|
33
|
+
- If a file is specified, only that file will be pushed to the store.
|
|
34
|
+
- If a directory is specified, only files in that directory will be pushed.
|
|
35
|
+
|
|
36
|
+
App file directories:
|
|
37
|
+
${Object.values(ConfigPaths)
|
|
38
|
+
.map((dir) => style.path(`${dir}/`))
|
|
39
|
+
.join('\n')}`;
|
|
40
|
+
static examples = [
|
|
41
|
+
'swell app push',
|
|
42
|
+
'swell app push content',
|
|
43
|
+
'swell app push models/products.json',
|
|
44
|
+
];
|
|
45
|
+
static delayOrientation = true;
|
|
46
46
|
async run() {
|
|
47
47
|
const { args, flags } = await this.parse(AppPush);
|
|
48
|
-
const { file
|
|
49
|
-
const {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const confirmExistingApp = await this.confirmRemoveInstalledApp(currentStore);
|
|
58
|
-
if (confirmExistingApp === false) {
|
|
48
|
+
const { file } = args;
|
|
49
|
+
const { force } = flags;
|
|
50
|
+
const noDeploy = flags['no-deploy'];
|
|
51
|
+
if (this.swellConfig.store?.type === 'theme') {
|
|
52
|
+
await this.config.runCommand('theme:push', [
|
|
53
|
+
...(file ? [file] : []),
|
|
54
|
+
...(force ? ['--force'] : []),
|
|
55
|
+
'--sync-app',
|
|
56
|
+
]);
|
|
59
57
|
return;
|
|
60
58
|
}
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
const otherAppName = this.app.name || this.appConfig.get('name');
|
|
65
|
-
const confirmPushNew = await confirm({
|
|
66
|
-
message: `${style.appConfigValue(otherAppName)} has a dev version in another environment. Do you want to create a new development instance in ${style.storeId(currentStore)}?`,
|
|
67
|
-
default: false,
|
|
68
|
-
});
|
|
69
|
-
if (!confirmPushNew) {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// create a new app
|
|
74
|
-
this.app = await this.getCreateUpdateApp();
|
|
75
|
-
if (!this.app) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
else if (!file) {
|
|
80
|
-
// update existing app if not targeting a file
|
|
81
|
-
this.app = await this.getCreateUpdateApp(this.app);
|
|
59
|
+
await this.logOrientation();
|
|
60
|
+
if (!(await this.ensureAppExists(file))) {
|
|
61
|
+
return;
|
|
82
62
|
}
|
|
83
63
|
if (file) {
|
|
84
|
-
const filePath =
|
|
85
|
-
if
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
64
|
+
const { filePath, relativePath } = this.resolvePushPaths(file);
|
|
65
|
+
// Push individual files if not referring to the whole app path
|
|
66
|
+
if (filePath !== this.appPath) {
|
|
67
|
+
if (!filePathExists(filePath)) {
|
|
68
|
+
this.error(`Path ${style.path(file)} does not exist.`);
|
|
69
|
+
}
|
|
70
|
+
// Targeting a directory
|
|
71
|
+
if (isPathDirectory(filePath)) {
|
|
72
|
+
await this.pushFilePath(relativePath, force);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
await this.pushFile(relativePath);
|
|
76
|
+
}
|
|
77
|
+
this.log();
|
|
78
|
+
this.logDashboardUrl(flags);
|
|
94
79
|
return;
|
|
95
80
|
}
|
|
96
|
-
await this.pushFile(filePath);
|
|
97
|
-
this.logDashboardUrl(flags, currentStore);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
const syncResult = await this.syncAppConfigs();
|
|
101
|
-
const { installed, removed } = syncResult;
|
|
102
|
-
this.log(`Pushed ${installed.length} ${installed.length === 1 ? 'configuration' : 'configurations'}${removed.length > 0 ? ` (removed ${removed.length}).` : '.'}`);
|
|
103
|
-
this.logDashboardUrl(flags, currentStore);
|
|
104
|
-
}
|
|
105
|
-
logDashboardUrl(flags, storeId) {
|
|
106
|
-
this.log(`View the ${style.appConfigValue(this.app.name)} app in your dashboard at ${style.link(this.dashboardUrl(flags, storeId))}.`);
|
|
107
|
-
}
|
|
108
|
-
async pushConfigPath(path) {
|
|
109
|
-
const configType = getConfigTypeFromPath(path);
|
|
110
|
-
const configTypeKey = getConfigTypeKeyFromValue(configType);
|
|
111
|
-
if (!configTypeKey) {
|
|
112
|
-
this.error(`Invalid config path: ${path}`);
|
|
113
81
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
await this.pushConfigFile(appConfig);
|
|
119
|
-
pushedFiles += 1;
|
|
120
|
-
}
|
|
121
|
-
if (pushedFiles === 0) {
|
|
122
|
-
this.log(`No valid files to push found for ${style.path(path)}.`);
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
this.log(`Pushed ${style.path(path)} to ${style.appConfigValue(this.app.name)}.`);
|
|
82
|
+
await this.pushAppConfigs(force);
|
|
83
|
+
if (!noDeploy) {
|
|
84
|
+
await this.deployAppFrontend(force);
|
|
85
|
+
this.log();
|
|
126
86
|
}
|
|
87
|
+
this.logDashboardUrl(flags);
|
|
127
88
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
async confirmRemoveInstalledApp(currentStore) {
|
|
137
|
-
if (!this.app.private_id) {
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
const existingInstalledApp = await this.api.get({
|
|
141
|
-
adminPath: `/client/apps/${this.app.private_id}`,
|
|
142
|
-
}, { query: { versioned: true } });
|
|
143
|
-
if (existingInstalledApp) {
|
|
144
|
-
const continuePush = await confirm({
|
|
145
|
-
message: `${style.storeId(currentStore)} has an installed version of ${style.appConfigValue(this.app.name)} v${existingInstalledApp.version}. Do you want to replace it with a development instance?`,
|
|
146
|
-
default: false,
|
|
147
|
-
});
|
|
148
|
-
if (!continuePush) {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
// uninstall the existing app
|
|
152
|
-
const spinner = ora();
|
|
153
|
-
spinner.start('Removing installed app...');
|
|
154
|
-
try {
|
|
155
|
-
await this.api.put({
|
|
156
|
-
adminPath: `/client/apps/${this.app.id}`,
|
|
157
|
-
}, {
|
|
158
|
-
body: { uninstalled: true },
|
|
159
|
-
});
|
|
160
|
-
spinner.succeed('Installed app removed.');
|
|
161
|
-
this.log();
|
|
162
|
-
}
|
|
163
|
-
catch (err) {
|
|
164
|
-
spinner.fail(`Error removing installed app from ${style.storeId(currentStore)}.`);
|
|
165
|
-
this.logError(err.message);
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
89
|
+
resolvePushPaths(file) {
|
|
90
|
+
// Join the appPath and file to get the absolute filePath
|
|
91
|
+
const filePath = path.resolve(process.cwd(), file);
|
|
92
|
+
// Check if filePath is outside of appPath
|
|
93
|
+
const relativePath = path.relative(this.appPath, filePath);
|
|
94
|
+
if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
|
|
95
|
+
this.error(`Path ${style.path(file)} is outside of the current app directory.`);
|
|
168
96
|
}
|
|
169
|
-
return
|
|
97
|
+
return { filePath, relativePath };
|
|
170
98
|
}
|
|
171
99
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { PushAppCommand } from '../../push-app-command.js';
|
|
2
|
+
export default class AppRelease extends PushAppCommand {
|
|
3
|
+
static args: {
|
|
4
|
+
version: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
5
|
+
};
|
|
6
|
+
static flags: any;
|
|
7
|
+
static summary: string;
|
|
8
|
+
static description: string;
|
|
9
|
+
static examples: string[];
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
private validateCreateVersion;
|
|
12
|
+
validateReleaseDetails(): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { Args, Flags } from '@oclif/core';
|
|
2
|
+
import { CLIError } from '@oclif/errors';
|
|
3
|
+
import { confirm } from '@inquirer/prompts';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import * as fs from 'node:fs';
|
|
6
|
+
import * as semver from 'semver';
|
|
7
|
+
import style from '../../lib/style.js';
|
|
8
|
+
import { PushAppCommand } from '../../push-app-command.js';
|
|
9
|
+
export default class AppRelease extends PushAppCommand {
|
|
10
|
+
static args = {
|
|
11
|
+
version: Args.string({
|
|
12
|
+
description: 'semver version to release',
|
|
13
|
+
async parse(input) {
|
|
14
|
+
const parsedInput = semver.valid(input);
|
|
15
|
+
if (parsedInput) {
|
|
16
|
+
return parsedInput;
|
|
17
|
+
}
|
|
18
|
+
throw new CLIError(`Invalid version: ${input}`);
|
|
19
|
+
},
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
static flags = {
|
|
23
|
+
'release-notes': Flags.string({
|
|
24
|
+
description: 'relative path to a file containing release notes',
|
|
25
|
+
}),
|
|
26
|
+
message: Flags.string({
|
|
27
|
+
char: 'm',
|
|
28
|
+
description: 'description of the changes in this version for internal use',
|
|
29
|
+
}),
|
|
30
|
+
amend: Flags.boolean({
|
|
31
|
+
description: 'amend the description or release notes of an existing release',
|
|
32
|
+
}),
|
|
33
|
+
'not-supported': Flags.boolean({
|
|
34
|
+
description: 'indicates the version is not officially supported by the developer',
|
|
35
|
+
}),
|
|
36
|
+
'no-git-tag': Flags.boolean({
|
|
37
|
+
description: 'if the version is being created, do not create a git tag',
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
static summary = 'Release a new version of your an app for publishing to the App Store.';
|
|
41
|
+
static description = `App released are reviewed by the Swell team for quality and functionality before publishing.
|
|
42
|
+
|
|
43
|
+
If the version does not exist, you will be prompted to create it.
|
|
44
|
+
|
|
45
|
+
Use the --amend flag to update the description or release notes of an existing release version.
|
|
46
|
+
|
|
47
|
+
Use the --supported=false flag to indicate the version is not officially supported by the developer.
|
|
48
|
+
|
|
49
|
+
The app must be connected to a partner account with access to publish.`;
|
|
50
|
+
static examples = [
|
|
51
|
+
'swell app release',
|
|
52
|
+
'swell app release 2.0.0',
|
|
53
|
+
'swell app release 2.0.0 --amend --release-notes ./releases/v2.0.0.md',
|
|
54
|
+
];
|
|
55
|
+
async run() {
|
|
56
|
+
const { args, flags } = await this.parse(AppRelease);
|
|
57
|
+
const { amend, 'release-notes': releaseNotesFile, 'not-supported': notSupported, } = flags;
|
|
58
|
+
if (!(await this.ensureAppExists(undefined, false))) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
let releaseNotes;
|
|
62
|
+
if (releaseNotesFile) {
|
|
63
|
+
try {
|
|
64
|
+
releaseNotes = fs.readFileSync(releaseNotesFile, 'utf8');
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
this.error(`Release notes could not be loaded from \`${releaseNotesFile}\``);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const appVersion = await this.validateCreateVersion(args.version, flags);
|
|
71
|
+
if (!appVersion) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (appVersion.released && !amend) {
|
|
75
|
+
throw new Error(`Version ${appVersion.version} has already been released. Use --amend to update version details.`);
|
|
76
|
+
}
|
|
77
|
+
const releaseData = {
|
|
78
|
+
description: flags.message ? flags.message : appVersion.description,
|
|
79
|
+
release_notes: releaseNotesFile ? releaseNotes : appVersion.release_notes,
|
|
80
|
+
supported: notSupported
|
|
81
|
+
? false
|
|
82
|
+
: appVersion.supported === undefined
|
|
83
|
+
? true
|
|
84
|
+
: appVersion.supported,
|
|
85
|
+
};
|
|
86
|
+
if (!amend) {
|
|
87
|
+
const continueVersion = await confirm({
|
|
88
|
+
message: `Release ${style.appConfigValue(this.app.name)} version ${appVersion.version}?`,
|
|
89
|
+
});
|
|
90
|
+
if (!continueVersion) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const spinner = ora();
|
|
95
|
+
this.log();
|
|
96
|
+
spinner.start(`${amend ? 'Updating release' : 'Releasing'} version ${style.appConfigValue(appVersion.version)}`);
|
|
97
|
+
await this.updateVersion(appVersion.version, {
|
|
98
|
+
...releaseData,
|
|
99
|
+
released: true,
|
|
100
|
+
}, spinner);
|
|
101
|
+
spinner.succeed(`${style.appConfigValue(this.app.name)} release ${appVersion.version} ${amend ? 'updated' : 'released'}.`);
|
|
102
|
+
}
|
|
103
|
+
async validateCreateVersion(inputVersion, flags) {
|
|
104
|
+
let version = inputVersion;
|
|
105
|
+
if (!version) {
|
|
106
|
+
version = this.app.version;
|
|
107
|
+
if (!version) {
|
|
108
|
+
throw new Error('No version specified');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
this.validateReleaseDetails();
|
|
112
|
+
let appVersion = await this.getVersion(version);
|
|
113
|
+
if (!appVersion) {
|
|
114
|
+
if (!flags.amend) {
|
|
115
|
+
await this.config.runCommand('app:version', [
|
|
116
|
+
version,
|
|
117
|
+
...(flags.message ? [`-m ${JSON.stringify(flags.message)}`] : []),
|
|
118
|
+
...(flags['no-git-tag'] ? ['--no-git-tag'] : []),
|
|
119
|
+
]);
|
|
120
|
+
this.log();
|
|
121
|
+
appVersion = await this.getVersion(version);
|
|
122
|
+
if (!appVersion) {
|
|
123
|
+
throw new Error(`An error occurred while creating version ${version}.`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
throw new Error(`Version ${version} does not exist.`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return appVersion;
|
|
131
|
+
}
|
|
132
|
+
validateReleaseDetails() {
|
|
133
|
+
if (!this.app.description) {
|
|
134
|
+
throw new Error('App must have a description');
|
|
135
|
+
}
|
|
136
|
+
const requiresPreviewImages = this.app.type === 'storefront' &&
|
|
137
|
+
(!this.app.kind || this.app.kind === 'shop') &&
|
|
138
|
+
this.app.storefront?.theme.provider !== 'app';
|
|
139
|
+
if (requiresPreviewImages) {
|
|
140
|
+
if (!this.app.preview_image) {
|
|
141
|
+
throw new Error('Storefront app must have a preview image to release');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
if (this.app.type !== 'theme' && !this.app.logo_icon) {
|
|
146
|
+
throw new Error('App must have a logo icon to release');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export default class AppVersion extends
|
|
1
|
+
import { PushAppCommand } from '../../push-app-command.js';
|
|
2
|
+
export default class AppVersion extends PushAppCommand {
|
|
3
3
|
static args: {
|
|
4
4
|
nextVersion: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
5
5
|
};
|
|
6
|
-
static description: string;
|
|
7
|
-
static examples: string[];
|
|
8
6
|
static flags: {
|
|
9
|
-
'git-tag': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
10
7
|
message: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
|
+
amend: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
9
|
+
'no-git-tag': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
11
10
|
};
|
|
12
11
|
static summary: string;
|
|
12
|
+
static description: string;
|
|
13
|
+
static examples: string[];
|
|
13
14
|
run(): Promise<void>;
|
|
14
15
|
private gitCommit;
|
|
15
16
|
private gitTag;
|
|
16
|
-
private
|
|
17
|
+
private createRemoteVersion;
|
|
17
18
|
private validateAndComputeVersion;
|
|
18
19
|
}
|
|
@@ -2,10 +2,10 @@ import { confirm, input } from '@inquirer/prompts';
|
|
|
2
2
|
import git from '@npmcli/git';
|
|
3
3
|
import { Args, Flags } from '@oclif/core';
|
|
4
4
|
import { CLIError } from '@oclif/errors';
|
|
5
|
-
import * as semver from 'semver';
|
|
6
5
|
import ora from 'ora';
|
|
6
|
+
import * as semver from 'semver';
|
|
7
7
|
import style from '../../lib/style.js';
|
|
8
|
-
import {
|
|
8
|
+
import { PushAppCommand } from '../../push-app-command.js';
|
|
9
9
|
const RELEASE_TYPES = [
|
|
10
10
|
'major',
|
|
11
11
|
'premajor',
|
|
@@ -15,8 +15,7 @@ const RELEASE_TYPES = [
|
|
|
15
15
|
'prepatch',
|
|
16
16
|
'prerelease',
|
|
17
17
|
];
|
|
18
|
-
|
|
19
|
-
export default class AppVersion extends RemoteAppCommand {
|
|
18
|
+
export default class AppVersion extends PushAppCommand {
|
|
20
19
|
static args = {
|
|
21
20
|
nextVersion: Args.string({
|
|
22
21
|
description: `semver version or release type: ${[
|
|
@@ -35,6 +34,19 @@ export default class AppVersion extends RemoteAppCommand {
|
|
|
35
34
|
},
|
|
36
35
|
}),
|
|
37
36
|
};
|
|
37
|
+
static flags = {
|
|
38
|
+
message: Flags.string({
|
|
39
|
+
char: 'm',
|
|
40
|
+
description: 'description of the changes in this version for internal use',
|
|
41
|
+
}),
|
|
42
|
+
amend: Flags.boolean({
|
|
43
|
+
description: 'amend the description an existing version',
|
|
44
|
+
}),
|
|
45
|
+
'no-git-tag': Flags.boolean({
|
|
46
|
+
description: 'do not create a git tag for this version',
|
|
47
|
+
}),
|
|
48
|
+
};
|
|
49
|
+
static summary = 'Increment the version of your Swell app.';
|
|
38
50
|
static description = `When executed without specifying NEXTVERSION, the command will display the current
|
|
39
51
|
version of your app.
|
|
40
52
|
|
|
@@ -45,47 +57,51 @@ The version change will be committed to the Git repository using the provided
|
|
|
45
57
|
message or a default similar to the tag, e.g. "v1.0.1".
|
|
46
58
|
|
|
47
59
|
By default, it creates a Git tag for the specified version. You can disable this
|
|
48
|
-
behavior by using the --git-tag
|
|
60
|
+
behavior by using the --no-git-tag option.`;
|
|
49
61
|
static examples = [
|
|
50
62
|
'swell app version',
|
|
51
63
|
'swell app version minor',
|
|
52
64
|
'swell app version 1.0.0 -m "First release"',
|
|
53
65
|
];
|
|
54
|
-
static flags = {
|
|
55
|
-
'git-tag': Flags.boolean({
|
|
56
|
-
default: true,
|
|
57
|
-
description: 'create a git tag for this version (default true)',
|
|
58
|
-
}),
|
|
59
|
-
message: Flags.string({
|
|
60
|
-
char: 'm',
|
|
61
|
-
description: 'description of the changes in this version',
|
|
62
|
-
}),
|
|
63
|
-
};
|
|
64
|
-
static summary = 'Increment the version of your Swell app.';
|
|
65
66
|
async run() {
|
|
66
67
|
const { args, flags } = await this.parse(AppVersion);
|
|
67
|
-
const { 'git-tag':
|
|
68
|
+
const { amend, 'no-git-tag': noGitTag } = flags;
|
|
68
69
|
let { message } = flags;
|
|
69
70
|
let { nextVersion } = args;
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
if (!(await this.ensureAppExists(undefined, false))) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
72
74
|
const remoteVersion = this.app.version;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
if (amend) {
|
|
76
|
+
const amendVersion = nextVersion || remoteVersion;
|
|
77
|
+
const appVersion = await this.getVersion(amendVersion);
|
|
78
|
+
if (appVersion) {
|
|
79
|
+
await this.updateVersion(amendVersion, {
|
|
80
|
+
description: message,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
this.error(`Version ${amendVersion} does not exist yet.`);
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// If the user didn't specify a version, show the latest one
|
|
75
89
|
if (!nextVersion) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
this.log(`${style.appConfigValue(this.app.name)} latest version is ${style.appConfigValue(
|
|
90
|
+
const latestVersion = await this.getVersion();
|
|
91
|
+
if (latestVersion) {
|
|
92
|
+
this.log(`${style.appConfigValue(this.app.name)} latest remote version is ${style.appConfigValue(latestVersion.version)}.`);
|
|
79
93
|
}
|
|
80
94
|
else {
|
|
81
|
-
this.
|
|
95
|
+
this.error(`${style.appConfigValue(this.app.name)} has not been versioned yet.`);
|
|
82
96
|
}
|
|
83
97
|
return;
|
|
84
98
|
}
|
|
85
99
|
nextVersion = this.validateAndComputeVersion(nextVersion, remoteVersion);
|
|
100
|
+
await this.pushAppConfigs();
|
|
101
|
+
await this.deployAppFrontend(false, false);
|
|
86
102
|
if (!message) {
|
|
87
103
|
message = await input({
|
|
88
|
-
message:
|
|
104
|
+
message: `Describe the changes in version ${nextVersion}:`,
|
|
89
105
|
});
|
|
90
106
|
}
|
|
91
107
|
const continueVersion = await confirm({
|
|
@@ -95,18 +111,19 @@ behavior by using the --git-tag=false option.`;
|
|
|
95
111
|
return;
|
|
96
112
|
}
|
|
97
113
|
if (!message) {
|
|
98
|
-
message =
|
|
114
|
+
message = nextVersion;
|
|
99
115
|
}
|
|
116
|
+
this.log();
|
|
100
117
|
const spinner = ora();
|
|
101
|
-
spinner.start('Creating version
|
|
118
|
+
spinner.start('Creating version');
|
|
102
119
|
try {
|
|
103
|
-
await this.
|
|
120
|
+
await this.createRemoteVersion(nextVersion, message, spinner);
|
|
104
121
|
await this.gitCommit(message);
|
|
105
|
-
if (
|
|
122
|
+
if (!noGitTag)
|
|
106
123
|
await this.gitTag(nextVersion);
|
|
107
124
|
}
|
|
108
|
-
catch (
|
|
109
|
-
spinner.fail(
|
|
125
|
+
catch (error) {
|
|
126
|
+
spinner.fail(error.message);
|
|
110
127
|
return;
|
|
111
128
|
}
|
|
112
129
|
spinner.succeed(`${style.appConfigValue(this.app.name)} version ${nextVersion} created.`);
|
|
@@ -114,30 +131,36 @@ behavior by using the --git-tag=false option.`;
|
|
|
114
131
|
async gitCommit(message) {
|
|
115
132
|
// check if there are changes in the app config file
|
|
116
133
|
// if there are no changes, there is nothing to commit
|
|
117
|
-
const configStatus = await git.spawn(['status', this.
|
|
118
|
-
cwd: this.
|
|
134
|
+
const configStatus = await git.spawn(['status', this.swellConfig.path], {
|
|
135
|
+
cwd: this.swellConfig.cwd,
|
|
119
136
|
});
|
|
120
137
|
if (configStatus.stdout.includes('nothing to commit')) {
|
|
121
138
|
return;
|
|
122
139
|
}
|
|
123
140
|
// add the app config file to commit - this will contain the new version
|
|
124
|
-
await git.spawn(['add', this.
|
|
141
|
+
await git.spawn(['add', this.swellConfig.path]);
|
|
125
142
|
// commit the version change to git. It skips the git hooks with -n
|
|
126
143
|
await git.spawn(['commit', '-n', '-m', message]);
|
|
127
144
|
}
|
|
128
145
|
async gitTag(nextVersion) {
|
|
129
|
-
const tag =
|
|
146
|
+
const tag = nextVersion;
|
|
130
147
|
// tag the version in git
|
|
131
148
|
await git.spawn(['tag', tag], { cwd: process.cwd() });
|
|
132
149
|
}
|
|
133
|
-
async
|
|
150
|
+
async createRemoteVersion(nextVersion, message, spinner) {
|
|
134
151
|
// update the version on the remote
|
|
135
|
-
const versionResponse = await this.api.post({ adminPath: `/apps/${this.app.id}/versions` }, {
|
|
152
|
+
const versionResponse = await this.api.post({ adminPath: `/apps/${this.app.id}/versions` }, {
|
|
153
|
+
body: { description: message, version: nextVersion },
|
|
154
|
+
spinner,
|
|
155
|
+
onAsyncGetPath: (response) => ({
|
|
156
|
+
adminPath: `/apps/${this.app.id}/versions/${response.id}`,
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
136
159
|
if (!versionResponse || !versionResponse.version) {
|
|
137
160
|
spinner.fail('Something went wrong.');
|
|
138
161
|
}
|
|
139
162
|
// update the local app config file since remote was successful
|
|
140
|
-
this.
|
|
163
|
+
this.swellConfig.set('version', nextVersion);
|
|
141
164
|
}
|
|
142
165
|
validateAndComputeVersion(nextVersion, remoteVersion) {
|
|
143
166
|
let version = nextVersion;
|
|
@@ -146,10 +169,8 @@ behavior by using the --git-tag=false option.`;
|
|
|
146
169
|
// increment the version
|
|
147
170
|
version = semver.inc(remoteVersion, version);
|
|
148
171
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
semver.lt(version, remoteVersion)) {
|
|
152
|
-
this.error(`The version you are trying to create ${version} needs to be greater than the current version ${remoteVersion}`);
|
|
172
|
+
if (semver.lt(version, remoteVersion)) {
|
|
173
|
+
this.error(`Version ${version} must be greater than the current version ${remoteVersion}.`);
|
|
153
174
|
}
|
|
154
175
|
return version;
|
|
155
176
|
}
|