@swell/cli 1.0.2 → 2.0.1-alpha.10
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 +2 -140
- 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 +179 -0
- package/dist/commands/app/push.d.ts +20 -0
- package/dist/commands/app/push.js +170 -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 +24 -0
- package/dist/commands/logs.js +212 -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 +126 -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 +170 -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 +43 -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 +22 -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 +388 -0
- package/dist/swell-command.d.ts +21 -0
- package/dist/swell-command.js +43 -0
- package/oclif.manifest.json +1090 -0
- package/package.json +81 -43
- 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
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { inflect } from 'inflection';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { AppCommand } from './app-command.js';
|
|
6
|
+
import { appConfigFileExists, readRcFile, writeRcFile, } from './lib/app-config.js';
|
|
7
|
+
import { FunctionProcessingError, IgnoringFileError, allConfigFilesPaths, appConfigFromFile, appLogoIcon, batchAppConfigsByType, configFilePath, isAppLogoIcon, } from './lib/apps/index.js';
|
|
8
|
+
import { toAppId } from './lib/create/index.js';
|
|
9
|
+
import { default as swellConfig } from './lib/config.js';
|
|
10
|
+
import { getLoginHost } from './lib/constants.js';
|
|
11
|
+
import style from './lib/style.js';
|
|
12
|
+
/**
|
|
13
|
+
* Extends the AppCommand with methods that ease managing remote apps and
|
|
14
|
+
* configurations.
|
|
15
|
+
*/
|
|
16
|
+
export class RemoteAppCommand extends AppCommand {
|
|
17
|
+
// allows child commands to set orientation
|
|
18
|
+
// most of the times this will be the target environment
|
|
19
|
+
static orientation;
|
|
20
|
+
// an API instance of the current app definition
|
|
21
|
+
app = {};
|
|
22
|
+
dashboardUrl(flags = {}, storeId) {
|
|
23
|
+
const { env } = flags;
|
|
24
|
+
const envFlag = env || this.ctor.orientation?.env;
|
|
25
|
+
const outputEnv = envFlag === 'live' ? '' : !envFlag ? '' : 'test';
|
|
26
|
+
return `${getLoginHost(storeId)}/admin/${outputEnv ? `${outputEnv}/` : ''}apps/${this.app.id}`;
|
|
27
|
+
}
|
|
28
|
+
async deleteConfig(configId) {
|
|
29
|
+
if (!this.app) {
|
|
30
|
+
this.error('App is not defined');
|
|
31
|
+
}
|
|
32
|
+
return this.api.delete({
|
|
33
|
+
adminPath: `/apps/${this.app.id}/configs/${configId}`,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async getApp(id = '') {
|
|
37
|
+
const app = await this.api.get({ adminPath: `/apps/${id}` });
|
|
38
|
+
// Get app configs for processing
|
|
39
|
+
app.configs = [];
|
|
40
|
+
const configsResponse = await this.api.get({ adminPath: `/apps/${id}/configs` }, { query: { limit: 1000 } });
|
|
41
|
+
this.debugJson('getApp: configsResponse', configsResponse);
|
|
42
|
+
// for each config, we check if there's a local file and we add it to the
|
|
43
|
+
// AppConfig object
|
|
44
|
+
for (const config of configsResponse.results || []) {
|
|
45
|
+
const localPath = configFilePath(this.appPath, config);
|
|
46
|
+
if (appConfigFileExists(localPath)) {
|
|
47
|
+
config.localPath = localPath;
|
|
48
|
+
}
|
|
49
|
+
app.configs.push(config);
|
|
50
|
+
}
|
|
51
|
+
this.debugJson('getApp: remote app', app);
|
|
52
|
+
return app;
|
|
53
|
+
}
|
|
54
|
+
async getAppWithConfig(id = '', errorNotFound = true) {
|
|
55
|
+
const swellId = this.app?.id;
|
|
56
|
+
const appConfigId = this.appConfig.get('id');
|
|
57
|
+
if (!appConfigId) {
|
|
58
|
+
this.error(`App ${style.appConfigName('id')} is not defined in swell.json.`);
|
|
59
|
+
}
|
|
60
|
+
let appId = id || swellId;
|
|
61
|
+
if (!appId) {
|
|
62
|
+
const privateId = this.app?.private_id;
|
|
63
|
+
if (!privateId) {
|
|
64
|
+
this.error(`App ${style.appConfigName('id')} is not defined in swell.json.`);
|
|
65
|
+
}
|
|
66
|
+
if (privateId !== appConfigId) {
|
|
67
|
+
this.error(`App ${style.appConfigName('id')} must be declared in snake_case: ${style.appConfigValue(appConfigId)}.`);
|
|
68
|
+
}
|
|
69
|
+
appId = privateId;
|
|
70
|
+
}
|
|
71
|
+
// we initialize the app with the id so we can use it in the API calls
|
|
72
|
+
let app = {};
|
|
73
|
+
try {
|
|
74
|
+
app = await this.getApp(appId);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
this.debug('getApp: app not found', error);
|
|
78
|
+
let errorJson;
|
|
79
|
+
try {
|
|
80
|
+
errorJson = JSON.parse(error.message);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// if there's a JSON parse error, throw the original error
|
|
84
|
+
this.error(error);
|
|
85
|
+
}
|
|
86
|
+
// if the app doesn't exist, we don't want to throw an error as we might
|
|
87
|
+
// want to create it.
|
|
88
|
+
//
|
|
89
|
+
// if the `id` was passed as argument though, we want to throw an error so
|
|
90
|
+
// we don't create a loop.
|
|
91
|
+
//
|
|
92
|
+
// we don't throw an error ever sometimes, like when pushing an app
|
|
93
|
+
const shouldThrow = !errorNotFound && (errorJson.error.message !== 'App not found' || id);
|
|
94
|
+
if (shouldThrow) {
|
|
95
|
+
const errorMessage = typeof errorJson.error === 'string'
|
|
96
|
+
? errorJson.error
|
|
97
|
+
: errorJson.error.message;
|
|
98
|
+
this.error(errorMessage);
|
|
99
|
+
}
|
|
100
|
+
if (swellId && swellId !== id && swellId !== this.app?.id) {
|
|
101
|
+
app = await this.getAppWithConfig(swellId);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return app;
|
|
105
|
+
}
|
|
106
|
+
async getCreateUpdateApp(updateApp) {
|
|
107
|
+
const spinner = ora();
|
|
108
|
+
let remoteApp;
|
|
109
|
+
try {
|
|
110
|
+
remoteApp = await readRcFile(this.swellRcPath());
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
if (error.code !== 'ENOENT') {
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/* eslint-disable camelcase */
|
|
118
|
+
const body = {
|
|
119
|
+
...this.appConfig.store,
|
|
120
|
+
// we store locally the private id as the id, but we need to switch it
|
|
121
|
+
// when posting to the API
|
|
122
|
+
id: undefined,
|
|
123
|
+
logo_icon: null,
|
|
124
|
+
...(this.appConfig.store.id
|
|
125
|
+
? { private_id: this.appConfig.store.id }
|
|
126
|
+
: undefined),
|
|
127
|
+
...(remoteApp?.id ? { source_id: remoteApp.id } : undefined),
|
|
128
|
+
};
|
|
129
|
+
const logoIcon = appLogoIcon(this.appPath);
|
|
130
|
+
if (logoIcon) {
|
|
131
|
+
const icon = fs.readFileSync(logoIcon, { encoding: 'base64' });
|
|
132
|
+
body.logo_icon = icon;
|
|
133
|
+
}
|
|
134
|
+
/* eslint-enable camelcase */
|
|
135
|
+
let app = updateApp || {};
|
|
136
|
+
let appCreated = false;
|
|
137
|
+
if (!app.id) {
|
|
138
|
+
// May already have a dev instance
|
|
139
|
+
let existingDevApp;
|
|
140
|
+
try {
|
|
141
|
+
existingDevApp = await this.api.get({
|
|
142
|
+
adminPath: `/apps/${this.appConfig.store.id}`,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
// noop
|
|
147
|
+
}
|
|
148
|
+
spinner.start(`Creating app...`);
|
|
149
|
+
if (existingDevApp) {
|
|
150
|
+
app = await this.handleRequestErrors(async () => this.api.put({ adminPath: `/apps/${existingDevApp.id}` }, { body }), () => spinner.fail('Error creating app'));
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
app = await this.handleRequestErrors(async () => this.api.post({ adminPath: '/apps' }, { body }), () => spinner.fail('Error creating app'));
|
|
154
|
+
appCreated = true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (app.id) {
|
|
158
|
+
spinner.start(`Updating app...`);
|
|
159
|
+
app = await this.handleRequestErrors(async () => this.api.put({ adminPath: `/apps/${app.id}` }, { body }), () => spinner.fail('Error updating app'));
|
|
160
|
+
}
|
|
161
|
+
if (app?.id) {
|
|
162
|
+
await writeRcFile(this.swellRcPath(), { id: app.id });
|
|
163
|
+
// if not failed, we want to display correct messaging
|
|
164
|
+
if (spinner.isSpinning) {
|
|
165
|
+
if (appCreated) {
|
|
166
|
+
spinner.succeed('Development app created.');
|
|
167
|
+
this.log();
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
spinner.stop();
|
|
171
|
+
}
|
|
172
|
+
this.log('App details');
|
|
173
|
+
spinner.succeed('app details');
|
|
174
|
+
if (logoIcon) {
|
|
175
|
+
spinner.succeed('app logo');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
this.log();
|
|
179
|
+
return app;
|
|
180
|
+
}
|
|
181
|
+
return {};
|
|
182
|
+
}
|
|
183
|
+
async handleRequestErrors(request, spinnerError) {
|
|
184
|
+
try {
|
|
185
|
+
const response = await request();
|
|
186
|
+
if (response?.status !== 200 && response?.errors) {
|
|
187
|
+
this.showErrors(response.errors, spinnerError);
|
|
188
|
+
this.error('API request failed');
|
|
189
|
+
}
|
|
190
|
+
return response;
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
let jsonError;
|
|
194
|
+
try {
|
|
195
|
+
jsonError = JSON.parse(error.message);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// noop, likely JSON parse error
|
|
199
|
+
}
|
|
200
|
+
if (jsonError && jsonError.error) {
|
|
201
|
+
this.showErrors(jsonError, spinnerError);
|
|
202
|
+
this.error('API request failed');
|
|
203
|
+
}
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async init() {
|
|
208
|
+
await super.init();
|
|
209
|
+
this.app = this.initApp();
|
|
210
|
+
const klass = this.ctor;
|
|
211
|
+
const { flags } = await this.parse(klass);
|
|
212
|
+
if (klass.orientation) {
|
|
213
|
+
this.showOrientation(klass.orientation, flags);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async postConfig(configJson) {
|
|
217
|
+
if (!this.app) {
|
|
218
|
+
this.error('App is not defined');
|
|
219
|
+
}
|
|
220
|
+
this.debug('postConfig: configJson', configJson);
|
|
221
|
+
return this.api.post({ adminPath: `/apps/${this.app.id}/configs` }, { body: configJson });
|
|
222
|
+
}
|
|
223
|
+
async pushConfigFile(appConfig) {
|
|
224
|
+
const { localPath } = appConfig;
|
|
225
|
+
const spinner = ora();
|
|
226
|
+
let postData;
|
|
227
|
+
if (!localPath) {
|
|
228
|
+
throw new Error('Missing local config path');
|
|
229
|
+
}
|
|
230
|
+
// we don't want to treat the app logo asset as config file
|
|
231
|
+
// it is dealt with at the app level
|
|
232
|
+
if (isAppLogoIcon(localPath)) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const pathDesc = localPath.split('/').slice(-2).join('/');
|
|
236
|
+
spinner.start(`Pushing ${pathDesc}`);
|
|
237
|
+
try {
|
|
238
|
+
postData = await appConfig.postData();
|
|
239
|
+
if (!postData) {
|
|
240
|
+
spinner.stop();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
if (error instanceof IgnoringFileError) {
|
|
246
|
+
spinner.fail(`Ignoring file: ${error.message} in ${localPath}`);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (error instanceof FunctionProcessingError) {
|
|
250
|
+
this.debug(error);
|
|
251
|
+
this.debug(error.originalError);
|
|
252
|
+
spinner.fail(`${error.message} ${error.originalError?.message?.toLowerCase()}`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
spinner.fail(error.message);
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
const result = await this.handleRequestErrors(async () => this.postConfig(postData), () => spinner.fail(`Error pushing ${pathDesc}`));
|
|
259
|
+
spinner.succeed(`Pushed ${pathDesc}`);
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
async removeConfig(config) {
|
|
263
|
+
const spinner = ora();
|
|
264
|
+
spinner.start(`Removing ${config.type} ${config.name}`);
|
|
265
|
+
const result = await this.handleRequestErrors(async () => this.deleteConfig(config.id), () => spinner.fail(`Error removing ${config.type} ${config.name}`));
|
|
266
|
+
spinner.warn(`Removed ${config.type} ${config.name}`);
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
showErrors(errors, spinnerError) {
|
|
270
|
+
const actualErrors = Object.keys(errors).filter((err) => Object.prototype.hasOwnProperty.call(errors, err));
|
|
271
|
+
if (spinnerError) {
|
|
272
|
+
spinnerError();
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
this.logError(inflect('Error', actualErrors.length));
|
|
276
|
+
}
|
|
277
|
+
for (const err of actualErrors) {
|
|
278
|
+
// we need to display `id` instead of `private_id`
|
|
279
|
+
// developers don't have any knowledge of private ids
|
|
280
|
+
const displayErr = err === 'private_id' ? 'id' : err;
|
|
281
|
+
this.logError([
|
|
282
|
+
' -',
|
|
283
|
+
`${displayErr}:`.toLowerCase().replace(/^error:/, ''),
|
|
284
|
+
errors[err].message,
|
|
285
|
+
]
|
|
286
|
+
.filter(Boolean)
|
|
287
|
+
.join(' '));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
async syncAppConfigs() {
|
|
291
|
+
const configsToPush = [];
|
|
292
|
+
const installed = [];
|
|
293
|
+
const removed = [];
|
|
294
|
+
for (const { configFile, configType } of allConfigFilesPaths(this.appPath)) {
|
|
295
|
+
// skip logo icon as it is dealt with at the app level
|
|
296
|
+
if (isAppLogoIcon(configFile)) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
const appConfig = appConfigFromFile(configFile, configType);
|
|
300
|
+
configsToPush.push(appConfig);
|
|
301
|
+
}
|
|
302
|
+
const batches = batchAppConfigsByType(configsToPush);
|
|
303
|
+
for (const batch of batches) {
|
|
304
|
+
this.log(style.appConfigName(batch.label));
|
|
305
|
+
// eslint-disable-next-line no-await-in-loop
|
|
306
|
+
for (const appConfig of batch.configs) {
|
|
307
|
+
const installedConfig = await this.pushConfigFile(appConfig);
|
|
308
|
+
if (installedConfig)
|
|
309
|
+
installed.push(installedConfig.id);
|
|
310
|
+
}
|
|
311
|
+
this.log();
|
|
312
|
+
}
|
|
313
|
+
// pull the app again after the latest push
|
|
314
|
+
// this allows us to get the latest configs updates that the remote app
|
|
315
|
+
// might have done
|
|
316
|
+
this.app = await this.getAppWithConfig(this.app.id);
|
|
317
|
+
this.debugJson('syncAppConfigs: app', this.app);
|
|
318
|
+
// if the remote app has configs that are not in the local app, we want to
|
|
319
|
+
// remove them
|
|
320
|
+
for (const config of this.app?.configs || []) {
|
|
321
|
+
const appConfig = config;
|
|
322
|
+
if (!appConfig.localPath) {
|
|
323
|
+
// eslint-disable-next-line no-await-in-loop
|
|
324
|
+
await this.removeConfig(appConfig);
|
|
325
|
+
removed.push(appConfig.id);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return { installed, removed };
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Reads the .swellrc file and sets up the remote app configuration.
|
|
332
|
+
*
|
|
333
|
+
* @returns App
|
|
334
|
+
*/
|
|
335
|
+
initApp() {
|
|
336
|
+
let remoteApp = {};
|
|
337
|
+
try {
|
|
338
|
+
this.debug('initApp: swellRcPath', this.swellRcPath());
|
|
339
|
+
remoteApp = readRcFile(this.swellRcPath());
|
|
340
|
+
this.debug('initApp: remoteApp', remoteApp);
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
// if the file doesn't exist, we don't want to throw an error as we might
|
|
344
|
+
// want to create it later.
|
|
345
|
+
if (error.code !== 'ENOENT') {
|
|
346
|
+
throw error;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (this.appConfig.get('id')) {
|
|
350
|
+
// use the same formula as the backend api
|
|
351
|
+
// eslint-disable-next-line camelcase
|
|
352
|
+
remoteApp.private_id = toAppId(this.appConfig.get('id'));
|
|
353
|
+
}
|
|
354
|
+
return remoteApp;
|
|
355
|
+
}
|
|
356
|
+
showOrientation(orientation, flags) {
|
|
357
|
+
const orientationOutput = [];
|
|
358
|
+
const currentStore = swellConfig.getDefaultStore();
|
|
359
|
+
const { env, store } = flags;
|
|
360
|
+
if (store) {
|
|
361
|
+
orientationOutput.push(`[store] ${style.storeId(store)}`);
|
|
362
|
+
}
|
|
363
|
+
else if (currentStore) {
|
|
364
|
+
orientationOutput.push(`[store] ${style.storeId(currentStore)}`);
|
|
365
|
+
}
|
|
366
|
+
const outputEnv = env || orientation.env;
|
|
367
|
+
if (outputEnv) {
|
|
368
|
+
const outputStyle = outputEnv === 'live' ? style.appEnvLive : style.appEnv;
|
|
369
|
+
orientationOutput.push(`[env] ${outputStyle(outputEnv)}`);
|
|
370
|
+
}
|
|
371
|
+
this.log(orientationOutput.join(' '));
|
|
372
|
+
this.log();
|
|
373
|
+
}
|
|
374
|
+
async getVersions(version) {
|
|
375
|
+
const versionsResponse = await this.api.get({ adminPath: `/apps/${this.app.id}/versions` }, {
|
|
376
|
+
query: {
|
|
377
|
+
limit: 1000,
|
|
378
|
+
sort: 'version desc',
|
|
379
|
+
...(version ? { version } : undefined),
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
this.debugJson('getApp: versionsResponse', versionsResponse);
|
|
383
|
+
return versionsResponse.results || [];
|
|
384
|
+
}
|
|
385
|
+
swellRcPath() {
|
|
386
|
+
return path.join(this.appPath, '.swellrc');
|
|
387
|
+
}
|
|
388
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command, Config } from '@oclif/core';
|
|
2
|
+
import Api from './lib/api.js';
|
|
3
|
+
/**
|
|
4
|
+
* A base class for Swell CLI commands.
|
|
5
|
+
*
|
|
6
|
+
* This class extends the oclif Command class and adds:
|
|
7
|
+
*
|
|
8
|
+
* - an Api instance
|
|
9
|
+
* - logging methods
|
|
10
|
+
* - a catch method to handle errors
|
|
11
|
+
*/
|
|
12
|
+
export declare abstract class SwellCommand extends Command {
|
|
13
|
+
static examples: Command.Example[];
|
|
14
|
+
api: Api;
|
|
15
|
+
requiresAuth: boolean;
|
|
16
|
+
constructor(argv: string[], config: Config);
|
|
17
|
+
catch(error: any): Promise<void>;
|
|
18
|
+
debugJson(...args: any[]): void;
|
|
19
|
+
logError(message: string): void;
|
|
20
|
+
logSuccess(message: string): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import git from '@npmcli/git';
|
|
2
|
+
import { Command } from '@oclif/core';
|
|
3
|
+
import { FetchError } from 'node-fetch';
|
|
4
|
+
import Api from './lib/api.js';
|
|
5
|
+
import style from './lib/style.js';
|
|
6
|
+
const { GitUnknownError } = git.errors;
|
|
7
|
+
/**
|
|
8
|
+
* A base class for Swell CLI commands.
|
|
9
|
+
*
|
|
10
|
+
* This class extends the oclif Command class and adds:
|
|
11
|
+
*
|
|
12
|
+
* - an Api instance
|
|
13
|
+
* - logging methods
|
|
14
|
+
* - a catch method to handle errors
|
|
15
|
+
*/
|
|
16
|
+
export class SwellCommand extends Command {
|
|
17
|
+
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
18
|
+
api;
|
|
19
|
+
requiresAuth = true;
|
|
20
|
+
constructor(argv, config) {
|
|
21
|
+
super(argv, config);
|
|
22
|
+
this.api = new Api();
|
|
23
|
+
}
|
|
24
|
+
async catch(error) {
|
|
25
|
+
if (error instanceof GitUnknownError) {
|
|
26
|
+
this.error(style.error(`git: ${error.stderr || error.stdout || error.message}`));
|
|
27
|
+
}
|
|
28
|
+
else if (error instanceof FetchError) {
|
|
29
|
+
this.error(`Could not connect to Swell API. Please try again later: ${error.message}`);
|
|
30
|
+
}
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
debugJson(...args) {
|
|
34
|
+
const stringified = args.map((arg) => typeof arg === 'string' ? arg : JSON.stringify(arg, null, 4));
|
|
35
|
+
this.debug(...stringified);
|
|
36
|
+
}
|
|
37
|
+
logError(message) {
|
|
38
|
+
this.log(`${style.error(message)}`);
|
|
39
|
+
}
|
|
40
|
+
logSuccess(message) {
|
|
41
|
+
this.log(`${style.success(message)}`);
|
|
42
|
+
}
|
|
43
|
+
}
|