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.
Files changed (36) hide show
  1. package/dist/api/components/components.sync.d.ts +1 -0
  2. package/dist/api/components/components.sync.js +50 -8
  3. package/dist/api/datasources/datasource-entries.js +31 -15
  4. package/dist/api/datasources/datasources.d.ts +2 -1
  5. package/dist/api/datasources/datasources.js +33 -3
  6. package/dist/api/datasources/datasources.sync.js +2 -2
  7. package/dist/api/datasources/datasources.types.d.ts +6 -1
  8. package/dist/api/datasources/error-formatting.d.ts +1 -0
  9. package/dist/api/datasources/error-formatting.js +24 -0
  10. package/dist/api/migrate.js +50 -12
  11. package/dist/api/migrate.types.d.ts +14 -6
  12. package/dist/api/plugins/plugins.d.ts +2 -1
  13. package/dist/api/plugins/plugins.js +19 -1
  14. package/dist/api/plugins/plugins.sync.js +8 -4
  15. package/dist/api/plugins/plugins.types.d.ts +1 -0
  16. package/dist/api/roles/roles.d.ts +2 -1
  17. package/dist/api/roles/roles.js +16 -3
  18. package/dist/api/roles/roles.sync.js +2 -2
  19. package/dist/api/roles/roles.types.d.ts +6 -3
  20. package/dist/api/stories/stories.js +29 -1
  21. package/dist/api/sync/sync.types.d.ts +3 -0
  22. package/dist/api-v2/sync/index.js +4 -89
  23. package/dist/cli/cli-descriptions.d.ts +1 -1
  24. package/dist/cli/cli-descriptions.js +4 -0
  25. package/dist/cli/commands/sync.js +109 -64
  26. package/dist/cli/datasources/sync.js +5 -2
  27. package/dist/cli/roles/sync.js +4 -4
  28. package/dist-cjs/api/components/components.sync.js +50 -8
  29. package/dist-cjs/api/datasources/datasource-entries.js +31 -15
  30. package/dist-cjs/api/datasources/datasources.js +33 -3
  31. package/dist-cjs/api/datasources/error-formatting.js +28 -0
  32. package/dist-cjs/api/plugins/plugins.js +19 -1
  33. package/dist-cjs/api/roles/roles.js +16 -3
  34. package/dist-cjs/api/stories/stories.js +29 -1
  35. package/dist-cjs/api-v2/sync/index.js +4 -89
  36. package/package.json +1 -1
@@ -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 = await Promise.allSettled(rolesToUpdate.map((role) => updateRole(role, config)));
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 = await Promise.allSettled(rolesToCreate.map((role) => createRole(role, config)));
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
- console.error(err);
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
  });
@@ -8,6 +8,9 @@ export interface SyncResult {
8
8
  skipped: string[];
9
9
  errors: SyncError[];
10
10
  }
11
+ export interface SyncOptions {
12
+ dryRun?: boolean;
13
+ }
11
14
  /**
12
15
  * Progress event emitted during sync operations
13
16
  */
@@ -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
- if (args.dryRun) {
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
- if (args.dryRun) {
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
- if (args.dryRun) {
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
- await setComponentDefaultPreset({
46
- presets: Boolean(flags.presets),
47
- apiConfig,
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
- await setComponentDefaultPreset({
55
- presets: Boolean(flags.presets),
56
- componentsToSync,
57
- apiConfig,
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
- 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 () => {
64
- await removeAllComponents(apiConfig);
65
- await syncAllComponents(presets, apiConfig);
66
- }, () => {
67
- Logger.success("Syncing components aborted.");
68
- }, flags["yes"]);
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
- await askForConfirmation("Are you sure you want to delete all content (stories) in your space and then apply new ones ?", async () => {
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
- Logger.success("Stories not deleted, exiting the program...");
139
- }, flags["yes"]);
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
- await askForConfirmation("Are you sure you want to delete all stories in your space and then apply new ones ?", async () => {
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
- Logger.success("Stories not deleted, exiting the program...");
191
- }, flags["yes"]);
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
  };
@@ -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
  };