sb-mig 5.4.1 → 5.5.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/dist/cli/cli-descriptions.d.ts +1 -0
- package/dist/cli/cli-descriptions.js +16 -0
- package/dist/cli/commands/copy.d.ts +2 -0
- package/dist/cli/commands/copy.js +195 -0
- package/dist/cli/index.js +11 -1
- package/dist/cli/roles/sync.js +0 -1
- package/dist/cli/utils/discover.js +8 -1
- package/dist/utils/others.d.ts +5 -0
- package/dist/utils/others.js +5 -0
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const mainDescription = "\n USAGE\n $ sb-mig [command]\n \n COMMANDS\n sync Synchronize components, datasources, roles, stories, assets with Storyblok space.\n discover Discover components and write to file or stdout.\n backup Command for backing up anything related to Storyblok\n migrate Migrate content from space to space, or from file to space.\n debug Output extra debugging information\n help This screen\n \n Examples\n $ sb-mig sync components --all\n $ sb-mig debug \n";
|
|
2
2
|
export declare const syncDescription = "\n Usage\n $ sb-mig sync [components|roles|datasources|plugins|content] [space separated file names] or --all\n \n Description\n Synchronize components, roles, datasources, plugins, content with Storyblok space.\n \n COMMANDS\n components - sync components\n roles - sync roles\n datasources - sync datasources\n plugins - sync plugins\n content - sync content (stories, assets) - ! right now destructive, it will move content from 1 space to another, completelly overwriting it\n \n FLAGS\n --all - Sync all components, roles, datasources [components, roles, datasources]\n --presets - Pass it, if u want to sync also with presets (will take longer) [components only]\n \n --yes - Skip ask for confirmation (dangerous, but useful in CI/CD) [content only]\n --from - Space ID from which you want to sync content [content only]\n --to - Space ID to which you want to sync content [content only]\n --syncDirection [fromSpaceToFile|fromFileToSpace|fromSpaceToSpace|fromAWStoSpace] \n - Sync direction (from, to) [content only]\n \n EXAMPLES\n $ sb-mig sync components --all\n $ sb-mig sync components --all --presets\n $ sb-mig sync components accordion accordion-item\n $ sb-mig sync components accordion accordion-item --presets\n \n $ sb-mig sync roles --all\n \n $ sb-mig sync datasources --all\n \n $ sb-mig sync plugins my-awesome-plugin - (you have to be in catalog which has ./dist/export.js file with compiled plugin)\n \n $ sb-mig sync content --all --from 12345 --to 12345\n $ sb-mig sync content --stories --from 12345 --to 12345\n $ sb-mig sync content --assets --from 12345 --to 12345\n";
|
|
3
|
+
export declare const copyDescription = "\n Usage\n $ sb-mig copy\n \n Description\n Copy stuff\n \n COMMANDS\n ?\n \n FLAGS\n ?\n \n EXAMPLES\n $ sb-mig copy ?\n";
|
|
3
4
|
export declare const migrateDescription = "\n Usage\n $ sb-mig migrate [content] [space separated file names] or --all --from [spaceId] --to [spaceId] --migration [migration-config-filename]\n \n Description\n Migrate content from space to space, or from file to space. It's potentially dangerous command, so it will ask for confirmation.\n Use with care.\n \n COMMANDS\n content - migrate content \n \n FLAGS\n --from - Space ID from which you want to migrate / or file name if passed '--migrate-from file'\n --to - Space ID to which you want to migrate\n --migrate-from - Migrate from (space, file) default: space\n --migration - File name of migration file (without extension)\n --yes - Skip ask for confirmation (dangerous, but useful in CI/CD) \n \n EXAMPLES\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration\n $ sb-mig migrate content --all --migrate-from file --from file-with-stories --to 12345 --migration file-with-migration\n $ sb-mig migrate content my-component-1 my-component-2 --from 12345 --to 12345 --migration file-with-migration\n $ sb-mig migrate content my-component-1 my-component-2 --migrate-from file --from file-with-stories --to 12345 --migration file-with-migration \n";
|
|
4
5
|
export declare const revertDescription = "\n Usage\n $ sb-mig revert [content] --migration\n \n Description\n Revert content migration\n \n COMMANDS\n content - revert content migration \n \n FLAGS\n --migration - ???\n --yes - Skip ask for confirmation (dangerous, but useful in CI/CD) \n \n EXAMPLES\n $ sb-mig revert content --migration \n";
|
|
5
6
|
export declare const discoverDescription = "\n Usage\n $ sb-mig discover [components] --all --write\n \n Description\n Discover all component and write to file or stdout\n \n COMMANDS\n components - discover components\n \n FLAGS\n --all - Discover all components\n --write - Write to file \n \n EXAMPLES\n $ sb-mig discover components --all\n $ sb-mig discover components --all -- write\n";
|
|
@@ -54,6 +54,22 @@ export const syncDescription = `
|
|
|
54
54
|
$ sb-mig sync content --stories --from 12345 --to 12345
|
|
55
55
|
$ sb-mig sync content --assets --from 12345 --to 12345
|
|
56
56
|
`;
|
|
57
|
+
export const copyDescription = `
|
|
58
|
+
Usage
|
|
59
|
+
$ sb-mig copy
|
|
60
|
+
|
|
61
|
+
Description
|
|
62
|
+
Copy stuff
|
|
63
|
+
|
|
64
|
+
COMMANDS
|
|
65
|
+
?
|
|
66
|
+
|
|
67
|
+
FLAGS
|
|
68
|
+
?
|
|
69
|
+
|
|
70
|
+
EXAMPLES
|
|
71
|
+
$ sb-mig copy ?
|
|
72
|
+
`;
|
|
57
73
|
export const migrateDescription = `
|
|
58
74
|
Usage
|
|
59
75
|
$ sb-mig migrate [content] [space separated file names] or --all --from [spaceId] --to [spaceId] --migration [migration-config-filename]
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { managementApi } from "../../api/managementApi.js";
|
|
2
|
+
import { createTree, traverseAndCreate } from "../../api/stories/tree.js";
|
|
3
|
+
import Logger from "../../utils/logger.js";
|
|
4
|
+
import { getSourceSpace, getTargetSpace, getWhat, getWhere, } from "../../utils/others.js";
|
|
5
|
+
import { apiConfig } from "../api-config.js";
|
|
6
|
+
const COPY_COMMANDS = {
|
|
7
|
+
stories: "stories",
|
|
8
|
+
};
|
|
9
|
+
const attachToFolder = async (where, targetSpace) => {
|
|
10
|
+
console.log("attachToFolder", where, targetSpace);
|
|
11
|
+
const entryStory = await managementApi.stories.getStoryBySlug(where, {
|
|
12
|
+
...apiConfig,
|
|
13
|
+
spaceId: targetSpace,
|
|
14
|
+
});
|
|
15
|
+
console.log("entryStory", entryStory);
|
|
16
|
+
return entryStory;
|
|
17
|
+
};
|
|
18
|
+
const decideStrategy = async (args) => {
|
|
19
|
+
const { what, sourceSpace } = args;
|
|
20
|
+
// Check if path ends with /* for recursive folder strategy
|
|
21
|
+
if (what.endsWith("/*")) {
|
|
22
|
+
const folderPath = what.slice(0, -2); // Remove /* from the end
|
|
23
|
+
const entryStory = await managementApi.stories.getStoryBySlug(folderPath, {
|
|
24
|
+
...apiConfig,
|
|
25
|
+
spaceId: sourceSpace,
|
|
26
|
+
});
|
|
27
|
+
if (!entryStory) {
|
|
28
|
+
throw new Error(`Story not found: ${folderPath}`);
|
|
29
|
+
}
|
|
30
|
+
if (!entryStory.story.is_folder) {
|
|
31
|
+
throw new Error(`${folderPath} is not a folder`);
|
|
32
|
+
}
|
|
33
|
+
return { strategy: "folder_recursive" };
|
|
34
|
+
}
|
|
35
|
+
// For regular paths, check if it's a folder or story
|
|
36
|
+
const entryStory = await managementApi.stories.getStoryBySlug(what, {
|
|
37
|
+
...apiConfig,
|
|
38
|
+
spaceId: sourceSpace,
|
|
39
|
+
});
|
|
40
|
+
if (!entryStory) {
|
|
41
|
+
throw new Error(`Story not found: ${what}`);
|
|
42
|
+
}
|
|
43
|
+
const entryStoryIsFolder = entryStory.story.is_folder;
|
|
44
|
+
if (entryStoryIsFolder) {
|
|
45
|
+
return { strategy: "folder_with_root" };
|
|
46
|
+
}
|
|
47
|
+
return { strategy: "story" };
|
|
48
|
+
};
|
|
49
|
+
export const copyCommand = async (props) => {
|
|
50
|
+
const { input, flags } = props;
|
|
51
|
+
const command = input[1];
|
|
52
|
+
switch (command) {
|
|
53
|
+
case COPY_COMMANDS.stories:
|
|
54
|
+
Logger.warning(`test sb-mig... with command: ${command}`);
|
|
55
|
+
console.log({ flags });
|
|
56
|
+
const sourceSpace = getSourceSpace(flags, apiConfig);
|
|
57
|
+
const targetSpace = getTargetSpace(flags, apiConfig);
|
|
58
|
+
const what = getWhat(flags);
|
|
59
|
+
const where = getWhere(flags);
|
|
60
|
+
console.log({ sourceSpace, targetSpace, what, where });
|
|
61
|
+
const { strategy } = await decideStrategy({ what, sourceSpace });
|
|
62
|
+
if (strategy === "folder_recursive") {
|
|
63
|
+
console.log({ strategy });
|
|
64
|
+
const rootStorySlug = what.slice(0, -2);
|
|
65
|
+
// 0. get the root story
|
|
66
|
+
const rootStory = await managementApi.stories.getStoryBySlug(rootStorySlug, {
|
|
67
|
+
...apiConfig,
|
|
68
|
+
spaceId: sourceSpace,
|
|
69
|
+
});
|
|
70
|
+
// 1. get all stories inside this folder, and recursively
|
|
71
|
+
const allSourceStories = [rootStory].concat(await managementApi.stories.getAllStories({
|
|
72
|
+
options: {
|
|
73
|
+
starts_with: `${rootStorySlug}/`,
|
|
74
|
+
},
|
|
75
|
+
}, {
|
|
76
|
+
...apiConfig,
|
|
77
|
+
spaceId: sourceSpace,
|
|
78
|
+
}));
|
|
79
|
+
const normalizedStories = allSourceStories
|
|
80
|
+
.map((item) => item.story)
|
|
81
|
+
.map((item) => {
|
|
82
|
+
if (item.full_slug === rootStorySlug) {
|
|
83
|
+
return {
|
|
84
|
+
...item,
|
|
85
|
+
parent_id: null,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
...item,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
const treeOfStories = createTree(normalizedStories);
|
|
93
|
+
const entryStory = await attachToFolder(where, targetSpace);
|
|
94
|
+
const reAttachedTreeOfStories = treeOfStories[0]?.children?.map((child) => {
|
|
95
|
+
return {
|
|
96
|
+
...child,
|
|
97
|
+
parent_id: entryStory.story.id,
|
|
98
|
+
story: {
|
|
99
|
+
...child.story,
|
|
100
|
+
parent_id: entryStory.story.id,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
await traverseAndCreate({
|
|
105
|
+
tree: reAttachedTreeOfStories,
|
|
106
|
+
realParentId: entryStory.story.id,
|
|
107
|
+
spaceId: targetSpace,
|
|
108
|
+
}, {
|
|
109
|
+
...apiConfig,
|
|
110
|
+
spaceId: targetSpace,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (strategy === "folder_with_root") {
|
|
114
|
+
console.log({ strategy });
|
|
115
|
+
// 0. get the root story
|
|
116
|
+
const rootStory = await managementApi.stories.getStoryBySlug(what, {
|
|
117
|
+
...apiConfig,
|
|
118
|
+
spaceId: sourceSpace,
|
|
119
|
+
});
|
|
120
|
+
// 1. get all stories
|
|
121
|
+
const allSourceStories = [rootStory].concat(await managementApi.stories.getAllStories({
|
|
122
|
+
options: {
|
|
123
|
+
starts_with: `${what}/`,
|
|
124
|
+
},
|
|
125
|
+
}, {
|
|
126
|
+
...apiConfig,
|
|
127
|
+
spaceId: sourceSpace,
|
|
128
|
+
}));
|
|
129
|
+
const normalizedStories = allSourceStories
|
|
130
|
+
.map((item) => item.story)
|
|
131
|
+
.map((item) => {
|
|
132
|
+
if (item.full_slug === what) {
|
|
133
|
+
return {
|
|
134
|
+
...item,
|
|
135
|
+
parent_id: null,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
...item,
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
const treeOfStories = createTree(normalizedStories);
|
|
143
|
+
const entryStory = await attachToFolder(where, targetSpace);
|
|
144
|
+
const enhancedTreeOfStories = {
|
|
145
|
+
...treeOfStories[0],
|
|
146
|
+
parent_id: entryStory.story.id,
|
|
147
|
+
};
|
|
148
|
+
await traverseAndCreate({
|
|
149
|
+
tree: [enhancedTreeOfStories],
|
|
150
|
+
realParentId: entryStory.story.id,
|
|
151
|
+
spaceId: targetSpace,
|
|
152
|
+
}, {
|
|
153
|
+
...apiConfig,
|
|
154
|
+
spaceId: targetSpace,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
if (strategy === "story") {
|
|
158
|
+
console.log({ strategy });
|
|
159
|
+
const entryStory = await managementApi.stories.getStoryBySlug(what, {
|
|
160
|
+
...apiConfig,
|
|
161
|
+
spaceId: sourceSpace,
|
|
162
|
+
});
|
|
163
|
+
const targetEntryStory = await attachToFolder(where, targetSpace);
|
|
164
|
+
const finalStories = [
|
|
165
|
+
{
|
|
166
|
+
...entryStory.story,
|
|
167
|
+
parent_id: targetEntryStory.story.id,
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
const normalizedStories = finalStories.map((item) => {
|
|
171
|
+
if (item.full_slug === what) {
|
|
172
|
+
return {
|
|
173
|
+
...item,
|
|
174
|
+
parent_id: null,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
...item,
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
const treeOfStories = createTree(normalizedStories);
|
|
182
|
+
await traverseAndCreate({
|
|
183
|
+
tree: treeOfStories,
|
|
184
|
+
realParentId: targetEntryStory.story.id,
|
|
185
|
+
spaceId: targetSpace,
|
|
186
|
+
}, {
|
|
187
|
+
...apiConfig,
|
|
188
|
+
spaceId: targetSpace,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
break;
|
|
192
|
+
default:
|
|
193
|
+
console.log(`no command like that: ${command}`);
|
|
194
|
+
}
|
|
195
|
+
};
|
package/dist/cli/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
2
|
import meow from "meow";
|
|
3
3
|
import { pipe, prop } from "../utils/main.js";
|
|
4
|
-
import { backupDescription, debugDescription, mainDescription, syncDescription, removeDescription, initDescription, discoverDescription, migrateDescription, revertDescription, migrationsDescription, } from "./cli-descriptions.js";
|
|
4
|
+
import { backupDescription, debugDescription, mainDescription, syncDescription, removeDescription, initDescription, discoverDescription, migrateDescription, revertDescription, migrationsDescription, copyDescription, } from "./cli-descriptions.js";
|
|
5
5
|
import { backup } from "./commands/backup.js";
|
|
6
|
+
import { copyCommand } from "./commands/copy.js";
|
|
6
7
|
import { debug } from "./commands/debug.js";
|
|
7
8
|
import { discover } from "./commands/discover.js";
|
|
8
9
|
import { init } from "./commands/init.js";
|
|
@@ -28,6 +29,15 @@ app.sync = () => ({
|
|
|
28
29
|
sync(cli);
|
|
29
30
|
},
|
|
30
31
|
});
|
|
32
|
+
app.copy = () => ({
|
|
33
|
+
cli: meow(copyDescription, {
|
|
34
|
+
importMeta: import.meta,
|
|
35
|
+
booleanDefault: undefined,
|
|
36
|
+
}),
|
|
37
|
+
action: (cli) => {
|
|
38
|
+
copyCommand(cli);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
31
41
|
app.migrate = () => ({
|
|
32
42
|
cli: meow(migrateDescription, {
|
|
33
43
|
importMeta: import.meta,
|
package/dist/cli/roles/sync.js
CHANGED
|
@@ -37,7 +37,6 @@ export const syncProvidedRoles = async ({ roles }, config) => {
|
|
|
37
37
|
local: allLocalSbComponentsSchemaFiles,
|
|
38
38
|
external: allExternalSbComponentsSchemaFiles,
|
|
39
39
|
});
|
|
40
|
-
console.log({ local, external });
|
|
41
40
|
// #4: sync - do all stuff already done (groups resolving, and so on)
|
|
42
41
|
await managementApi.roles.syncRoles({ specifiedRoles: [...local, ...external] }, config);
|
|
43
42
|
};
|
|
@@ -38,11 +38,18 @@ export const compare = (request) => {
|
|
|
38
38
|
p,
|
|
39
39
|
};
|
|
40
40
|
});
|
|
41
|
-
const splittedExternal = external
|
|
41
|
+
const splittedExternal = external
|
|
42
|
+
.map((p) => {
|
|
42
43
|
return {
|
|
43
44
|
name: p.split(path.sep)[p.split(path.sep).length - 1],
|
|
44
45
|
p,
|
|
45
46
|
};
|
|
47
|
+
})
|
|
48
|
+
.filter((file) => {
|
|
49
|
+
// 1. check if the file has node_modules > 1
|
|
50
|
+
const nodeModulesCount = (file.p.match(/node_modules/g) || [])
|
|
51
|
+
.length;
|
|
52
|
+
return nodeModulesCount > 1;
|
|
46
53
|
});
|
|
47
54
|
// we only want to modify external array, because we want sometimes remove stuff which are already on local (overwrite node_modules ones)
|
|
48
55
|
const result = {
|
package/dist/utils/others.d.ts
CHANGED
|
@@ -2,3 +2,8 @@ import type { RequestBaseConfig } from "../api/utils/request.js";
|
|
|
2
2
|
export declare const generateDatestamp: (datestamp: Date) => string;
|
|
3
3
|
export declare const getFrom: (flags: any, config: RequestBaseConfig) => string;
|
|
4
4
|
export declare const getTo: (flags: any, config: RequestBaseConfig) => string;
|
|
5
|
+
export declare const getSourceSpace: (flags: any, config: RequestBaseConfig) => string;
|
|
6
|
+
export declare const getTargetSpace: (flags: any, config: RequestBaseConfig) => string;
|
|
7
|
+
export declare const getWhat: (flags: any) => string;
|
|
8
|
+
export declare const getWhere: (flags: any) => string;
|
|
9
|
+
export declare const getRecursive: (flags: any) => boolean;
|
package/dist/utils/others.js
CHANGED
|
@@ -8,3 +8,8 @@ export const generateDatestamp = (datestamp) => {
|
|
|
8
8
|
};
|
|
9
9
|
export const getFrom = (flags, config) => (flags["from"] ? flags["from"] : config.spaceId).toString();
|
|
10
10
|
export const getTo = (flags, config) => (flags["to"] ? flags["to"] : config.spaceId).toString();
|
|
11
|
+
export const getSourceSpace = (flags, config) => (flags["sourceSpace"] ? flags["sourceSpace"] : config.spaceId).toString();
|
|
12
|
+
export const getTargetSpace = (flags, config) => (flags["targetSpace"] ? flags["targetSpace"] : config.spaceId).toString();
|
|
13
|
+
export const getWhat = (flags) => (flags["what"] ? flags["what"] : "all").toString();
|
|
14
|
+
export const getWhere = (flags) => (flags["where"] ? flags["where"] : "all").toString();
|
|
15
|
+
export const getRecursive = (flags) => flags["recursive"] ? flags["recursive"] : false;
|