@superblocksteam/cli 1.5.2 → 1.7.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/README.md +3 -3
- package/assets/custom-components/setup/package.json +1 -1
- package/dist/commands/init.js +17 -6
- package/dist/commands/login.js +1 -1
- package/dist/commands/migrate.d.ts +2 -0
- package/dist/commands/migrate.js +41 -4
- package/dist/commands/pull.js +53 -29
- package/dist/commands/push.js +67 -15
- package/dist/commands/rm.js +10 -5
- package/dist/common/authenticated-command.js +1 -1
- package/dist/common/version-control.d.ts +15 -3
- package/dist/common/version-control.js +369 -4
- package/oclif.manifest.json +2 -2
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ $ npm install -g @superblocksteam/cli
|
|
|
12
12
|
$ superblocks COMMAND
|
|
13
13
|
running command...
|
|
14
14
|
$ superblocks (--version)
|
|
15
|
-
@superblocksteam/cli/1.
|
|
15
|
+
@superblocksteam/cli/1.7.0 linux-x64 node-v18.20.2
|
|
16
16
|
$ superblocks --help [COMMAND]
|
|
17
17
|
USAGE
|
|
18
18
|
$ superblocks COMMAND
|
|
@@ -147,8 +147,8 @@ USAGE
|
|
|
147
147
|
$ superblocks init [RESOURCE_URL] [-m latest-edits|most-recent-commit|deployed]
|
|
148
148
|
|
|
149
149
|
ARGUMENTS
|
|
150
|
-
RESOURCE_URL Superblocks resource URL (i.e.
|
|
151
|
-
https://app.superblocks.com/applications/<application_id
|
|
150
|
+
RESOURCE_URL Superblocks resource URL (i.e. https://app.superblocks.com/applications/<application_id> or
|
|
151
|
+
https://app.superblocks.com/applications/edit/<application_id>)
|
|
152
152
|
|
|
153
153
|
FLAGS
|
|
154
154
|
-m, --mode=<option> Pull mode
|
package/dist/commands/init.js
CHANGED
|
@@ -138,15 +138,17 @@ class Initialize extends authenticated_command_1.AuthenticatedCommand {
|
|
|
138
138
|
const headers = {
|
|
139
139
|
[util_1.COMPONENT_EVENT_HEADER]: util_1.ComponentEvent.INIT,
|
|
140
140
|
};
|
|
141
|
-
const application = await this.getSdk().fetchApplicationWithComponents({
|
|
141
|
+
const application = (await this.getSdk().fetchApplicationWithComponents({
|
|
142
142
|
applicationId: resourceId,
|
|
143
143
|
branch: version_control_1.DEFAULT_BRANCH,
|
|
144
144
|
viewMode: ctx.viewMode,
|
|
145
145
|
headers,
|
|
146
|
-
});
|
|
146
|
+
}));
|
|
147
147
|
task.title += `: fetched`;
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
if (application) {
|
|
149
|
+
ctx.writtenResources[resourceId] =
|
|
150
|
+
await (0, version_control_1.writeMultiPageApplicationToDisk)(application, superblocksRootPath !== null && superblocksRootPath !== void 0 ? superblocksRootPath : process.cwd());
|
|
151
|
+
}
|
|
150
152
|
task.title += `: done`;
|
|
151
153
|
},
|
|
152
154
|
});
|
|
@@ -293,7 +295,7 @@ Initialize.flags = {
|
|
|
293
295
|
};
|
|
294
296
|
Initialize.args = {
|
|
295
297
|
resource_url: core_1.Args.string({
|
|
296
|
-
description: "Superblocks resource URL (i.e. https://app.superblocks.com/applications/<application_id
|
|
298
|
+
description: "Superblocks resource URL (i.e. https://app.superblocks.com/applications/<application_id> or https://app.superblocks.com/applications/edit/<application_id>)",
|
|
297
299
|
required: false,
|
|
298
300
|
}),
|
|
299
301
|
};
|
|
@@ -301,7 +303,16 @@ exports.default = Initialize;
|
|
|
301
303
|
function getResourceIdFromUrl(resourceUrl) {
|
|
302
304
|
const url = new URL(resourceUrl);
|
|
303
305
|
if (url.pathname.startsWith("/applications")) {
|
|
304
|
-
|
|
306
|
+
const tokens = url.pathname.split("/");
|
|
307
|
+
if (tokens.length < 2) {
|
|
308
|
+
throw new Error(`Failed to parse resource URL: ${resourceUrl}`);
|
|
309
|
+
}
|
|
310
|
+
if (tokens[2] === "edit") {
|
|
311
|
+
return [tokens[3], "APPLICATION"];
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
return [tokens[2], "APPLICATION"];
|
|
315
|
+
}
|
|
305
316
|
}
|
|
306
317
|
else if (url.pathname.startsWith("/workflows") ||
|
|
307
318
|
url.pathname.startsWith("/scheduled_jobs")) {
|
package/dist/commands/login.js
CHANGED
|
@@ -39,7 +39,7 @@ class Login extends core_1.Command {
|
|
|
39
39
|
this.log((0, colorette_1.green)(`Welcome to the Superblocks 🐨 CLI ${user.user.name}!`));
|
|
40
40
|
}
|
|
41
41
|
catch (error) {
|
|
42
|
-
if (error
|
|
42
|
+
if (error instanceof util_1.FileAccessError) {
|
|
43
43
|
this.log((0, colorette_1.red)("Could not save token, ensure the Superblocks CLI has access to create folders in your home directory."));
|
|
44
44
|
return;
|
|
45
45
|
}
|
package/dist/commands/migrate.js
CHANGED
|
@@ -119,11 +119,39 @@ class Migrate extends authenticated_command_1.AuthenticatedCommand {
|
|
|
119
119
|
}
|
|
120
120
|
return resourceIdsToMigrate;
|
|
121
121
|
}
|
|
122
|
-
async
|
|
122
|
+
async migrateSinglePageApplicationToMultiPage(applicationResource, superblocksRootPath) {
|
|
123
|
+
const fileStructure = await (0, version_control_1.getFileStructureType)(superblocksRootPath, applicationResource.location);
|
|
124
|
+
if (fileStructure == version_control_1.FileStructureType.SINGLE_PAGE &&
|
|
125
|
+
(await this.getSdk().fetchCurrentUser()).flagBootstrap["server.multipage.enabled"]) {
|
|
126
|
+
this.log(`Migrating single page application at ${applicationResource.location} to multi-page application...`);
|
|
127
|
+
const singlePageApplication = await (0, version_control_1.readApplicationFromDisk)(superblocksRootPath, applicationResource.location);
|
|
128
|
+
const multiPageApplication = {
|
|
129
|
+
application: {
|
|
130
|
+
id: singlePageApplication.application.id,
|
|
131
|
+
name: singlePageApplication.application.name,
|
|
132
|
+
organizationId: singlePageApplication.application.organizationId,
|
|
133
|
+
settings: singlePageApplication.application.settings,
|
|
134
|
+
configuration: {},
|
|
135
|
+
},
|
|
136
|
+
apis: [],
|
|
137
|
+
pages: [
|
|
138
|
+
{
|
|
139
|
+
...singlePageApplication.page,
|
|
140
|
+
apis: singlePageApplication.apis,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
componentFiles: [],
|
|
144
|
+
};
|
|
145
|
+
await (0, version_control_1.writeMultiPageApplicationToDisk)(multiPageApplication, superblocksRootPath, applicationResource.location, true);
|
|
146
|
+
this.log(`Successfully migrated single page application at ${applicationResource.location} to multi-page application.`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async migrateCustomComponentVersion(applicationResource, superblocksRootPath) {
|
|
123
150
|
var _a;
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
151
|
+
const packageJsonPath = node_path_1.default.join(superblocksRootPath, applicationResource.location, "package.json");
|
|
152
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
153
|
+
this.log("Checking CLI version compatibility...");
|
|
154
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
127
155
|
const versionStr = (_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a["@superblocksteam/custom-components"];
|
|
128
156
|
if (!semver_1.default.satisfies(this.config.version, versionStr)) {
|
|
129
157
|
this.log("Migrating application dependencies...");
|
|
@@ -135,6 +163,15 @@ class Migrate extends authenticated_command_1.AuthenticatedCommand {
|
|
|
135
163
|
this.log("CLI version matches package.json version");
|
|
136
164
|
}
|
|
137
165
|
}
|
|
166
|
+
else {
|
|
167
|
+
this.log(`No package.json found in ${applicationResource.location}. Skipping migration of dependencies.`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async migrateApplication(applicationResource, superblocksRootPath) {
|
|
171
|
+
try {
|
|
172
|
+
await this.migrateCustomComponentVersion(applicationResource, superblocksRootPath);
|
|
173
|
+
await this.migrateSinglePageApplicationToMultiPage(applicationResource, superblocksRootPath);
|
|
174
|
+
}
|
|
138
175
|
catch (e) {
|
|
139
176
|
this.error(e.message, {
|
|
140
177
|
exit: 1,
|
package/dist/commands/pull.js
CHANGED
|
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
5
5
|
const core_1 = require("@oclif/core");
|
|
6
|
+
const sdk_1 = require("@superblocksteam/sdk");
|
|
6
7
|
const util_1 = require("@superblocksteam/util");
|
|
7
|
-
const fs = tslib_1.__importStar(require("fs-extra"));
|
|
8
8
|
const listr2_1 = require("listr2");
|
|
9
9
|
const authenticated_command_1 = require("../common/authenticated-command");
|
|
10
10
|
const version_control_1 = require("../common/version-control");
|
|
@@ -28,6 +28,7 @@ class Pull extends authenticated_command_1.AuthenticatedCommand {
|
|
|
28
28
|
] = await (0, util_1.getSuperblocksMonorepoConfigJson)(true);
|
|
29
29
|
ctx.existingSuperblocksResourceConfig =
|
|
30
30
|
await (0, util_1.getSuperblocksResourceConfigIfExists)();
|
|
31
|
+
ctx.superblocksRootPath = node_path_1.default.resolve(node_path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
|
|
31
32
|
}
|
|
32
33
|
catch {
|
|
33
34
|
// no existing superblocks config
|
|
@@ -51,7 +52,7 @@ class Pull extends authenticated_command_1.AuthenticatedCommand {
|
|
|
51
52
|
{
|
|
52
53
|
title: "Checking for deleted Superblocks resources...",
|
|
53
54
|
task: async (ctx, task) => {
|
|
54
|
-
var _a, _b
|
|
55
|
+
var _a, _b;
|
|
55
56
|
try {
|
|
56
57
|
for (const [resourceId, resource] of Object.entries((_b = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources) !== null && _b !== void 0 ? _b : {})) {
|
|
57
58
|
switch (resource === null || resource === void 0 ? void 0 : resource.resourceType) {
|
|
@@ -109,10 +110,7 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
109
110
|
},
|
|
110
111
|
]);
|
|
111
112
|
if (removeResourcesFromDisk) {
|
|
112
|
-
|
|
113
|
-
const resource = (_c = ctx.existingSuperblocksRootConfig) === null || _c === void 0 ? void 0 : _c.resources[resourceId];
|
|
114
|
-
await (0, version_control_1.removeResourceFromDisk)(resource.location);
|
|
115
|
-
}
|
|
113
|
+
await (0, version_control_1.deleteResourcesAndUpdateRootConfig)(ctx.removedResourceIds, ctx.existingSuperblocksRootConfig, ctx.superblocksRootPath, ctx.superblocksRootConfigPath);
|
|
116
114
|
}
|
|
117
115
|
}
|
|
118
116
|
catch (e) {
|
|
@@ -131,14 +129,26 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
131
129
|
var _a, _b;
|
|
132
130
|
task.title = `Validating git configuration...`;
|
|
133
131
|
const subtasks = [];
|
|
132
|
+
ctx.resourceIdsToSkip = new Set();
|
|
134
133
|
for (const resourceId of ctx.resourceIdsToPull) {
|
|
135
134
|
const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
|
|
136
135
|
const resourceTitle = `${((_b = resource.resourceType) !== null && _b !== void 0 ? _b : "").toLowerCase()} ${resourceId}`;
|
|
137
136
|
subtasks.push({
|
|
138
137
|
title: `Checking ${resourceTitle}...`,
|
|
139
138
|
task: async () => {
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
try {
|
|
140
|
+
const { branchName } = await this.validateGitSetup(resource === null || resource === void 0 ? void 0 : resource.resourceType, resourceId, util_1.ComponentEvent.PULL, ctx.localBranchName);
|
|
141
|
+
ctx.branchToPullFrom.set(resourceId, branchName);
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
if ((0, version_control_1.isCI)() && error instanceof sdk_1.ValidateGitSetupError) {
|
|
145
|
+
this.log(`WARN: Failed to validate git setup for ${resourceTitle}. Skipping pull.\n\n${error.message}.`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
ctx.resourceIdsToSkip.add(resourceId);
|
|
151
|
+
}
|
|
142
152
|
},
|
|
143
153
|
});
|
|
144
154
|
}
|
|
@@ -152,8 +162,9 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
152
162
|
var _a, _b;
|
|
153
163
|
task.title = `Pulling resources from branch ${ctx.localBranchName}...`;
|
|
154
164
|
const viewMode = await (0, version_control_1.getMode)(task, mode);
|
|
165
|
+
// Remove resources to skip from list of resources to push
|
|
166
|
+
ctx.resourceIdsToPull = ctx.resourceIdsToPull.filter((id) => !ctx.resourceIdsToSkip.has(id));
|
|
155
167
|
const subtasks = [];
|
|
156
|
-
const superblocksRootPath = node_path_1.default.resolve(node_path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
|
|
157
168
|
for (const resourceId of ctx.resourceIdsToPull) {
|
|
158
169
|
const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
|
|
159
170
|
const branchName = (_b = ctx.branchToPullFrom.get(resourceId)) !== null && _b !== void 0 ? _b : ctx.localBranchName;
|
|
@@ -166,15 +177,39 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
166
177
|
[util_1.COMPONENT_EVENT_HEADER]: util_1.ComponentEvent.PULL,
|
|
167
178
|
};
|
|
168
179
|
try {
|
|
169
|
-
const application = await this.getSdk().fetchApplicationWithComponents({
|
|
170
|
-
applicationId: resourceId,
|
|
171
|
-
branch: branchName,
|
|
172
|
-
viewMode,
|
|
173
|
-
headers,
|
|
174
|
-
});
|
|
175
180
|
task.title += `: fetched`;
|
|
176
|
-
ctx.
|
|
177
|
-
|
|
181
|
+
const fileStructureType = await (0, version_control_1.getFileStructureType)(ctx.superblocksRootPath, resource.location);
|
|
182
|
+
if (fileStructureType === version_control_1.FileStructureType.SINGLE_PAGE) {
|
|
183
|
+
const multiPageEnabled = (await this.getSdk().fetchCurrentUser()).flagBootstrap["server.multipage.enabled"];
|
|
184
|
+
if (multiPageEnabled) {
|
|
185
|
+
this.error(`Application files at ${resource.location} are in single page format, but the multi-page feature is enabled for your account. Please run superblocks migrate to convert your application to multi-page format and commit the changes before pulling.`);
|
|
186
|
+
}
|
|
187
|
+
const application = await this.getSdk().fetchApplicationWithComponents({
|
|
188
|
+
applicationId: resourceId,
|
|
189
|
+
branch: branchName,
|
|
190
|
+
viewMode,
|
|
191
|
+
headers,
|
|
192
|
+
fetchSinglePageApplication: true,
|
|
193
|
+
});
|
|
194
|
+
if (!application) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
ctx.writtenResources[resourceId] =
|
|
198
|
+
await (0, version_control_1.writeResourceToDisk)("APPLICATION", resourceId, application, ctx.superblocksRootPath, resource.location);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const application = (await this.getSdk().fetchApplicationWithComponents({
|
|
202
|
+
applicationId: resourceId,
|
|
203
|
+
branch: branchName,
|
|
204
|
+
viewMode,
|
|
205
|
+
headers,
|
|
206
|
+
}));
|
|
207
|
+
if (!application) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
ctx.writtenResources[resourceId] =
|
|
211
|
+
await (0, version_control_1.writeMultiPageApplicationToDisk)(application, ctx.superblocksRootPath, resource.location, false);
|
|
212
|
+
}
|
|
178
213
|
task.title += `: done`;
|
|
179
214
|
}
|
|
180
215
|
catch (e) {
|
|
@@ -198,7 +233,7 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
198
233
|
const backend = await this.getSdk().fetchApi(resourceId, viewMode, branchName);
|
|
199
234
|
task.title += `: fetched`;
|
|
200
235
|
ctx.writtenResources[resourceId] =
|
|
201
|
-
await (0, version_control_1.writeResourceToDisk)("BACKEND", resourceId, backend, superblocksRootPath, resource.location);
|
|
236
|
+
await (0, version_control_1.writeResourceToDisk)("BACKEND", resourceId, backend, ctx.superblocksRootPath, resource.location);
|
|
202
237
|
task.title += `: done`;
|
|
203
238
|
},
|
|
204
239
|
});
|
|
@@ -215,17 +250,6 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
215
250
|
});
|
|
216
251
|
},
|
|
217
252
|
},
|
|
218
|
-
{
|
|
219
|
-
title: "Updating Superblocks project file...",
|
|
220
|
-
task: async (ctx) => {
|
|
221
|
-
const [superblocksRootConfig, rootConfigPath] = await (0, util_1.getSuperblocksMonorepoConfigJson)(true);
|
|
222
|
-
for (const removedResourceId of ctx.removedResourceIds) {
|
|
223
|
-
delete superblocksRootConfig.resources[removedResourceId];
|
|
224
|
-
}
|
|
225
|
-
// update superblocks.json file
|
|
226
|
-
await fs.writeFile(rootConfigPath, JSON.stringify((0, version_control_1.sortByKey)(superblocksRootConfig), null, 2));
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
253
|
], {
|
|
230
254
|
concurrent: false,
|
|
231
255
|
});
|
package/dist/commands/push.js
CHANGED
|
@@ -29,6 +29,7 @@ class Push extends authenticated_command_1.AuthenticatedCommand {
|
|
|
29
29
|
] = await (0, util_1.getSuperblocksMonorepoConfigJson)(true);
|
|
30
30
|
ctx.existingSuperblocksResourceConfig =
|
|
31
31
|
await (0, util_1.getSuperblocksResourceConfigIfExists)();
|
|
32
|
+
ctx.superblocksRootPath = node_path_1.default.resolve(node_path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
|
|
32
33
|
}
|
|
33
34
|
catch {
|
|
34
35
|
// no existing superblocks config
|
|
@@ -57,7 +58,7 @@ class Push extends authenticated_command_1.AuthenticatedCommand {
|
|
|
57
58
|
{
|
|
58
59
|
title: "Checking for deleted Superblocks resources...",
|
|
59
60
|
task: async (ctx, task) => {
|
|
60
|
-
var _a, _b
|
|
61
|
+
var _a, _b;
|
|
61
62
|
try {
|
|
62
63
|
for (const [resourceId, resource] of Object.entries((_b = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources) !== null && _b !== void 0 ? _b : {})) {
|
|
63
64
|
switch (resource === null || resource === void 0 ? void 0 : resource.resourceType) {
|
|
@@ -115,10 +116,7 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
115
116
|
},
|
|
116
117
|
]);
|
|
117
118
|
if (removeResourcesFromDisk) {
|
|
118
|
-
|
|
119
|
-
const resource = (_c = ctx.existingSuperblocksRootConfig) === null || _c === void 0 ? void 0 : _c.resources[resourceId];
|
|
120
|
-
await (0, version_control_1.removeResourceFromDisk)(resource.location);
|
|
121
|
-
}
|
|
119
|
+
await (0, version_control_1.deleteResourcesAndUpdateRootConfig)(ctx.removedResourceIds, ctx.existingSuperblocksRootConfig, ctx.superblocksRootPath, ctx.superblocksRootConfigPath);
|
|
122
120
|
}
|
|
123
121
|
}
|
|
124
122
|
catch (e) {
|
|
@@ -137,6 +135,7 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
137
135
|
var _a, _b;
|
|
138
136
|
task.title = `Validating git configuration...`;
|
|
139
137
|
const subtasks = [];
|
|
138
|
+
ctx.resourceIdsToSkip = new Set();
|
|
140
139
|
for (const resourceId of ctx.resourceIdsToPush) {
|
|
141
140
|
const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
|
|
142
141
|
// for user messages:
|
|
@@ -144,8 +143,43 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
144
143
|
subtasks.push({
|
|
145
144
|
title: `Checking ${resourceTitle}...`,
|
|
146
145
|
task: async () => {
|
|
147
|
-
|
|
148
|
-
|
|
146
|
+
try {
|
|
147
|
+
const { branchName } = await this.validateGitSetup(resource === null || resource === void 0 ? void 0 : resource.resourceType, resourceId, util_1.ComponentEvent.PUSH, ctx.localBranchName);
|
|
148
|
+
ctx.branchToPushTo.set(resourceId, branchName);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
if ((0, version_control_1.isCI)() && error instanceof sdk_1.ValidateGitSetupError) {
|
|
152
|
+
this.log(`WARN: Failed to validate git setup for ${resourceTitle}. Skipping push.\n\n${error.message}.`);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
ctx.resourceIdsToSkip.add(resourceId);
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return task.newListr(subtasks, {
|
|
163
|
+
concurrent: true,
|
|
164
|
+
});
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
task: async (ctx, task) => {
|
|
169
|
+
var _a, _b;
|
|
170
|
+
task.title = `Validating project structure...`;
|
|
171
|
+
const subtasks = [];
|
|
172
|
+
const superblocksRootPath = node_path_1.default.resolve(node_path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
|
|
173
|
+
for (const resourceId of ctx.resourceIdsToPush) {
|
|
174
|
+
const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
|
|
175
|
+
const resourceTitle = `${((_b = resource.resourceType) !== null && _b !== void 0 ? _b : "").toLowerCase()} at ${resource.location}`;
|
|
176
|
+
subtasks.push({
|
|
177
|
+
title: `Validating ${resourceTitle}...`,
|
|
178
|
+
task: async () => {
|
|
179
|
+
const validationError = await (0, version_control_1.validateLocalResource)(superblocksRootPath, resource);
|
|
180
|
+
if (validationError) {
|
|
181
|
+
this.error(`Push failed for resource '${resource.location}' (id: ${resourceId}). ${validationError}`);
|
|
182
|
+
}
|
|
149
183
|
},
|
|
150
184
|
});
|
|
151
185
|
}
|
|
@@ -158,6 +192,8 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
158
192
|
task: async (ctx, task) => {
|
|
159
193
|
var _a;
|
|
160
194
|
task.title = `Pushing resources to branch ${ctx.localBranchName}...`;
|
|
195
|
+
// Remove resources to skip from list of resources to push
|
|
196
|
+
ctx.resourceIdsToPush = ctx.resourceIdsToPush.filter((id) => !ctx.resourceIdsToSkip.has(id));
|
|
161
197
|
const subtasks = [];
|
|
162
198
|
const superblocksRootPath = node_path_1.default.resolve(node_path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
|
|
163
199
|
for (const resourceId of ctx.resourceIdsToPush) {
|
|
@@ -168,17 +204,31 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
168
204
|
title: `Pushing application ${resource.location}...`,
|
|
169
205
|
task: async (_ctx, task) => {
|
|
170
206
|
var _a;
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
207
|
+
const fileStructureType = await (0, version_control_1.getFileStructureType)(superblocksRootPath, resource.location);
|
|
208
|
+
if (fileStructureType === version_control_1.FileStructureType.SINGLE_PAGE &&
|
|
209
|
+
(await this.getSdk().fetchCurrentUser()).flagBootstrap["server.multipage.enabled"]) {
|
|
210
|
+
this.error(`Application files at ${resource.location} are in single page format, but the multi-page feature is enabled for your account. Please run \`superblocks migrate\` to convert your application to multi-page format and commit the changes before pushing.`);
|
|
211
|
+
}
|
|
212
|
+
const applicationConfig = fileStructureType === version_control_1.FileStructureType.SINGLE_PAGE
|
|
213
|
+
? {
|
|
214
|
+
...(await (0, version_control_1.readApplicationFromDisk)(superblocksRootPath, resource.location)),
|
|
215
|
+
commitId: ctx.headCommitId,
|
|
216
|
+
commitMessage: ctx.headCommitMessage,
|
|
217
|
+
}
|
|
218
|
+
: {
|
|
219
|
+
...(await (0, version_control_1.readMultiPageApplicationFromDisk)(superblocksRootPath, resource.location)),
|
|
220
|
+
commitId: ctx.headCommitId,
|
|
221
|
+
commitMessage: ctx.headCommitMessage,
|
|
222
|
+
};
|
|
176
223
|
task.title += `: read from disk`;
|
|
177
224
|
try {
|
|
225
|
+
const branch = (_a = ctx.branchToPushTo.get(resourceId)) !== null && _a !== void 0 ? _a : ctx.localBranchName;
|
|
226
|
+
task.title += `: going to push commit ${applicationConfig.commitId} to branch ${branch}`;
|
|
178
227
|
await this.getSdk().pushApplication({
|
|
179
228
|
applicationId: resourceId,
|
|
180
229
|
applicationConfig,
|
|
181
|
-
branch
|
|
230
|
+
branch,
|
|
231
|
+
multiPage: fileStructureType === version_control_1.FileStructureType.MULTI_PAGE,
|
|
182
232
|
});
|
|
183
233
|
}
|
|
184
234
|
catch (error) {
|
|
@@ -210,14 +260,16 @@ Would you like to also delete these resources from your filesystem?`,
|
|
|
210
260
|
};
|
|
211
261
|
task.title += `: read from disk`;
|
|
212
262
|
try {
|
|
263
|
+
const branch = (_a = ctx.branchToPushTo.get(resourceId)) !== null && _a !== void 0 ? _a : ctx.localBranchName;
|
|
264
|
+
task.title += `: going to push commit ${apiConfig.commitId} to branch ${branch}`;
|
|
213
265
|
await this.getSdk().pushApi({
|
|
214
266
|
apiId: resourceId,
|
|
215
267
|
apiConfig,
|
|
216
|
-
branch
|
|
268
|
+
branch,
|
|
217
269
|
});
|
|
218
270
|
}
|
|
219
271
|
catch (error) {
|
|
220
|
-
if (
|
|
272
|
+
if (error instanceof sdk_1.BranchNotCheckedOutError) {
|
|
221
273
|
this.log(`WARN: Workflow/Scheduled Job ${(0, version_control_1.extractApiName)(apiConfig)} failed to push, please check branch out in the Superblocks UI first in order to check this branch out for this resource.`);
|
|
222
274
|
}
|
|
223
275
|
else if (error instanceof sdk_1.CommitAlreadyExistsError) {
|
package/dist/commands/rm.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
4
5
|
const core_1 = require("@oclif/core");
|
|
5
6
|
const util_1 = require("@superblocksteam/util");
|
|
6
7
|
const fs = tslib_1.__importStar(require("fs-extra"));
|
|
@@ -27,7 +28,11 @@ class Remove extends authenticated_command_1.AuthenticatedCommand {
|
|
|
27
28
|
ctx.fetchedResources = {};
|
|
28
29
|
ctx.removedResourceIds = [];
|
|
29
30
|
try {
|
|
30
|
-
|
|
31
|
+
[
|
|
32
|
+
ctx.existingSuperblocksRootConfig,
|
|
33
|
+
ctx.superblocksRootConfigPath,
|
|
34
|
+
] = await (0, util_1.getSuperblocksMonorepoConfigJson)(true);
|
|
35
|
+
ctx.superblocksRootPath = path_1.default.resolve(path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
|
|
31
36
|
}
|
|
32
37
|
catch {
|
|
33
38
|
// no existing superblocks config
|
|
@@ -83,14 +88,14 @@ class Remove extends authenticated_command_1.AuthenticatedCommand {
|
|
|
83
88
|
var _a;
|
|
84
89
|
const subtasks = [];
|
|
85
90
|
for (const resourceId of ctx.resourceIdsToRemove) {
|
|
86
|
-
const resourceLocation = (_a = ctx.
|
|
91
|
+
const resourceLocation = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId].location;
|
|
87
92
|
if (!resourceLocation) {
|
|
88
93
|
this.error(`Resource location not found for resource with id ${resourceId}`);
|
|
89
94
|
}
|
|
90
95
|
subtasks.push({
|
|
91
96
|
title: `Removing ${resourceId} from ${resourceLocation}...`,
|
|
92
97
|
task: async (_ctx, task) => {
|
|
93
|
-
await (0, version_control_1.removeResourceFromDisk)(resourceLocation);
|
|
98
|
+
await (0, version_control_1.removeResourceFromDisk)(ctx.superblocksRootPath, resourceLocation);
|
|
94
99
|
ctx.removedResourceIds.push(resourceId);
|
|
95
100
|
task.title += `: done`;
|
|
96
101
|
},
|
|
@@ -126,7 +131,7 @@ class Remove extends authenticated_command_1.AuthenticatedCommand {
|
|
|
126
131
|
else {
|
|
127
132
|
const choices = [];
|
|
128
133
|
for (const [resourceId, resource] of Object.entries(ctx.fetchedResources)) {
|
|
129
|
-
if ((_a = ctx.
|
|
134
|
+
if ((_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId]) {
|
|
130
135
|
choices.push({
|
|
131
136
|
name: resourceId,
|
|
132
137
|
message: `${resource.name} (${resource.resourceType})`,
|
|
@@ -191,7 +196,7 @@ Remove.args = {
|
|
|
191
196
|
exports.default = Remove;
|
|
192
197
|
function getResourceIdFromLocation(ctx, resourceLocation) {
|
|
193
198
|
var _a, _b;
|
|
194
|
-
for (const [resourceId, resource] of Object.entries((_b = (_a = ctx.
|
|
199
|
+
for (const [resourceId, resource] of Object.entries((_b = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources) !== null && _b !== void 0 ? _b : {})) {
|
|
195
200
|
if (resource.location === resourceLocation) {
|
|
196
201
|
return [resourceId, resource.resourceType];
|
|
197
202
|
}
|
|
@@ -121,7 +121,7 @@ class AuthenticatedApplicationCommand extends AuthenticatedCommand {
|
|
|
121
121
|
async getEditModeUrl() {
|
|
122
122
|
const result = await (0, util_1.getLocalTokenWithUrlIfExists)();
|
|
123
123
|
const baseUrl = typeof result === "string" ? result : result === null || result === void 0 ? void 0 : result.superblocksBaseUrl;
|
|
124
|
-
return new URL(`/applications/${this.applicationConfig.id}
|
|
124
|
+
return new URL(`/applications/edit/${this.applicationConfig.id}`, baseUrl).toString();
|
|
125
125
|
}
|
|
126
126
|
async init() {
|
|
127
127
|
var _a, _b;
|
|
@@ -1,20 +1,29 @@
|
|
|
1
|
-
import { ApiWrapper, ApplicationWrapper, ViewMode } from "@superblocksteam/sdk";
|
|
2
|
-
import { LocalGitRepoState } from "@superblocksteam/util";
|
|
1
|
+
import { ApiWrapper, ApplicationWrapper, MultiPageApplicationWrapper, ViewMode } from "@superblocksteam/sdk";
|
|
2
|
+
import { LocalGitRepoState, SuperblocksMonorepoConfig } from "@superblocksteam/util";
|
|
3
3
|
import { VersionedResourceConfig } from "@superblocksteam/util";
|
|
4
4
|
export declare const LATEST_EDITS_MODE = "latest-edits";
|
|
5
5
|
export declare const MOST_RECENT_COMMIT_MODE = "most-recent-commit";
|
|
6
6
|
export declare const DEPLOYED_MODE = "deployed";
|
|
7
7
|
export declare const DEFAULT_BRANCH = "main";
|
|
8
8
|
export declare const modeFlagValuesMap: Record<string, string>;
|
|
9
|
+
export declare enum FileStructureType {
|
|
10
|
+
SINGLE_PAGE = "single-page",
|
|
11
|
+
MULTI_PAGE = "multi-page"
|
|
12
|
+
}
|
|
9
13
|
export declare function modeFlagToViewMode(modeFlag: ModeFlag): ViewMode;
|
|
10
14
|
export type ModeFlag = (keyof typeof modeFlagValuesMap)[number];
|
|
11
15
|
export declare const SELECT_PROMPT_HELP = "Use \u2191/\u2193 arrow keys, Enter to confirm";
|
|
12
16
|
export declare const MULTI_SELECT_PROMPT_HELP = "Type to filter, Use \u2191/\u2193 arrow keys, Space to select, Enter to confirm";
|
|
13
17
|
export declare const atLeastOneSelection: (value: string[]) => string | true;
|
|
18
|
+
export declare function getFileStructureType(rootPath: string, existingRelativeLocation: string): Promise<FileStructureType>;
|
|
14
19
|
export declare function readApplicationFromDisk(rootPath: string, existingRelativeLocation: string): Promise<ApplicationWrapper>;
|
|
20
|
+
export declare function readMultiPageApplicationFromDisk(rootPath: string, existingRelativeLocation: string): Promise<MultiPageApplicationWrapper>;
|
|
15
21
|
export declare function readApiFromDisk(rootPath: string, existingRelativeLocation: string): Promise<ApiWrapper>;
|
|
16
22
|
export declare function writeResourceToDisk(resourceType: string, resourceId: string, resource: any, rootPath: string, existingRelativeLocation?: string): Promise<VersionedResourceConfig>;
|
|
17
|
-
export declare function
|
|
23
|
+
export declare function writeMultiPageApplicationToDisk(resource: MultiPageApplicationWrapper & {
|
|
24
|
+
componentFiles: any;
|
|
25
|
+
}, rootPath: string, existingRelativeLocation?: string, migrateFromSinglePage?: boolean): Promise<VersionedResourceConfig>;
|
|
26
|
+
export declare function removeResourceFromDisk(rootPath: string, resourceRelativeLocation: string): Promise<void>;
|
|
18
27
|
export declare function getMode(task: any, mode: ModeFlag): Promise<ViewMode>;
|
|
19
28
|
export declare function sortByKey(obj: unknown): unknown;
|
|
20
29
|
export declare function getLocalGitRepoState(overrideLocalBranch?: string): Promise<LocalGitRepoState>;
|
|
@@ -27,5 +36,8 @@ export declare function getCurrentGitBranchIfGit(): Promise<string | null>;
|
|
|
27
36
|
*/
|
|
28
37
|
export declare function getCurrentGitBranch(): Promise<string>;
|
|
29
38
|
export declare function getHeadCommit(branch: string): Promise<[string, string]>;
|
|
39
|
+
export declare function isCI(): boolean;
|
|
30
40
|
export declare function isGitRepoDirty(): Promise<boolean>;
|
|
31
41
|
export declare function extractApiName(api: ApiWrapper): string;
|
|
42
|
+
export declare function validateLocalResource(superblocksRootPath: string, resource: VersionedResourceConfig): Promise<string | undefined>;
|
|
43
|
+
export declare function deleteResourcesAndUpdateRootConfig(removedResourceIds: string[], existingSuperblocksRootConfig: SuperblocksMonorepoConfig, superblocksRootPath: string, superblocksRootConfigPath: string): Promise<void>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.extractApiName = exports.isGitRepoDirty = exports.getHeadCommit = exports.getCurrentGitBranch = exports.getCurrentGitBranchIfGit = exports.getLocalGitRepoState = exports.sortByKey = exports.getMode = exports.removeResourceFromDisk = exports.writeResourceToDisk = exports.readApiFromDisk = exports.readApplicationFromDisk = exports.atLeastOneSelection = exports.MULTI_SELECT_PROMPT_HELP = exports.SELECT_PROMPT_HELP = exports.modeFlagToViewMode = exports.modeFlagValuesMap = exports.DEFAULT_BRANCH = exports.DEPLOYED_MODE = exports.MOST_RECENT_COMMIT_MODE = exports.LATEST_EDITS_MODE = void 0;
|
|
3
|
+
exports.deleteResourcesAndUpdateRootConfig = exports.validateLocalResource = exports.extractApiName = exports.isGitRepoDirty = exports.isCI = exports.getHeadCommit = exports.getCurrentGitBranch = exports.getCurrentGitBranchIfGit = exports.getLocalGitRepoState = exports.sortByKey = exports.getMode = exports.removeResourceFromDisk = exports.writeMultiPageApplicationToDisk = exports.writeResourceToDisk = exports.readApiFromDisk = exports.readMultiPageApplicationFromDisk = exports.readApplicationFromDisk = exports.getFileStructureType = exports.atLeastOneSelection = exports.MULTI_SELECT_PROMPT_HELP = exports.SELECT_PROMPT_HELP = exports.modeFlagToViewMode = exports.FileStructureType = exports.modeFlagValuesMap = exports.DEFAULT_BRANCH = exports.DEPLOYED_MODE = exports.MOST_RECENT_COMMIT_MODE = exports.LATEST_EDITS_MODE = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const https = tslib_1.__importStar(require("https"));
|
|
6
6
|
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
@@ -23,6 +23,11 @@ exports.modeFlagValuesMap = {
|
|
|
23
23
|
[exports.MOST_RECENT_COMMIT_MODE]: "Most recent commit",
|
|
24
24
|
[exports.DEPLOYED_MODE]: "Deployed",
|
|
25
25
|
};
|
|
26
|
+
var FileStructureType;
|
|
27
|
+
(function (FileStructureType) {
|
|
28
|
+
FileStructureType["SINGLE_PAGE"] = "single-page";
|
|
29
|
+
FileStructureType["MULTI_PAGE"] = "multi-page";
|
|
30
|
+
})(FileStructureType || (exports.FileStructureType = FileStructureType = {}));
|
|
26
31
|
function modeFlagToViewMode(modeFlag) {
|
|
27
32
|
switch (modeFlag) {
|
|
28
33
|
case exports.LATEST_EDITS_MODE:
|
|
@@ -93,8 +98,20 @@ async function downloadFile(rootDirectory, filepath, url) {
|
|
|
93
98
|
async function readYamlFile(path) {
|
|
94
99
|
return (0, yaml_1.parse)(await fs.readFile(path, "utf8"));
|
|
95
100
|
}
|
|
101
|
+
function getFileStructureTypeFromResourceConfig(superblocksConfig) {
|
|
102
|
+
if (superblocksConfig.pages) {
|
|
103
|
+
return FileStructureType.MULTI_PAGE;
|
|
104
|
+
}
|
|
105
|
+
return FileStructureType.SINGLE_PAGE;
|
|
106
|
+
}
|
|
107
|
+
async function getFileStructureType(rootPath, existingRelativeLocation) {
|
|
108
|
+
const superblocksConfig = await (0, util_1.getSuperblocksApplicationConfigJson)(`${rootPath}/${existingRelativeLocation}`);
|
|
109
|
+
return getFileStructureTypeFromResourceConfig(superblocksConfig);
|
|
110
|
+
}
|
|
111
|
+
exports.getFileStructureType = getFileStructureType;
|
|
96
112
|
// NOTE: If a change is made to how applications are read from disk, please update
|
|
97
113
|
// logic to write applications to disk in the "writeResourceToDisk" function accordingly.
|
|
114
|
+
// @deprecated this can be removed once all customers move to multi page applications
|
|
98
115
|
async function readApplicationFromDisk(rootPath, existingRelativeLocation) {
|
|
99
116
|
const application = await readYamlFile(`${rootPath}/${existingRelativeLocation}/application.yaml`);
|
|
100
117
|
const page = await readYamlFile(`${rootPath}/${existingRelativeLocation}/page.yaml`);
|
|
@@ -118,6 +135,59 @@ async function readApplicationFromDisk(rootPath, existingRelativeLocation) {
|
|
|
118
135
|
};
|
|
119
136
|
}
|
|
120
137
|
exports.readApplicationFromDisk = readApplicationFromDisk;
|
|
138
|
+
// NOTE: If a change is made to how applications are read from disk, please update
|
|
139
|
+
// logic to write applications to disk in the "writeResourceToDisk" function accordingly.
|
|
140
|
+
async function readMultiPageApplicationFromDisk(rootPath, existingRelativeLocation) {
|
|
141
|
+
var _a;
|
|
142
|
+
const superblocksApplicationConfig = await (0, util_1.getSuperblocksApplicationConfigJson)(`${rootPath}/${existingRelativeLocation}`);
|
|
143
|
+
const application = await readYamlFile(`${rootPath}/${existingRelativeLocation}/application.yaml`);
|
|
144
|
+
const pagesDirName = `${rootPath}/${existingRelativeLocation}/pages`;
|
|
145
|
+
const pages = [];
|
|
146
|
+
if (await fs.pathExists(pagesDirName)) {
|
|
147
|
+
for (const page of Object.values((_a = superblocksApplicationConfig.pages) !== null && _a !== void 0 ? _a : {})) {
|
|
148
|
+
const pageContent = await readYamlFile(`${pagesDirName}/${slugifyName(page.name)}/page.yaml`);
|
|
149
|
+
const pageApisDirName = `${pagesDirName}/${slugifyName(page.name)}/apis`;
|
|
150
|
+
const apis = [];
|
|
151
|
+
if (await fs.pathExists(pageApisDirName)) {
|
|
152
|
+
for (const apiName of Object.values(page.apis)) {
|
|
153
|
+
const apiContent = await readYamlFile(`${pageApisDirName}/${slugifyName(apiName)}.yaml`);
|
|
154
|
+
// This mimics the shape of the ApiV3Dto object
|
|
155
|
+
apis.push({
|
|
156
|
+
id: apiContent.metadata.id,
|
|
157
|
+
pageId: page.id,
|
|
158
|
+
apiPb: apiContent,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
pages.push({
|
|
163
|
+
id: pageContent.id,
|
|
164
|
+
name: pageContent.name,
|
|
165
|
+
applicationId: pageContent.applicationId,
|
|
166
|
+
isHidden: pageContent.isHidden,
|
|
167
|
+
layouts: pageContent.layouts,
|
|
168
|
+
apis,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const appApisDirName = `${rootPath}/${existingRelativeLocation}/apis`;
|
|
173
|
+
const apis = [];
|
|
174
|
+
if (await fs.pathExists(appApisDirName)) {
|
|
175
|
+
for (const apiName of Object.values(superblocksApplicationConfig.apis)) {
|
|
176
|
+
const apiContent = await readYamlFile(`${appApisDirName}/${slugifyName(apiName)}.yaml`);
|
|
177
|
+
// This mimics the shape of the ApiV3Dto object
|
|
178
|
+
apis.push({
|
|
179
|
+
id: apiContent.metadata.id,
|
|
180
|
+
apiPb: apiContent,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
application,
|
|
186
|
+
apis,
|
|
187
|
+
pages,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
exports.readMultiPageApplicationFromDisk = readMultiPageApplicationFromDisk;
|
|
121
191
|
async function readApiFromDisk(rootPath, existingRelativeLocation) {
|
|
122
192
|
return {
|
|
123
193
|
apiPb: await readYamlFile(`${rootPath}/${existingRelativeLocation}/api.yaml`),
|
|
@@ -179,7 +249,9 @@ async function writeResourceToDisk(resourceType, resourceId, resource, rootPath,
|
|
|
179
249
|
for (const existingApiId of Object.keys(existingApplicationConfig.apis)) {
|
|
180
250
|
const existingApiName = existingApplicationConfig.apis[existingApiId];
|
|
181
251
|
const existingApiPath = `${apisDirName}/${slugifyName(existingApiName)}.yaml`;
|
|
182
|
-
|
|
252
|
+
const newApiName = newApplicationConfig.apis[existingApiId];
|
|
253
|
+
if (!(existingApiId in newApplicationConfig.apis) ||
|
|
254
|
+
slugifyName(newApiName) !== slugifyName(existingApiName)) {
|
|
183
255
|
await fs.remove(existingApiPath);
|
|
184
256
|
}
|
|
185
257
|
}
|
|
@@ -231,7 +303,144 @@ async function writeResourceToDisk(resourceType, resourceId, resource, rootPath,
|
|
|
231
303
|
}
|
|
232
304
|
}
|
|
233
305
|
exports.writeResourceToDisk = writeResourceToDisk;
|
|
234
|
-
|
|
306
|
+
// NOTE: If a change is made to how applications are written to disk, please update
|
|
307
|
+
// logic to read applications from disk in the "readMultiPageApplicationFromDisk" function accordingly.
|
|
308
|
+
async function writeMultiPageApplicationToDisk(resource, rootPath, existingRelativeLocation, migrateFromSinglePage) {
|
|
309
|
+
var _a, _b, _c, _d;
|
|
310
|
+
const parentDirName = "apps";
|
|
311
|
+
const newRelativeLocation = `${parentDirName}/${slugifyName(resource.application.name)}`;
|
|
312
|
+
const relativeLocation = existingRelativeLocation !== null && existingRelativeLocation !== void 0 ? existingRelativeLocation : newRelativeLocation;
|
|
313
|
+
const appDirName = node_path_1.default.resolve(rootPath, relativeLocation);
|
|
314
|
+
if (!(await fs.pathExists(appDirName))) {
|
|
315
|
+
await fs.mkdir(appDirName, { recursive: true });
|
|
316
|
+
}
|
|
317
|
+
const applicationContent = (0, yaml_1.stringify)(resource.application, {
|
|
318
|
+
sortMapEntries: true,
|
|
319
|
+
});
|
|
320
|
+
await fs.outputFile(`${appDirName}/application.yaml`, applicationContent);
|
|
321
|
+
const newApplicationConfig = {
|
|
322
|
+
configType: "APPLICATION",
|
|
323
|
+
apis: {},
|
|
324
|
+
id: resource.application.id,
|
|
325
|
+
};
|
|
326
|
+
newApplicationConfig.pages = {};
|
|
327
|
+
const apiPromises = [];
|
|
328
|
+
for (const page of Object.values(resource.pages)) {
|
|
329
|
+
const pageId = page.id;
|
|
330
|
+
const pageDirName = `${appDirName}/pages/${slugifyName(page.name)}`;
|
|
331
|
+
if (!(await fs.pathExists(pageDirName))) {
|
|
332
|
+
await fs.mkdir(pageDirName, { recursive: true });
|
|
333
|
+
}
|
|
334
|
+
newApplicationConfig.pages[pageId] = {
|
|
335
|
+
id: page.id,
|
|
336
|
+
name: page.name,
|
|
337
|
+
apis: {},
|
|
338
|
+
};
|
|
339
|
+
const pageContent = (0, yaml_1.stringify)({
|
|
340
|
+
id: page.id,
|
|
341
|
+
name: page.name,
|
|
342
|
+
applicationId: page.applicationId,
|
|
343
|
+
isHidden: page.isHidden,
|
|
344
|
+
layouts: page.layouts,
|
|
345
|
+
}, {
|
|
346
|
+
sortMapEntries: true,
|
|
347
|
+
blockQuote: "literal",
|
|
348
|
+
});
|
|
349
|
+
await fs.outputFile(`${pageDirName}/page.yaml`, pageContent);
|
|
350
|
+
const pageApisDirName = `${pageDirName}/apis`;
|
|
351
|
+
for (const api of page.apis) {
|
|
352
|
+
const originalApiName = extractApiName(api);
|
|
353
|
+
apiPromises.push(writeApi(api, originalApiName, pageApisDirName));
|
|
354
|
+
newApplicationConfig.pages[pageId].apis[api.id] = originalApiName;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const apisDirName = `${appDirName}/apis`;
|
|
358
|
+
if (resource.apis && resource.apis.length) {
|
|
359
|
+
await fs.ensureDir(apisDirName);
|
|
360
|
+
for (const api of resource.apis) {
|
|
361
|
+
const originalApiName = extractApiName(api);
|
|
362
|
+
apiPromises.push(writeApi(api, originalApiName, apisDirName));
|
|
363
|
+
newApplicationConfig.apis[api.id] = originalApiName;
|
|
364
|
+
}
|
|
365
|
+
await Promise.all(apiPromises);
|
|
366
|
+
}
|
|
367
|
+
const existingApplicationConfig = await (0, util_1.getSuperblocksApplicationConfigIfExists)(appDirName);
|
|
368
|
+
// iterate through existing app-level apis and remove from disk any that are no longer present
|
|
369
|
+
for (const existingApiId of Object.keys((_a = existingApplicationConfig === null || existingApplicationConfig === void 0 ? void 0 : existingApplicationConfig.apis) !== null && _a !== void 0 ? _a : {})) {
|
|
370
|
+
const existingApiName = existingApplicationConfig === null || existingApplicationConfig === void 0 ? void 0 : existingApplicationConfig.apis[existingApiId];
|
|
371
|
+
const existingApiPath = `${apisDirName}/${slugifyName(existingApiName)}.yaml`;
|
|
372
|
+
const newApiName = newApplicationConfig.apis[existingApiId];
|
|
373
|
+
if (!(existingApiId in newApplicationConfig.apis) ||
|
|
374
|
+
slugifyName(newApiName) !== slugifyName(existingApiName)) {
|
|
375
|
+
await fs.remove(existingApiPath);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// iterate through existing pages and remove from disk any that are no longer present
|
|
379
|
+
for (const existingPage of Object.values((_b = existingApplicationConfig === null || existingApplicationConfig === void 0 ? void 0 : existingApplicationConfig.pages) !== null && _b !== void 0 ? _b : {})) {
|
|
380
|
+
const existingPageId = existingPage.id;
|
|
381
|
+
const existingPageDir = `${appDirName}/pages/${slugifyName(existingPage.name)}`;
|
|
382
|
+
if (existingPageId in newApplicationConfig.pages) {
|
|
383
|
+
// page still exists
|
|
384
|
+
const currentPage = newApplicationConfig.pages[existingPageId];
|
|
385
|
+
const newPageName = currentPage.name;
|
|
386
|
+
if (slugifyName(newPageName) !== slugifyName(existingPage.name)) {
|
|
387
|
+
// page was renamed
|
|
388
|
+
await fs.remove(existingPageDir);
|
|
389
|
+
}
|
|
390
|
+
// iterate through existing page-level apis and remove from disk any that are no longer present
|
|
391
|
+
for (const [existingApiId, existingApiName] of Object.entries(existingPage.apis)) {
|
|
392
|
+
const existingApiPath = `${existingPageDir}/apis/${slugifyName(existingApiName)}.yaml`;
|
|
393
|
+
if (existingApiId in currentPage.apis) {
|
|
394
|
+
// api still exists
|
|
395
|
+
const newApiName = newApplicationConfig.pages[existingPageId].apis[existingApiId];
|
|
396
|
+
if (slugifyName(newApiName) !== slugifyName(existingApiName)) {
|
|
397
|
+
// api was renamed
|
|
398
|
+
await fs.remove(existingApiPath);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
// api was removed
|
|
403
|
+
await fs.remove(existingApiPath);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
// page was removed
|
|
409
|
+
await fs.remove(existingPageDir);
|
|
410
|
+
// remove all page-level apis
|
|
411
|
+
for (const existingApiName of Object.values(existingPage.apis)) {
|
|
412
|
+
const existingApiPath = `${existingPageDir}/apis/${slugifyName(existingApiName)}.yaml`;
|
|
413
|
+
await fs.remove(existingApiPath);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
await fs.ensureDir(`${appDirName}/${util_2.SUPERBLOCKS_HOME_FOLDER_NAME}`);
|
|
418
|
+
await fs.writeFile(`${appDirName}/${util_2.RESOURCE_CONFIG_PATH}`, JSON.stringify(sortByKey(newApplicationConfig), null, 2));
|
|
419
|
+
const createdFiles = await Promise.resolve(
|
|
420
|
+
// Defensive check for when application settings are missing componentFiles
|
|
421
|
+
(_d = (_c = resource.componentFiles) === null || _c === void 0 ? void 0 : _c.map((file) => downloadFile(appDirName, file.filename, file.url))) !== null && _d !== void 0 ? _d : []);
|
|
422
|
+
// print out failed downloads synchronously here
|
|
423
|
+
createdFiles
|
|
424
|
+
.filter((createdFiles) => createdFiles.length)
|
|
425
|
+
.forEach((createdFile) => {
|
|
426
|
+
console.log(`Unable to download ${createdFile}`);
|
|
427
|
+
});
|
|
428
|
+
// do a post-migration cleanup if necessary
|
|
429
|
+
if (migrateFromSinglePage) {
|
|
430
|
+
await fs.remove(`${appDirName}/page.yaml`);
|
|
431
|
+
// delete app-level apis if they are not present in the new application config
|
|
432
|
+
if (!resource.apis || !resource.apis.length) {
|
|
433
|
+
await fs.remove(`${appDirName}/apis`);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
location: relativeLocation,
|
|
438
|
+
resourceType: "APPLICATION",
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
exports.writeMultiPageApplicationToDisk = writeMultiPageApplicationToDisk;
|
|
442
|
+
async function removeResourceFromDisk(rootPath, resourceRelativeLocation) {
|
|
443
|
+
const resourceLocation = node_path_1.default.resolve(rootPath, resourceRelativeLocation);
|
|
235
444
|
await fs.remove(resourceLocation);
|
|
236
445
|
}
|
|
237
446
|
exports.removeResourceFromDisk = removeResourceFromDisk;
|
|
@@ -365,8 +574,12 @@ async function getHeadCommit(branch) {
|
|
|
365
574
|
return [headCommitId, headCommitMessage];
|
|
366
575
|
}
|
|
367
576
|
exports.getHeadCommit = getHeadCommit;
|
|
577
|
+
function isCI() {
|
|
578
|
+
return process.env.CI === "true";
|
|
579
|
+
}
|
|
580
|
+
exports.isCI = isCI;
|
|
368
581
|
async function isGitRepoDirty() {
|
|
369
|
-
if (
|
|
582
|
+
if (isCI()) {
|
|
370
583
|
// Skip dirtiness check in CI environments
|
|
371
584
|
return false;
|
|
372
585
|
}
|
|
@@ -380,3 +593,155 @@ function extractApiName(api) {
|
|
|
380
593
|
return (_d = (_c = (_b = (_a = api.apiPb) === null || _a === void 0 ? void 0 : _a.metadata) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : api === null || api === void 0 ? void 0 : api.name) !== null && _d !== void 0 ? _d : "Unknown";
|
|
381
594
|
}
|
|
382
595
|
exports.extractApiName = extractApiName;
|
|
596
|
+
function writeApi(api, originalApiName, appDirName) {
|
|
597
|
+
const apiName = slugifyName(originalApiName);
|
|
598
|
+
const apiContent = (0, yaml_1.stringify)(api.apiPb, {
|
|
599
|
+
sortMapEntries: true,
|
|
600
|
+
blockQuote: "literal",
|
|
601
|
+
});
|
|
602
|
+
const handleApi = async () => {
|
|
603
|
+
await fs.outputFile(`${appDirName}/${apiName}.yaml`, apiContent);
|
|
604
|
+
};
|
|
605
|
+
return handleApi();
|
|
606
|
+
}
|
|
607
|
+
function validateSinglePageApplication(applicationConfig, superblocksRootPath, location) {
|
|
608
|
+
// validate app level APIs
|
|
609
|
+
for (const apiName of Object.values(applicationConfig.apis)) {
|
|
610
|
+
const apiPath = node_path_1.default.resolve(superblocksRootPath, location, "apis", `${slugifyName(apiName)}.yaml`);
|
|
611
|
+
const validateApiError = validateYamlFile(apiPath);
|
|
612
|
+
if (validateApiError) {
|
|
613
|
+
return validateApiError;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
// validate page
|
|
617
|
+
const pagePath = node_path_1.default.resolve(superblocksRootPath, location, "page.yaml");
|
|
618
|
+
const validatePageError = validateYamlFile(pagePath);
|
|
619
|
+
if (validatePageError) {
|
|
620
|
+
return validatePageError;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
async function validateMultiPageApplication(applicationConfig, superblocksRootPath, location) {
|
|
624
|
+
var _a;
|
|
625
|
+
// validate app level APIs
|
|
626
|
+
for (const apiName of Object.values(applicationConfig.apis)) {
|
|
627
|
+
const apiPath = node_path_1.default.resolve(superblocksRootPath, location, "apis", `${slugifyName(apiName)}.yaml`);
|
|
628
|
+
const validateApiError = await validateYamlFile(apiPath);
|
|
629
|
+
if (validateApiError) {
|
|
630
|
+
return validateApiError;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
// validate pages
|
|
634
|
+
for (const page of Object.values((_a = applicationConfig.pages) !== null && _a !== void 0 ? _a : {})) {
|
|
635
|
+
const pagePath = node_path_1.default.resolve(superblocksRootPath, location, "pages", slugifyName(page.name), "page.yaml");
|
|
636
|
+
const validatePageError = await validateYamlFile(pagePath);
|
|
637
|
+
if (validatePageError) {
|
|
638
|
+
return validatePageError;
|
|
639
|
+
}
|
|
640
|
+
// validate page level APIs
|
|
641
|
+
for (const apiName of Object.values(page.apis)) {
|
|
642
|
+
const apiPath = node_path_1.default.resolve(superblocksRootPath, location, "pages", slugifyName(page.name), "apis", `${slugifyName(apiName)}.yaml`);
|
|
643
|
+
const validateApiError = await validateYamlFile(apiPath);
|
|
644
|
+
if (validateApiError) {
|
|
645
|
+
return validateApiError;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return undefined;
|
|
650
|
+
}
|
|
651
|
+
async function validateLocalResource(superblocksRootPath, resource) {
|
|
652
|
+
switch (resource.resourceType) {
|
|
653
|
+
case "APPLICATION": {
|
|
654
|
+
// make sure application config exists
|
|
655
|
+
const applicationConfigPath = node_path_1.default.resolve(superblocksRootPath, resource.location, util_2.RESOURCE_CONFIG_PATH);
|
|
656
|
+
if (!(await fs.pathExists(applicationConfigPath))) {
|
|
657
|
+
return `File ${relativeToCurrentDir(applicationConfigPath)} not found. Superblocks CLI commands cannot function without it.`;
|
|
658
|
+
}
|
|
659
|
+
let fileStructureType = undefined;
|
|
660
|
+
let applicationConfig = undefined;
|
|
661
|
+
try {
|
|
662
|
+
// make sure it's a well-formed application config
|
|
663
|
+
applicationConfig = await fs.readJSON(applicationConfigPath);
|
|
664
|
+
if (!applicationConfig) {
|
|
665
|
+
throw new Error();
|
|
666
|
+
}
|
|
667
|
+
fileStructureType =
|
|
668
|
+
getFileStructureTypeFromResourceConfig(applicationConfig);
|
|
669
|
+
}
|
|
670
|
+
catch {
|
|
671
|
+
return `File ${relativeToCurrentDir(applicationConfigPath)} is not a valid JSON file. Please be sure it's valid JSON and rerun the command.`;
|
|
672
|
+
}
|
|
673
|
+
const applicationYamlPath = node_path_1.default.resolve(superblocksRootPath, resource.location, "application.yaml");
|
|
674
|
+
// make sure application.yaml is a well-formed yaml file
|
|
675
|
+
try {
|
|
676
|
+
await readYamlFile(applicationYamlPath);
|
|
677
|
+
}
|
|
678
|
+
catch {
|
|
679
|
+
return `File ${relativeToCurrentDir(applicationYamlPath)} is not a valid YAML file. Please be sure it's valid YAML and rerun the command.`;
|
|
680
|
+
}
|
|
681
|
+
switch (fileStructureType) {
|
|
682
|
+
case FileStructureType.SINGLE_PAGE: {
|
|
683
|
+
validateSinglePageApplication(applicationConfig, superblocksRootPath, resource.location);
|
|
684
|
+
break;
|
|
685
|
+
}
|
|
686
|
+
case FileStructureType.MULTI_PAGE: {
|
|
687
|
+
const validationError = validateMultiPageApplication(applicationConfig, superblocksRootPath, resource.location);
|
|
688
|
+
if (validationError) {
|
|
689
|
+
return validationError;
|
|
690
|
+
}
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
default: {
|
|
694
|
+
return `Invalid file structure type ${fileStructureType}`;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
break;
|
|
698
|
+
}
|
|
699
|
+
case "BACKEND": {
|
|
700
|
+
// make sure the backend config exists
|
|
701
|
+
const backendConfigPath = node_path_1.default.resolve(superblocksRootPath, resource.location, util_2.RESOURCE_CONFIG_PATH);
|
|
702
|
+
if (!(await fs.pathExists(backendConfigPath))) {
|
|
703
|
+
return `File ${relativeToCurrentDir(backendConfigPath)} not found. Superblocks CLI commands cannot function without it.`;
|
|
704
|
+
}
|
|
705
|
+
// make sure it's a well-formed backend config
|
|
706
|
+
try {
|
|
707
|
+
await fs.readJSON(backendConfigPath);
|
|
708
|
+
}
|
|
709
|
+
catch {
|
|
710
|
+
return `File ${relativeToCurrentDir(backendConfigPath)} is not a valid JSON file. Please be sure it's valid JSON and rerun the command.`;
|
|
711
|
+
}
|
|
712
|
+
// make sure that api.yaml exists
|
|
713
|
+
const apiYamlPath = node_path_1.default.resolve(superblocksRootPath, resource.location, "api.yaml");
|
|
714
|
+
const validateYamlFileError = await validateYamlFile(apiYamlPath);
|
|
715
|
+
if (validateYamlFileError) {
|
|
716
|
+
return validateYamlFileError;
|
|
717
|
+
}
|
|
718
|
+
break;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return undefined;
|
|
722
|
+
}
|
|
723
|
+
exports.validateLocalResource = validateLocalResource;
|
|
724
|
+
async function deleteResourcesAndUpdateRootConfig(removedResourceIds, existingSuperblocksRootConfig, superblocksRootPath, superblocksRootConfigPath) {
|
|
725
|
+
for (const resourceId of removedResourceIds) {
|
|
726
|
+
const resource = existingSuperblocksRootConfig === null || existingSuperblocksRootConfig === void 0 ? void 0 : existingSuperblocksRootConfig.resources[resourceId];
|
|
727
|
+
await removeResourceFromDisk(superblocksRootPath, resource.location);
|
|
728
|
+
}
|
|
729
|
+
for (const removedResourceId of removedResourceIds) {
|
|
730
|
+
delete existingSuperblocksRootConfig.resources[removedResourceId];
|
|
731
|
+
}
|
|
732
|
+
// update superblocks.json file with removed resources
|
|
733
|
+
await fs.writeFile(superblocksRootConfigPath, JSON.stringify(sortByKey(existingSuperblocksRootConfig), null, 2));
|
|
734
|
+
}
|
|
735
|
+
exports.deleteResourcesAndUpdateRootConfig = deleteResourcesAndUpdateRootConfig;
|
|
736
|
+
async function validateYamlFile(yamlPath) {
|
|
737
|
+
// make sure a yaml file is well-formed
|
|
738
|
+
try {
|
|
739
|
+
await readYamlFile(yamlPath);
|
|
740
|
+
}
|
|
741
|
+
catch {
|
|
742
|
+
return `File ${relativeToCurrentDir(yamlPath)} is not a valid YAML file. Please be sure it's valid YAML and rerun the command.`;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
function relativeToCurrentDir(applicationConfigPath) {
|
|
746
|
+
return `./${node_path_1.default.relative(process.cwd(), applicationConfigPath)}`;
|
|
747
|
+
}
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
2
|
+
"version": "1.7.0",
|
|
3
3
|
"commands": {
|
|
4
4
|
"init": {
|
|
5
5
|
"id": "init",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"args": {
|
|
31
31
|
"resource_url": {
|
|
32
32
|
"name": "resource_url",
|
|
33
|
-
"description": "Superblocks resource URL (i.e. https://app.superblocks.com/applications/<application_id
|
|
33
|
+
"description": "Superblocks resource URL (i.e. https://app.superblocks.com/applications/<application_id> or https://app.superblocks.com/applications/edit/<application_id>)",
|
|
34
34
|
"required": false
|
|
35
35
|
}
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superblocksteam/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Official Superblocks CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"superblocks": "bin/run"
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
"@oclif/core": "^2.11.7",
|
|
19
19
|
"@oclif/plugin-help": "^5.2.16",
|
|
20
20
|
"@oclif/plugin-plugins": "^3.1.10",
|
|
21
|
-
"@superblocksteam/css-plugin": "1.
|
|
22
|
-
"@superblocksteam/react-shim": "1.
|
|
23
|
-
"@superblocksteam/sdk": "1.
|
|
24
|
-
"@superblocksteam/util": "1.
|
|
25
|
-
"@superblocksteam/vite-custom-component-reload-plugin": "1.
|
|
21
|
+
"@superblocksteam/css-plugin": "1.7.0",
|
|
22
|
+
"@superblocksteam/react-shim": "1.7.0",
|
|
23
|
+
"@superblocksteam/sdk": "1.7.0",
|
|
24
|
+
"@superblocksteam/util": "1.7.0",
|
|
25
|
+
"@superblocksteam/vite-custom-component-reload-plugin": "1.7.0",
|
|
26
26
|
"@vitejs/plugin-react": "^4.1.0",
|
|
27
27
|
"colorette": "^2.0.19",
|
|
28
28
|
"enquirer": "^2.3.6",
|