sb-mig 5.7.0 → 5.8.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/api/components/components.sync.d.ts +1 -0
- package/dist/api/components/components.sync.js +50 -8
- package/dist/api/datasources/datasource-entries.js +31 -15
- package/dist/api/datasources/datasources.d.ts +2 -1
- package/dist/api/datasources/datasources.js +33 -3
- package/dist/api/datasources/datasources.sync.js +2 -2
- package/dist/api/datasources/datasources.types.d.ts +6 -1
- package/dist/api/datasources/error-formatting.d.ts +1 -0
- package/dist/api/datasources/error-formatting.js +24 -0
- package/dist/api/migrate.js +50 -12
- package/dist/api/migrate.types.d.ts +14 -6
- package/dist/api/plugins/plugins.d.ts +2 -1
- package/dist/api/plugins/plugins.js +19 -1
- package/dist/api/plugins/plugins.sync.js +8 -4
- package/dist/api/plugins/plugins.types.d.ts +1 -0
- package/dist/api/roles/roles.d.ts +2 -1
- package/dist/api/roles/roles.js +16 -3
- package/dist/api/roles/roles.sync.js +2 -2
- package/dist/api/roles/roles.types.d.ts +6 -3
- package/dist/api/stories/stories.js +29 -1
- package/dist/api/sync/sync.types.d.ts +3 -0
- package/dist/api-v2/sync/index.js +4 -89
- package/dist/cli/cli-descriptions.d.ts +1 -1
- package/dist/cli/cli-descriptions.js +4 -0
- package/dist/cli/commands/sync.js +109 -64
- package/dist/cli/datasources/sync.js +5 -2
- package/dist/cli/roles/sync.js +4 -4
- package/dist-cjs/api/components/components.sync.js +50 -8
- package/dist-cjs/api/datasources/datasource-entries.js +31 -15
- package/dist-cjs/api/datasources/datasources.js +33 -3
- package/dist-cjs/api/datasources/error-formatting.js +28 -0
- package/dist-cjs/api/plugins/plugins.js +19 -1
- package/dist-cjs/api/roles/roles.js +16 -3
- package/dist-cjs/api/stories/stories.js +29 -1
- package/dist-cjs/api-v2/sync/index.js +4 -89
- package/package.json +1 -1
package/dist/api/roles/roles.js
CHANGED
|
@@ -75,13 +75,16 @@ export const getRole = async (roleName, config) => {
|
|
|
75
75
|
})
|
|
76
76
|
.catch((err) => Logger.error(err));
|
|
77
77
|
};
|
|
78
|
-
export const syncRolesData = async ({ roles }, config) => {
|
|
78
|
+
export const syncRolesData = async ({ roles, dryRun }, config) => {
|
|
79
79
|
const result = {
|
|
80
80
|
created: [],
|
|
81
81
|
updated: [],
|
|
82
82
|
skipped: [],
|
|
83
83
|
errors: [],
|
|
84
84
|
};
|
|
85
|
+
if (dryRun) {
|
|
86
|
+
Logger.warning("[dry-run] Role sync will only read remote data and report planned changes.");
|
|
87
|
+
}
|
|
85
88
|
const space_roles_raw = await getAllRoles(config);
|
|
86
89
|
const space_roles = Array.isArray(space_roles_raw) ? space_roles_raw : [];
|
|
87
90
|
const rolesToUpdate = [];
|
|
@@ -99,17 +102,27 @@ export const syncRolesData = async ({ roles }, config) => {
|
|
|
99
102
|
rolesToCreate.push(role);
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
|
-
const updateResults =
|
|
105
|
+
const updateResults = dryRun
|
|
106
|
+
? rolesToUpdate.map(() => ({ status: "fulfilled" }))
|
|
107
|
+
: await Promise.allSettled(rolesToUpdate.map((role) => updateRole(role, config)));
|
|
103
108
|
updateResults.forEach((r, idx) => {
|
|
104
109
|
const name = String(rolesToUpdate[idx]?.role ?? "unknown");
|
|
110
|
+
if (dryRun) {
|
|
111
|
+
Logger.warning(`[dry-run] Would update role '${name}'.`);
|
|
112
|
+
}
|
|
105
113
|
if (r.status === "fulfilled")
|
|
106
114
|
result.updated.push(name);
|
|
107
115
|
else
|
|
108
116
|
result.errors.push({ name, message: String(r.reason) });
|
|
109
117
|
});
|
|
110
|
-
const createResults =
|
|
118
|
+
const createResults = dryRun
|
|
119
|
+
? rolesToCreate.map(() => ({ status: "fulfilled" }))
|
|
120
|
+
: await Promise.allSettled(rolesToCreate.map((role) => createRole(role, config)));
|
|
111
121
|
createResults.forEach((r, idx) => {
|
|
112
122
|
const name = String(rolesToCreate[idx]?.role ?? "unknown");
|
|
123
|
+
if (dryRun) {
|
|
124
|
+
Logger.warning(`[dry-run] Would create role '${name}'.`);
|
|
125
|
+
}
|
|
113
126
|
if (r.status === "fulfilled")
|
|
114
127
|
result.created.push(name);
|
|
115
128
|
else
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getFileContentWithRequire } from "../../utils/files.js";
|
|
2
2
|
import { syncRolesData } from "./roles.js";
|
|
3
|
-
export const syncRoles = async ({ specifiedRoles }, config) => {
|
|
3
|
+
export const syncRoles = async ({ specifiedRoles, dryRun }, config) => {
|
|
4
4
|
const specifiedRolesContent = await Promise.all(specifiedRoles.map((roles) => getFileContentWithRequire({ file: roles.p })));
|
|
5
|
-
await syncRolesData({ roles: specifiedRolesContent }, config);
|
|
5
|
+
await syncRolesData({ roles: specifiedRolesContent, dryRun }, config);
|
|
6
6
|
};
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { OneFileElement } from "../../utils/path-utils.js";
|
|
2
|
+
import type { SyncOptions } from "../sync/sync.types.js";
|
|
2
3
|
import type { RequestBaseConfig } from "../utils/request.js";
|
|
3
4
|
export type GetRole = (roleName: string | undefined, config: RequestBaseConfig) => Promise<void>;
|
|
4
5
|
export type GetAllRoles = (config: RequestBaseConfig) => Promise<any>;
|
|
5
6
|
export type CreateRole = (role: any, config: RequestBaseConfig) => void;
|
|
6
7
|
export type UpdateRole = (role: any, config: RequestBaseConfig) => void;
|
|
7
|
-
export type SyncRoles = ({ specifiedRoles }: {
|
|
8
|
+
export type SyncRoles = ({ specifiedRoles, dryRun, }: {
|
|
8
9
|
specifiedRoles: OneFileElement[];
|
|
10
|
+
dryRun?: boolean;
|
|
9
11
|
}, config: RequestBaseConfig) => Promise<void>;
|
|
10
|
-
export type SyncAllRoles = (config: RequestBaseConfig) => Promise<void>;
|
|
11
|
-
export type SyncProvidedRoles = ({ roles }: {
|
|
12
|
+
export type SyncAllRoles = (config: RequestBaseConfig, options?: SyncOptions) => Promise<void>;
|
|
13
|
+
export type SyncProvidedRoles = ({ roles, dryRun }: {
|
|
12
14
|
roles: string[];
|
|
15
|
+
dryRun?: boolean;
|
|
13
16
|
}, config: RequestBaseConfig) => Promise<void>;
|
|
@@ -2,6 +2,24 @@ import chalk from "chalk";
|
|
|
2
2
|
import Logger from "../../utils/logger.js";
|
|
3
3
|
import { notNullish } from "../../utils/object-utils.js";
|
|
4
4
|
import { getAllItemsWithPagination } from "../utils/request.js";
|
|
5
|
+
const resolveStoryLabel = (content, storyId) => content?.full_slug || content?.slug || content?.name || String(storyId);
|
|
6
|
+
const resolveStoryblokErrorResponse = (err) => {
|
|
7
|
+
if (typeof err?.response === "string" && err.response.trim().length > 0) {
|
|
8
|
+
return err.response.trim();
|
|
9
|
+
}
|
|
10
|
+
if (typeof err?.response?.data === "string" &&
|
|
11
|
+
err.response.data.trim().length > 0) {
|
|
12
|
+
return err.response.data.trim();
|
|
13
|
+
}
|
|
14
|
+
if (typeof err?.response?.message === "string" &&
|
|
15
|
+
err.response.message.trim().length > 0) {
|
|
16
|
+
return err.response.message.trim();
|
|
17
|
+
}
|
|
18
|
+
if (typeof err?.message === "string" && err.message.trim().length > 0) {
|
|
19
|
+
return err.message.trim();
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
};
|
|
5
23
|
export const removeStory = (args, config) => {
|
|
6
24
|
const { storyId } = args;
|
|
7
25
|
const { spaceId, sbApi } = config;
|
|
@@ -106,6 +124,7 @@ export const createStory = (content, config) => {
|
|
|
106
124
|
// UPDATE
|
|
107
125
|
export const updateStory = (content, storyId, options, config) => {
|
|
108
126
|
const { spaceId, sbApi } = config;
|
|
127
|
+
const storyLabel = resolveStoryLabel(content, storyId);
|
|
109
128
|
Logger.warning("Trying to update Story...");
|
|
110
129
|
Logger.log(`Updating story with name: ${content.name} in space: ${spaceId}`);
|
|
111
130
|
// console.log("THis is content to update: ");
|
|
@@ -127,12 +146,21 @@ export const updateStory = (content, storyId, options, config) => {
|
|
|
127
146
|
};
|
|
128
147
|
})
|
|
129
148
|
.catch((err) => {
|
|
130
|
-
|
|
149
|
+
const status = err?.status || err?.response?.status;
|
|
150
|
+
const responseMessage = resolveStoryblokErrorResponse(err);
|
|
151
|
+
const statusLabel = status ? `status ${status}` : "unknown status";
|
|
152
|
+
const responseLabel = responseMessage
|
|
153
|
+
? ` Response: ${responseMessage}`
|
|
154
|
+
: "";
|
|
155
|
+
Logger.error(`Failed to update story '${storyLabel}' in space '${spaceId}' (${statusLabel}).${responseLabel}`);
|
|
131
156
|
return {
|
|
132
157
|
ok: false,
|
|
133
158
|
id: storyId,
|
|
134
159
|
name: content?.name,
|
|
135
160
|
slug: content?.full_slug || content?.slug,
|
|
161
|
+
spaceId,
|
|
162
|
+
status,
|
|
163
|
+
response: responseMessage,
|
|
136
164
|
error: err,
|
|
137
165
|
};
|
|
138
166
|
});
|
|
@@ -5,105 +5,20 @@ import { syncRolesData } from "../../api/roles/roles.js";
|
|
|
5
5
|
import { toRequestConfig } from "../requestConfig.js";
|
|
6
6
|
export async function syncComponents(client, args) {
|
|
7
7
|
const presets = args.presets ?? false;
|
|
8
|
-
if (args.dryRun) {
|
|
9
|
-
// minimal dry-run: compare names against remote
|
|
10
|
-
const remote = await client.sbApi.get(`spaces/${client.spaceId}/components/`, {
|
|
11
|
-
per_page: 100,
|
|
12
|
-
page: 1,
|
|
13
|
-
});
|
|
14
|
-
const remoteNames = new Set(remote.data?.components?.map((c) => c.name) ?? []);
|
|
15
|
-
const created = [];
|
|
16
|
-
const updated = [];
|
|
17
|
-
const skipped = [];
|
|
18
|
-
for (const c of args.components) {
|
|
19
|
-
const name = String(c?.name ?? "unknown");
|
|
20
|
-
if (!c?.name) {
|
|
21
|
-
skipped.push(name);
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
if (remoteNames.has(c.name))
|
|
25
|
-
updated.push(name);
|
|
26
|
-
else
|
|
27
|
-
created.push(name);
|
|
28
|
-
}
|
|
29
|
-
return { created, updated, skipped, errors: [] };
|
|
30
|
-
}
|
|
31
8
|
return (await syncComponentsData({
|
|
32
9
|
components: args.components,
|
|
33
10
|
presets,
|
|
34
11
|
ssot: args.ssot,
|
|
12
|
+
dryRun: args.dryRun,
|
|
35
13
|
onProgress: args.onProgress,
|
|
36
14
|
}, toRequestConfig(client)));
|
|
37
15
|
}
|
|
38
16
|
export async function syncDatasources(client, args) {
|
|
39
|
-
|
|
40
|
-
const remote = await client.sbApi.get(`spaces/${client.spaceId}/datasources/`);
|
|
41
|
-
const remoteNames = new Set(remote.data?.datasources?.map((d) => d.name) ?? []);
|
|
42
|
-
const created = [];
|
|
43
|
-
const updated = [];
|
|
44
|
-
const skipped = [];
|
|
45
|
-
for (const d of args.datasources) {
|
|
46
|
-
const name = String(d?.name ?? "unknown");
|
|
47
|
-
if (!d?.name) {
|
|
48
|
-
skipped.push(name);
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (remoteNames.has(d.name))
|
|
52
|
-
updated.push(name);
|
|
53
|
-
else
|
|
54
|
-
created.push(name);
|
|
55
|
-
}
|
|
56
|
-
return { created, updated, skipped, errors: [] };
|
|
57
|
-
}
|
|
58
|
-
return (await syncDatasourcesData({ datasources: args.datasources }, toRequestConfig(client)));
|
|
17
|
+
return (await syncDatasourcesData({ datasources: args.datasources, dryRun: args.dryRun }, toRequestConfig(client)));
|
|
59
18
|
}
|
|
60
19
|
export async function syncRoles(client, args) {
|
|
61
|
-
|
|
62
|
-
const remote = await client.sbApi.get(`spaces/${client.spaceId}/space_roles/`, {
|
|
63
|
-
per_page: 100,
|
|
64
|
-
page: 1,
|
|
65
|
-
});
|
|
66
|
-
const remoteNames = new Set(remote.data?.space_roles?.map((r) => r.role) ?? []);
|
|
67
|
-
const created = [];
|
|
68
|
-
const updated = [];
|
|
69
|
-
const skipped = [];
|
|
70
|
-
for (const r of args.roles) {
|
|
71
|
-
const name = String(r?.role ?? "unknown");
|
|
72
|
-
if (!r?.role) {
|
|
73
|
-
skipped.push(name);
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (remoteNames.has(r.role))
|
|
77
|
-
updated.push(name);
|
|
78
|
-
else
|
|
79
|
-
created.push(name);
|
|
80
|
-
}
|
|
81
|
-
return { created, updated, skipped, errors: [] };
|
|
82
|
-
}
|
|
83
|
-
return (await syncRolesData({ roles: args.roles }, toRequestConfig(client)));
|
|
20
|
+
return (await syncRolesData({ roles: args.roles, dryRun: args.dryRun }, toRequestConfig(client)));
|
|
84
21
|
}
|
|
85
22
|
export async function syncPlugins(client, args) {
|
|
86
|
-
|
|
87
|
-
const remote = await client.sbApi.get("field_types", {
|
|
88
|
-
per_page: 100,
|
|
89
|
-
page: 1,
|
|
90
|
-
});
|
|
91
|
-
const remoteNames = new Set(remote.data?.field_types?.map((p) => p.name) ?? []);
|
|
92
|
-
const created = [];
|
|
93
|
-
const updated = [];
|
|
94
|
-
const skipped = [];
|
|
95
|
-
for (const p of args.plugins) {
|
|
96
|
-
const name = String(p?.name ?? "unknown");
|
|
97
|
-
if (!p?.name) {
|
|
98
|
-
skipped.push(name);
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
if (remoteNames.has(p.name))
|
|
102
|
-
updated.push(name);
|
|
103
|
-
else
|
|
104
|
-
created.push(name);
|
|
105
|
-
}
|
|
106
|
-
return { created, updated, skipped, errors: [] };
|
|
107
|
-
}
|
|
108
|
-
return (await syncPluginsData({ plugins: args.plugins }, toRequestConfig(client)));
|
|
23
|
+
return (await syncPluginsData({ plugins: args.plugins, dryRun: args.dryRun }, toRequestConfig(client)));
|
|
109
24
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
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, migration configs 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
|
-
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";
|
|
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 --dry-run - Preview planned changes without making writes [components, roles, datasources, plugins, content]\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 --dry-run\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 $ sb-mig sync roles --all --dry-run\n \n $ sb-mig sync datasources --all\n $ sb-mig sync datasources --all --dry-run\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
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";
|
|
4
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 $ sb-mig migrate content --all --migration migration-a --migration migration-b --migration migration-c\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 --fromFilePath - Direct path to stories JSON file when using '--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). Can be repeated for ordered pipeline in content migration.\n --migrationComponentAlias - Add extra component aliases for a migration. Repeatable. Format: <migration>:<source>=<alias1>,<alias2>\n --migrationComponents - Override the exact component scope for a migration. Repeatable. Format: <migration>:<component1>,<component2>\n --withSlug - Filter stories by full slug (can be repeated)\n --startsWith - Filter stories by starts_with prefix\n --yes - Skip ask for confirmation (dangerous, but useful in CI/CD)\n --dry-run - Preview what would be migrated without making any API changes\n --fileName - Stable base name for migration output files (disables timestamp suffix for migration artifacts)\n\n EXAMPLES\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration migration-a --migration migration-b --migration migration-c\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration colorPickerModeValues --migrationComponentAlias colorPickerModeValues:sb-button=sb-open-drift-button\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration colorPickerModeValues --migrationComponentAlias colorPickerModeValues:sb-section=sb-tour-page-section --migrationComponents colorPickerModeValues:sb-section,sb-tour-page-section\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --withSlug blog/home --withSlug docs/getting-started\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --startsWith blog/\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration v3toV4AllMigrations --dry-run --fileName brand-hub-v3-v4-run\n $ sb-mig migrate content --all --migrate-from file --from file-with-stories --to 12345 --migration file-with-migration\n $ sb-mig migrate content --all --migrate-from file --fromFilePath sbmig/migrations/dry-run--123---story-to-migrate__2026-2-9_20-51.json --to 12345 --migration migration-a --migration migration-b\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";
|
|
5
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";
|
|
@@ -31,6 +31,7 @@ export const syncDescription = `
|
|
|
31
31
|
FLAGS
|
|
32
32
|
--all - Sync all components, roles, datasources [components, roles, datasources]
|
|
33
33
|
--presets - Pass it, if u want to sync also with presets (will take longer) [components only]
|
|
34
|
+
--dry-run - Preview planned changes without making writes [components, roles, datasources, plugins, content]
|
|
34
35
|
|
|
35
36
|
--yes - Skip ask for confirmation (dangerous, but useful in CI/CD) [content only]
|
|
36
37
|
--from - Space ID from which you want to sync content [content only]
|
|
@@ -40,13 +41,16 @@ export const syncDescription = `
|
|
|
40
41
|
|
|
41
42
|
EXAMPLES
|
|
42
43
|
$ sb-mig sync components --all
|
|
44
|
+
$ sb-mig sync components --all --dry-run
|
|
43
45
|
$ sb-mig sync components --all --presets
|
|
44
46
|
$ sb-mig sync components accordion accordion-item
|
|
45
47
|
$ sb-mig sync components accordion accordion-item --presets
|
|
46
48
|
|
|
47
49
|
$ sb-mig sync roles --all
|
|
50
|
+
$ sb-mig sync roles --all --dry-run
|
|
48
51
|
|
|
49
52
|
$ sb-mig sync datasources --all
|
|
53
|
+
$ sb-mig sync datasources --all --dry-run
|
|
50
54
|
|
|
51
55
|
$ sb-mig sync plugins my-awesome-plugin - (you have to be in catalog which has ./dist/export.js file with compiled plugin)
|
|
52
56
|
|
|
@@ -17,6 +17,7 @@ const SYNC_COMMANDS = {
|
|
|
17
17
|
export const sync = async (props) => {
|
|
18
18
|
const { input, flags } = props;
|
|
19
19
|
const command = input[1];
|
|
20
|
+
const dryRun = Boolean(flags["dryRun"]);
|
|
20
21
|
const rules = {
|
|
21
22
|
all: ["all"],
|
|
22
23
|
assets: ["assets"],
|
|
@@ -34,6 +35,7 @@ export const sync = async (props) => {
|
|
|
34
35
|
"presets",
|
|
35
36
|
"packageName",
|
|
36
37
|
"yes",
|
|
38
|
+
"dryRun",
|
|
37
39
|
]);
|
|
38
40
|
switch (command) {
|
|
39
41
|
case SYNC_COMMANDS.components:
|
|
@@ -41,53 +43,65 @@ export const sync = async (props) => {
|
|
|
41
43
|
if (isIt("all")) {
|
|
42
44
|
Logger.log(`Syncing ALL components with ${storyblokConfig.schemaFileExt} extension...`);
|
|
43
45
|
const presets = flags["presets"] || false;
|
|
44
|
-
await syncAllComponents(presets, apiConfig);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
await syncAllComponents(presets, apiConfig, { dryRun });
|
|
47
|
+
if (!dryRun) {
|
|
48
|
+
await setComponentDefaultPreset({
|
|
49
|
+
presets: Boolean(flags.presets),
|
|
50
|
+
apiConfig,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
49
53
|
}
|
|
50
54
|
if (isIt("empty")) {
|
|
51
55
|
Logger.warning("Synchronizing PROVIDED components...");
|
|
52
56
|
const componentsToSync = unpackElements(input);
|
|
53
|
-
await syncProvidedComponents(Boolean(flags.presets), componentsToSync, flags.packageName, apiConfig);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
await syncProvidedComponents(Boolean(flags.presets), componentsToSync, flags.packageName, apiConfig, { dryRun });
|
|
58
|
+
if (!dryRun) {
|
|
59
|
+
await setComponentDefaultPreset({
|
|
60
|
+
presets: Boolean(flags.presets),
|
|
61
|
+
componentsToSync,
|
|
62
|
+
apiConfig,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
59
65
|
}
|
|
60
66
|
if (isIt("allWithSSOT")) {
|
|
61
67
|
Logger.warning("Synchronizing ALL components as Single Source of Truth...");
|
|
62
68
|
const presets = flags["presets"] || false;
|
|
63
|
-
|
|
64
|
-
await
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
+
if (dryRun) {
|
|
70
|
+
await syncAllComponents(presets, apiConfig, {
|
|
71
|
+
dryRun,
|
|
72
|
+
ssot: true,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
await askForConfirmation("Are you sure you want to use Single Source of truth? It will remove all your components added in GUI and replace them 1 to 1 with code versions.", async () => {
|
|
77
|
+
await removeAllComponents(apiConfig);
|
|
78
|
+
await syncAllComponents(presets, apiConfig);
|
|
79
|
+
}, () => {
|
|
80
|
+
Logger.success("Syncing components aborted.");
|
|
81
|
+
}, flags["yes"]);
|
|
82
|
+
}
|
|
69
83
|
}
|
|
70
84
|
break;
|
|
71
85
|
case SYNC_COMMANDS.roles:
|
|
72
86
|
if (isIt("all")) {
|
|
73
87
|
Logger.log("Syncing all roles...");
|
|
74
|
-
await syncAllRoles(apiConfig);
|
|
88
|
+
await syncAllRoles(apiConfig, { dryRun });
|
|
75
89
|
}
|
|
76
90
|
if (isIt("empty")) {
|
|
77
91
|
Logger.log("Syncing provided roles...");
|
|
78
92
|
const rolesToSync = unpackElements(input);
|
|
79
|
-
await syncProvidedRoles({ roles: rolesToSync }, apiConfig);
|
|
93
|
+
await syncProvidedRoles({ roles: rolesToSync, dryRun }, apiConfig);
|
|
80
94
|
}
|
|
81
95
|
break;
|
|
82
96
|
case SYNC_COMMANDS.datasources:
|
|
83
97
|
if (isIt("all")) {
|
|
84
|
-
Logger.log("Syncing all datasources with extension
|
|
85
|
-
await syncAllDatasources(apiConfig);
|
|
98
|
+
Logger.log(`${dryRun ? "[dry-run] " : ""}Syncing all datasources with extension...`);
|
|
99
|
+
await syncAllDatasources(apiConfig, { dryRun });
|
|
86
100
|
}
|
|
87
101
|
if (!flags["all"]) {
|
|
88
|
-
Logger.log("Syncing provided datasources with extension
|
|
102
|
+
Logger.log(`${dryRun ? "[dry-run] " : ""}Syncing provided datasources with extension...`);
|
|
89
103
|
const datasourcesToSync = unpackElements(input);
|
|
90
|
-
await syncProvidedDatasources({ datasources: datasourcesToSync }, apiConfig);
|
|
104
|
+
await syncProvidedDatasources({ datasources: datasourcesToSync, dryRun }, apiConfig);
|
|
91
105
|
}
|
|
92
106
|
break;
|
|
93
107
|
case SYNC_COMMANDS.content:
|
|
@@ -107,36 +121,52 @@ export const sync = async (props) => {
|
|
|
107
121
|
if (syncDirection) {
|
|
108
122
|
if (isIt("all")) {
|
|
109
123
|
if (syncDirection !== "fromSpaceToFile") {
|
|
110
|
-
|
|
111
|
-
Logger.warning("Deleting all stories in your space and then applying migrated ones...");
|
|
112
|
-
// Backup all stories to file
|
|
113
|
-
await syncContent({
|
|
114
|
-
type: "stories",
|
|
115
|
-
transmission: {
|
|
116
|
-
from: to,
|
|
117
|
-
to: `${to}_stories-backup`,
|
|
118
|
-
},
|
|
119
|
-
syncDirection: "fromSpaceToFile",
|
|
120
|
-
}, apiConfig);
|
|
121
|
-
// Remove all stories from 'to' space
|
|
122
|
-
await managementApi.stories.removeAllStories({
|
|
123
|
-
...apiConfig,
|
|
124
|
-
spaceId: to,
|
|
125
|
-
});
|
|
126
|
-
// Sync stories from 'from' space to 'to' space
|
|
124
|
+
if (dryRun) {
|
|
127
125
|
await syncContent({
|
|
128
126
|
type: "stories",
|
|
129
127
|
transmission: { from, to },
|
|
130
128
|
syncDirection,
|
|
129
|
+
dryRun,
|
|
131
130
|
}, apiConfig);
|
|
132
131
|
await syncContent({
|
|
133
132
|
type: "assets",
|
|
134
133
|
transmission: { from, to },
|
|
135
134
|
syncDirection,
|
|
135
|
+
dryRun,
|
|
136
136
|
}, apiConfig);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
await askForConfirmation("Are you sure you want to delete all content (stories) in your space and then apply new ones ?", async () => {
|
|
140
|
+
Logger.warning("Deleting all stories in your space and then applying migrated ones...");
|
|
141
|
+
// Backup all stories to file
|
|
142
|
+
await syncContent({
|
|
143
|
+
type: "stories",
|
|
144
|
+
transmission: {
|
|
145
|
+
from: to,
|
|
146
|
+
to: `${to}_stories-backup`,
|
|
147
|
+
},
|
|
148
|
+
syncDirection: "fromSpaceToFile",
|
|
149
|
+
}, apiConfig);
|
|
150
|
+
// Remove all stories from 'to' space
|
|
151
|
+
await managementApi.stories.removeAllStories({
|
|
152
|
+
...apiConfig,
|
|
153
|
+
spaceId: to,
|
|
154
|
+
});
|
|
155
|
+
// Sync stories from 'from' space to 'to' space
|
|
156
|
+
await syncContent({
|
|
157
|
+
type: "stories",
|
|
158
|
+
transmission: { from, to },
|
|
159
|
+
syncDirection,
|
|
160
|
+
}, apiConfig);
|
|
161
|
+
await syncContent({
|
|
162
|
+
type: "assets",
|
|
163
|
+
transmission: { from, to },
|
|
164
|
+
syncDirection,
|
|
165
|
+
}, apiConfig);
|
|
166
|
+
}, () => {
|
|
167
|
+
Logger.success("Stories not deleted, exiting the program...");
|
|
168
|
+
}, flags["yes"]);
|
|
169
|
+
}
|
|
140
170
|
}
|
|
141
171
|
else {
|
|
142
172
|
await syncContent({
|
|
@@ -144,11 +174,13 @@ export const sync = async (props) => {
|
|
|
144
174
|
transmission: { from, to },
|
|
145
175
|
syncDirection,
|
|
146
176
|
filename: flags.to,
|
|
177
|
+
dryRun,
|
|
147
178
|
}, apiConfig);
|
|
148
179
|
await syncContent({
|
|
149
180
|
type: "assets",
|
|
150
181
|
transmission: { from, to },
|
|
151
182
|
syncDirection,
|
|
183
|
+
dryRun,
|
|
152
184
|
}, apiConfig);
|
|
153
185
|
}
|
|
154
186
|
}
|
|
@@ -158,37 +190,48 @@ export const sync = async (props) => {
|
|
|
158
190
|
type: "assets",
|
|
159
191
|
transmission: { from, to },
|
|
160
192
|
syncDirection,
|
|
193
|
+
dryRun,
|
|
161
194
|
}, apiConfig);
|
|
162
195
|
}
|
|
163
196
|
else if (isIt("stories")) {
|
|
164
197
|
Logger.warning(`Syncing using sync direction: ${syncDirection}`);
|
|
165
198
|
if (syncDirection !== "fromSpaceToFile") {
|
|
166
|
-
|
|
167
|
-
Logger.warning("Deleting all stories in your space and then applying test ones...");
|
|
168
|
-
// Backup all stories to file
|
|
169
|
-
await syncContent({
|
|
170
|
-
type: "stories",
|
|
171
|
-
transmission: {
|
|
172
|
-
from: to,
|
|
173
|
-
to: `${to}_stories-backup`,
|
|
174
|
-
},
|
|
175
|
-
syncDirection: "fromSpaceToFile",
|
|
176
|
-
}, apiConfig);
|
|
177
|
-
// Remove all stories from 'to' space
|
|
178
|
-
await managementApi.stories.removeAllStories({
|
|
179
|
-
...apiConfig,
|
|
180
|
-
spaceId: to,
|
|
181
|
-
});
|
|
182
|
-
//
|
|
183
|
-
// Sync stories to 'to' space
|
|
199
|
+
if (dryRun) {
|
|
184
200
|
await syncContent({
|
|
185
201
|
type: "stories",
|
|
186
202
|
transmission: { from, to },
|
|
187
203
|
syncDirection,
|
|
204
|
+
dryRun,
|
|
188
205
|
}, apiConfig);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
await askForConfirmation("Are you sure you want to delete all stories in your space and then apply new ones ?", async () => {
|
|
209
|
+
Logger.warning("Deleting all stories in your space and then applying test ones...");
|
|
210
|
+
// Backup all stories to file
|
|
211
|
+
await syncContent({
|
|
212
|
+
type: "stories",
|
|
213
|
+
transmission: {
|
|
214
|
+
from: to,
|
|
215
|
+
to: `${to}_stories-backup`,
|
|
216
|
+
},
|
|
217
|
+
syncDirection: "fromSpaceToFile",
|
|
218
|
+
}, apiConfig);
|
|
219
|
+
// Remove all stories from 'to' space
|
|
220
|
+
await managementApi.stories.removeAllStories({
|
|
221
|
+
...apiConfig,
|
|
222
|
+
spaceId: to,
|
|
223
|
+
});
|
|
224
|
+
//
|
|
225
|
+
// Sync stories to 'to' space
|
|
226
|
+
await syncContent({
|
|
227
|
+
type: "stories",
|
|
228
|
+
transmission: { from, to },
|
|
229
|
+
syncDirection,
|
|
230
|
+
}, apiConfig);
|
|
231
|
+
}, () => {
|
|
232
|
+
Logger.success("Stories not deleted, exiting the program...");
|
|
233
|
+
}, flags["yes"]);
|
|
234
|
+
}
|
|
192
235
|
}
|
|
193
236
|
else {
|
|
194
237
|
// Sync stories to 'to' space
|
|
@@ -196,6 +239,7 @@ export const sync = async (props) => {
|
|
|
196
239
|
type: "stories",
|
|
197
240
|
transmission: { from, to },
|
|
198
241
|
syncDirection,
|
|
242
|
+
dryRun,
|
|
199
243
|
}, apiConfig);
|
|
200
244
|
}
|
|
201
245
|
}
|
|
@@ -216,6 +260,7 @@ export const sync = async (props) => {
|
|
|
216
260
|
const pluginsToSync = unpackElements(input);
|
|
217
261
|
await managementApi.plugins.syncProvidedPlugins({
|
|
218
262
|
plugins: pluginsToSync,
|
|
263
|
+
dryRun,
|
|
219
264
|
}, apiConfig);
|
|
220
265
|
}
|
|
221
266
|
break;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { managementApi } from "../../api/managementApi.js";
|
|
2
2
|
import { compare, discoverDatasources, discoverManyDatasources, LOOKUP_TYPE, SCOPE, } from "../utils/discover.js";
|
|
3
3
|
export const syncProvidedDatasources = async (args, config) => {
|
|
4
|
-
const { datasources } = args;
|
|
4
|
+
const { datasources, dryRun } = args;
|
|
5
5
|
const allLocalDatasources = await discoverManyDatasources({
|
|
6
6
|
scope: SCOPE.local,
|
|
7
7
|
type: LOOKUP_TYPE.fileName,
|
|
@@ -19,9 +19,11 @@ export const syncProvidedDatasources = async (args, config) => {
|
|
|
19
19
|
});
|
|
20
20
|
await managementApi.datasources.syncDatasources({
|
|
21
21
|
providedDatasources: [...local, ...external],
|
|
22
|
+
dryRun,
|
|
22
23
|
}, config);
|
|
23
24
|
};
|
|
24
|
-
export const syncAllDatasources = async (config) => {
|
|
25
|
+
export const syncAllDatasources = async (config, args = {}) => {
|
|
26
|
+
const { dryRun } = args;
|
|
25
27
|
const allLocalDatasources = await discoverDatasources({
|
|
26
28
|
scope: SCOPE.local,
|
|
27
29
|
type: LOOKUP_TYPE.fileName,
|
|
@@ -36,5 +38,6 @@ export const syncAllDatasources = async (config) => {
|
|
|
36
38
|
});
|
|
37
39
|
await managementApi.datasources.syncDatasources({
|
|
38
40
|
providedDatasources: [...local, ...external],
|
|
41
|
+
dryRun,
|
|
39
42
|
}, config);
|
|
40
43
|
};
|
package/dist/cli/roles/sync.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { managementApi } from "../../api/managementApi.js";
|
|
2
2
|
import { compare, discoverManyRoles, discoverRoles, LOOKUP_TYPE, SCOPE, } from "../utils/discover.js";
|
|
3
|
-
export const syncAllRoles = async (config) => {
|
|
3
|
+
export const syncAllRoles = async (config, options = {}) => {
|
|
4
4
|
// #1: discover all external .roles.sb.js files
|
|
5
5
|
const allLocalSbComponentsSchemaFiles = await discoverRoles({
|
|
6
6
|
scope: SCOPE.local,
|
|
@@ -17,9 +17,9 @@ export const syncAllRoles = async (config) => {
|
|
|
17
17
|
external: allExternalSbComponentsSchemaFiles,
|
|
18
18
|
});
|
|
19
19
|
// #4: sync - do all stuff already done (groups resolving, and so on)
|
|
20
|
-
await managementApi.roles.syncRoles({ specifiedRoles: [...local, ...external] }, config);
|
|
20
|
+
await managementApi.roles.syncRoles({ specifiedRoles: [...local, ...external], dryRun: options.dryRun }, config);
|
|
21
21
|
};
|
|
22
|
-
export const syncProvidedRoles = async ({ roles }, config) => {
|
|
22
|
+
export const syncProvidedRoles = async ({ roles, dryRun }, config) => {
|
|
23
23
|
// #1: discover all external .sb.js files
|
|
24
24
|
const allLocalSbComponentsSchemaFiles = await discoverManyRoles({
|
|
25
25
|
scope: SCOPE.local,
|
|
@@ -38,5 +38,5 @@ export const syncProvidedRoles = async ({ roles }, config) => {
|
|
|
38
38
|
external: allExternalSbComponentsSchemaFiles,
|
|
39
39
|
});
|
|
40
40
|
// #4: sync - do all stuff already done (groups resolving, and so on)
|
|
41
|
-
await managementApi.roles.syncRoles({ specifiedRoles: [...local, ...external] }, config);
|
|
41
|
+
await managementApi.roles.syncRoles({ specifiedRoles: [...local, ...external], dryRun }, config);
|
|
42
42
|
};
|