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