windmill-cli 1.518.2 → 1.519.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 (204) hide show
  1. package/esm/gen/core/OpenAPI.js +1 -1
  2. package/esm/gen/services.gen.js +74 -2
  3. package/esm/{apps.js → src/commands/app/apps.js} +6 -6
  4. package/esm/{dev.js → src/commands/dev/dev.js} +11 -11
  5. package/esm/{flow.js → src/commands/flow/flow.js} +13 -13
  6. package/esm/{folder.js → src/commands/folder/folder.js} +6 -6
  7. package/esm/src/commands/gitsync-settings/converter.js +134 -0
  8. package/esm/src/commands/gitsync-settings/gitsync-settings.js +2 -0
  9. package/esm/src/commands/gitsync-settings/index.js +28 -0
  10. package/esm/src/commands/gitsync-settings/legacySettings.js +119 -0
  11. package/esm/src/commands/gitsync-settings/pull.js +372 -0
  12. package/esm/src/commands/gitsync-settings/push.js +263 -0
  13. package/esm/src/commands/gitsync-settings/types.js +37 -0
  14. package/esm/src/commands/gitsync-settings/utils.js +129 -0
  15. package/esm/{hub.js → src/commands/hub/hub.js} +6 -6
  16. package/esm/src/commands/init/init.js +214 -0
  17. package/esm/{instance.js → src/commands/instance/instance.js} +18 -18
  18. package/esm/{queues.js → src/commands/queues/queues.js} +4 -4
  19. package/esm/{resource.js → src/commands/resource/resource.js} +7 -7
  20. package/esm/{resource-type.js → src/commands/resource-type/resource-type.js} +6 -6
  21. package/esm/{schedule.js → src/commands/schedule/schedule.js} +6 -6
  22. package/esm/{script.js → src/commands/script/script.js} +14 -14
  23. package/esm/{pull.js → src/commands/sync/pull.js} +2 -2
  24. package/esm/{sync.js → src/commands/sync/sync.js} +47 -146
  25. package/esm/{trigger.js → src/commands/trigger/trigger.js} +6 -6
  26. package/esm/{user.js → src/commands/user/user.js} +6 -6
  27. package/esm/{variable.js → src/commands/variable/variable.js} +6 -6
  28. package/esm/{worker_groups.js → src/commands/worker-groups/worker_groups.js} +5 -5
  29. package/esm/{workers.js → src/commands/workers/workers.js} +4 -4
  30. package/esm/{workspace.js → src/commands/workspace/workspace.js} +78 -44
  31. package/esm/{auth.js → src/core/auth.js} +3 -3
  32. package/esm/src/core/branch-profiles.js +46 -0
  33. package/esm/src/core/conf.js +162 -0
  34. package/esm/src/core/context.js +263 -0
  35. package/esm/{login.js → src/core/login.js} +2 -2
  36. package/esm/{settings.js → src/core/settings.js} +8 -8
  37. package/esm/src/core/store.js +19 -0
  38. package/esm/src/main.js +174 -0
  39. package/esm/{types.js → src/types.js} +16 -16
  40. package/esm/{codebase.js → src/utils/codebase.js} +1 -1
  41. package/esm/src/utils/git.js +29 -0
  42. package/esm/{metadata.js → src/utils/metadata.js} +31 -31
  43. package/esm/{upgrade.js → src/utils/upgrade.js} +1 -1
  44. package/esm/{utils.js → src/utils/utils.js} +11 -3
  45. package/esm/windmill-utils-internal/src/config/config.js +190 -0
  46. package/esm/windmill-utils-internal/src/inline-scripts/extractor.js +13 -9
  47. package/esm/windmill-utils-internal/src/path-utils/path-assigner.js +25 -9
  48. package/package.json +6 -6
  49. package/types/gen/services.gen.d.ts +37 -1
  50. package/types/gen/services.gen.d.ts.map +1 -1
  51. package/types/gen/types.gen.d.ts +48 -3
  52. package/types/gen/types.gen.d.ts.map +1 -1
  53. package/types/src/commands/app/apps.d.ts +17 -0
  54. package/types/src/commands/app/apps.d.ts.map +1 -0
  55. package/types/src/commands/dev/dev.d.ts +12 -0
  56. package/types/src/commands/dev/dev.d.ts.map +1 -0
  57. package/types/{flow.d.ts → src/commands/flow/flow.d.ts} +5 -5
  58. package/types/src/commands/flow/flow.d.ts.map +1 -0
  59. package/types/{folder.d.ts → src/commands/folder/folder.d.ts} +3 -3
  60. package/types/src/commands/folder/folder.d.ts.map +1 -0
  61. package/types/src/commands/gitsync-settings/converter.d.ts +10 -0
  62. package/types/src/commands/gitsync-settings/converter.d.ts.map +1 -0
  63. package/types/src/commands/gitsync-settings/gitsync-settings.d.ts +3 -0
  64. package/types/src/commands/gitsync-settings/gitsync-settings.d.ts.map +1 -0
  65. package/types/src/commands/gitsync-settings/index.d.ts +25 -0
  66. package/types/src/commands/gitsync-settings/index.d.ts.map +1 -0
  67. package/types/src/commands/gitsync-settings/legacySettings.d.ts +4 -0
  68. package/types/src/commands/gitsync-settings/legacySettings.d.ts.map +1 -0
  69. package/types/src/commands/gitsync-settings/pull.d.ts +14 -0
  70. package/types/src/commands/gitsync-settings/pull.d.ts.map +1 -0
  71. package/types/src/commands/gitsync-settings/push.d.ts +10 -0
  72. package/types/src/commands/gitsync-settings/push.d.ts.map +1 -0
  73. package/types/src/commands/gitsync-settings/types.d.ts +35 -0
  74. package/types/src/commands/gitsync-settings/types.d.ts.map +1 -0
  75. package/types/src/commands/gitsync-settings/utils.d.ts +36 -0
  76. package/types/src/commands/gitsync-settings/utils.d.ts.map +1 -0
  77. package/types/{hub.d.ts → src/commands/hub/hub.d.ts} +2 -2
  78. package/types/src/commands/hub/hub.d.ts.map +1 -0
  79. package/types/src/commands/init/init.d.ts +34 -0
  80. package/types/src/commands/init/init.d.ts.map +1 -0
  81. package/types/{instance.d.ts → src/commands/instance/instance.d.ts} +1 -1
  82. package/types/src/commands/instance/instance.d.ts.map +1 -0
  83. package/types/src/commands/queues/queues.d.ts +14 -0
  84. package/types/src/commands/queues/queues.d.ts.map +1 -0
  85. package/types/{resource.d.ts → src/commands/resource/resource.d.ts} +3 -3
  86. package/types/src/commands/resource/resource.d.ts.map +1 -0
  87. package/types/src/commands/resource-type/resource-type.d.ts +16 -0
  88. package/types/src/commands/resource-type/resource-type.d.ts.map +1 -0
  89. package/types/{schedule.d.ts → src/commands/schedule/schedule.d.ts} +3 -3
  90. package/types/src/commands/schedule/schedule.d.ts.map +1 -0
  91. package/types/{script.d.ts → src/commands/script/script.d.ts} +9 -9
  92. package/types/src/commands/script/script.d.ts.map +1 -0
  93. package/types/{pull.d.ts → src/commands/sync/pull.d.ts} +3 -3
  94. package/types/src/commands/sync/pull.d.ts.map +1 -0
  95. package/types/{sync.d.ts → src/commands/sync/sync.d.ts} +11 -13
  96. package/types/src/commands/sync/sync.d.ts.map +1 -0
  97. package/types/{trigger.d.ts → src/commands/trigger/trigger.d.ts} +3 -3
  98. package/types/src/commands/trigger/trigger.d.ts.map +1 -0
  99. package/types/{user.d.ts → src/commands/user/user.d.ts} +5 -5
  100. package/types/src/commands/user/user.d.ts.map +1 -0
  101. package/types/{variable.d.ts → src/commands/variable/variable.d.ts} +3 -3
  102. package/types/src/commands/variable/variable.d.ts.map +1 -0
  103. package/types/{worker_groups.d.ts → src/commands/worker-groups/worker_groups.d.ts} +2 -3
  104. package/types/src/commands/worker-groups/worker_groups.d.ts.map +1 -0
  105. package/types/{workers.d.ts → src/commands/workers/workers.d.ts} +1 -1
  106. package/types/src/commands/workers/workers.d.ts.map +1 -0
  107. package/types/{workspace.d.ts → src/commands/workspace/workspace.d.ts} +6 -4
  108. package/types/src/commands/workspace/workspace.d.ts.map +1 -0
  109. package/types/{auth.d.ts → src/core/auth.d.ts} +2 -2
  110. package/types/src/core/auth.d.ts.map +1 -0
  111. package/types/src/core/branch-profiles.d.ts +12 -0
  112. package/types/src/core/branch-profiles.d.ts.map +1 -0
  113. package/types/{conf.d.ts → src/core/conf.d.ts} +12 -3
  114. package/types/src/core/conf.d.ts.map +1 -0
  115. package/types/{context.d.ts → src/core/context.d.ts} +2 -2
  116. package/types/src/core/context.d.ts.map +1 -0
  117. package/types/{login.d.ts → src/core/login.d.ts} +1 -1
  118. package/types/src/core/login.d.ts.map +1 -0
  119. package/types/{settings.d.ts → src/core/settings.d.ts} +2 -2
  120. package/types/src/core/settings.d.ts.map +1 -0
  121. package/types/src/core/store.d.ts +2 -0
  122. package/types/src/core/store.d.ts.map +1 -0
  123. package/types/src/guidance/flow_guidance.d.ts.map +1 -0
  124. package/types/src/guidance/script_guidance.d.ts.map +1 -0
  125. package/types/src/main.d.ts +74 -0
  126. package/types/src/main.d.ts.map +1 -0
  127. package/types/src/types.d.ts.map +1 -0
  128. package/types/{codebase.d.ts → src/utils/codebase.d.ts} +1 -1
  129. package/types/src/utils/codebase.d.ts.map +1 -0
  130. package/types/src/utils/git.d.ts +3 -0
  131. package/types/src/utils/git.d.ts.map +1 -0
  132. package/types/src/utils/local_encryption.d.ts.map +1 -0
  133. package/types/{metadata.d.ts → src/utils/metadata.d.ts} +4 -4
  134. package/types/src/utils/metadata.d.ts.map +1 -0
  135. package/types/src/utils/script_common.d.ts.map +1 -0
  136. package/types/{upgrade.d.ts → src/utils/upgrade.d.ts} +1 -1
  137. package/types/src/utils/upgrade.d.ts.map +1 -0
  138. package/types/{utils.d.ts → src/utils/utils.d.ts} +2 -1
  139. package/types/src/utils/utils.d.ts.map +1 -0
  140. package/types/windmill-utils-internal/src/config/config.d.ts +12 -0
  141. package/types/windmill-utils-internal/src/config/config.d.ts.map +1 -0
  142. package/types/windmill-utils-internal/src/gen/types.gen.d.ts +48 -3
  143. package/types/windmill-utils-internal/src/gen/types.gen.d.ts.map +1 -1
  144. package/types/windmill-utils-internal/src/inline-scripts/extractor.d.ts +1 -4
  145. package/types/windmill-utils-internal/src/inline-scripts/extractor.d.ts.map +1 -1
  146. package/types/windmill-utils-internal/src/path-utils/path-assigner.d.ts +6 -6
  147. package/types/windmill-utils-internal/src/path-utils/path-assigner.d.ts.map +1 -1
  148. package/esm/conf.js +0 -78
  149. package/esm/context.js +0 -121
  150. package/esm/gitsync-settings.js +0 -984
  151. package/esm/main.js +0 -337
  152. package/esm/store.js +0 -76
  153. package/types/apps.d.ts +0 -17
  154. package/types/apps.d.ts.map +0 -1
  155. package/types/auth.d.ts.map +0 -1
  156. package/types/codebase.d.ts.map +0 -1
  157. package/types/conf.d.ts.map +0 -1
  158. package/types/context.d.ts.map +0 -1
  159. package/types/dev.d.ts +0 -12
  160. package/types/dev.d.ts.map +0 -1
  161. package/types/flow.d.ts.map +0 -1
  162. package/types/flow_guidance.d.ts.map +0 -1
  163. package/types/folder.d.ts.map +0 -1
  164. package/types/gitsync-settings.d.ts +0 -40
  165. package/types/gitsync-settings.d.ts.map +0 -1
  166. package/types/hub.d.ts.map +0 -1
  167. package/types/instance.d.ts.map +0 -1
  168. package/types/local_encryption.d.ts.map +0 -1
  169. package/types/login.d.ts.map +0 -1
  170. package/types/main.d.ts +0 -70
  171. package/types/main.d.ts.map +0 -1
  172. package/types/metadata.d.ts.map +0 -1
  173. package/types/pull.d.ts.map +0 -1
  174. package/types/queues.d.ts +0 -14
  175. package/types/queues.d.ts.map +0 -1
  176. package/types/resource-type.d.ts +0 -16
  177. package/types/resource-type.d.ts.map +0 -1
  178. package/types/resource.d.ts.map +0 -1
  179. package/types/schedule.d.ts.map +0 -1
  180. package/types/script.d.ts.map +0 -1
  181. package/types/script_common.d.ts.map +0 -1
  182. package/types/script_guidance.d.ts.map +0 -1
  183. package/types/settings.d.ts.map +0 -1
  184. package/types/store.d.ts +0 -3
  185. package/types/store.d.ts.map +0 -1
  186. package/types/sync.d.ts.map +0 -1
  187. package/types/trigger.d.ts.map +0 -1
  188. package/types/types.d.ts.map +0 -1
  189. package/types/upgrade.d.ts.map +0 -1
  190. package/types/user.d.ts.map +0 -1
  191. package/types/utils.d.ts.map +0 -1
  192. package/types/variable.d.ts.map +0 -1
  193. package/types/worker_groups.d.ts.map +0 -1
  194. package/types/workers.d.ts.map +0 -1
  195. package/types/workspace.d.ts.map +0 -1
  196. /package/esm/{flow_guidance.js → src/guidance/flow_guidance.js} +0 -0
  197. /package/esm/{script_guidance.js → src/guidance/script_guidance.js} +0 -0
  198. /package/esm/{local_encryption.js → src/utils/local_encryption.js} +0 -0
  199. /package/esm/{script_common.js → src/utils/script_common.js} +0 -0
  200. /package/types/{flow_guidance.d.ts → src/guidance/flow_guidance.d.ts} +0 -0
  201. /package/types/{script_guidance.d.ts → src/guidance/script_guidance.d.ts} +0 -0
  202. /package/types/{types.d.ts → src/types.d.ts} +0 -0
  203. /package/types/{local_encryption.d.ts → src/utils/local_encryption.d.ts} +0 -0
  204. /package/types/{script_common.d.ts → src/utils/script_common.d.ts} +0 -0
@@ -1,984 +0,0 @@
1
- import * as dntShim from "./_dnt.shims.js";
2
- import { colors, Command, Confirm, log, yamlStringify } from "./deps.js";
3
- import { requireLogin } from "./auth.js";
4
- import { resolveWorkspace } from "./context.js";
5
- import * as wmill from "./gen/services.gen.js";
6
- import { DEFAULT_SYNC_OPTIONS, getEffectiveSettings, readConfigFile, } from "./conf.js";
7
- import { deepEqual, selectRepository } from "./utils.js";
8
- // Constants for git-sync fields to avoid duplication
9
- const GIT_SYNC_FIELDS = [
10
- "includes",
11
- "excludes",
12
- "extraIncludes",
13
- "skipScripts",
14
- "skipFlows",
15
- "skipApps",
16
- "skipFolders",
17
- "skipVariables",
18
- "skipResources",
19
- "skipResourceTypes",
20
- "skipSecrets",
21
- "includeSchedules",
22
- "includeTriggers",
23
- "includeUsers",
24
- "includeGroups",
25
- "includeSettings",
26
- "includeKey",
27
- ];
28
- // Helper to normalize repository path by removing $res: prefix
29
- function normalizeRepoPath(path) {
30
- return path.replace(/^\$res:/, "");
31
- }
32
- // Helper to get typed field value from SyncOptions
33
- function getFieldValue(opts, field) {
34
- return opts[field];
35
- }
36
- // Construct override key using the single format: baseUrl:workspaceId:repo
37
- function constructOverrideKey(baseUrl, workspaceId, repoPath, workspaceLevel = false) {
38
- // Validate that components don't contain colons to avoid key collisions
39
- if (baseUrl.includes(':') && !baseUrl.startsWith('http')) {
40
- throw new Error(`Invalid baseUrl contains colon: ${baseUrl}`);
41
- }
42
- if (workspaceId.includes(':')) {
43
- throw new Error(`Invalid workspaceId contains colon: ${workspaceId}`);
44
- }
45
- if (repoPath.includes(':') && !repoPath.startsWith('$res:')) {
46
- throw new Error(`Invalid repoPath contains colon: ${repoPath}`);
47
- }
48
- if (workspaceLevel) {
49
- return `${baseUrl}:${workspaceId}:*`;
50
- }
51
- return `${baseUrl}:${workspaceId}:${repoPath}`;
52
- }
53
- // Helper to compare string arrays (used for includes/excludes/extraIncludes)
54
- function arraysEqual(arr1, arr2) {
55
- if (arr1.length !== arr2.length) {
56
- return false;
57
- }
58
- const sorted1 = [...arr1].sort();
59
- const sorted2 = [...arr2].sort();
60
- return sorted1.every((item, index) => item === sorted2[index]);
61
- }
62
- // Normalize SyncOptions for semantic comparison - treat undefined arrays as empty arrays
63
- function normalizeSyncOptions(opts) {
64
- return {
65
- ...opts,
66
- includes: opts.includes || [],
67
- excludes: opts.excludes || [],
68
- extraIncludes: opts.extraIncludes || [],
69
- };
70
- }
71
- // Extract only git-sync relevant fields for comparison
72
- function extractGitSyncFields(opts) {
73
- return {
74
- includes: opts.includes || [],
75
- excludes: opts.excludes || [],
76
- extraIncludes: opts.extraIncludes || [],
77
- skipScripts: opts.skipScripts,
78
- skipFlows: opts.skipFlows,
79
- skipApps: opts.skipApps,
80
- skipFolders: opts.skipFolders,
81
- skipVariables: opts.skipVariables,
82
- skipResources: opts.skipResources,
83
- skipResourceTypes: opts.skipResourceTypes,
84
- skipSecrets: opts.skipSecrets,
85
- includeSchedules: opts.includeSchedules,
86
- includeTriggers: opts.includeTriggers,
87
- includeUsers: opts.includeUsers,
88
- includeGroups: opts.includeGroups,
89
- includeSettings: opts.includeSettings,
90
- includeKey: opts.includeKey,
91
- };
92
- }
93
- // Helper function to determine current settings based on write mode and conflicts
94
- function getCurrentSettings(localConfig, writeMode, overrideKey) {
95
- if (writeMode === "override" &&
96
- overrideKey &&
97
- localConfig.overrides?.[overrideKey]) {
98
- return {
99
- ...DEFAULT_SYNC_OPTIONS,
100
- ...localConfig,
101
- ...localConfig.overrides[overrideKey],
102
- };
103
- }
104
- else {
105
- // For "replace" mode, exclude overrides since they're never accessed
106
- const { overrides, ...configWithoutOverrides } = localConfig;
107
- return { ...DEFAULT_SYNC_OPTIONS, ...configWithoutOverrides };
108
- }
109
- }
110
- // Convert backend include_type array to SyncOptions boolean flags
111
- function includeTypeToSyncOptions(includeTypes) {
112
- return {
113
- skipScripts: !includeTypes.includes("script"),
114
- skipFlows: !includeTypes.includes("flow"),
115
- skipApps: !includeTypes.includes("app"),
116
- skipFolders: !includeTypes.includes("folder"),
117
- skipVariables: !includeTypes.includes("variable"),
118
- skipResources: !includeTypes.includes("resource"),
119
- skipResourceTypes: !includeTypes.includes("resourcetype"),
120
- skipSecrets: !includeTypes.includes("secret"),
121
- includeSchedules: includeTypes.includes("schedule"),
122
- includeTriggers: includeTypes.includes("trigger"),
123
- includeUsers: includeTypes.includes("user"),
124
- includeGroups: includeTypes.includes("group"),
125
- includeSettings: includeTypes.includes("settings"),
126
- includeKey: includeTypes.includes("key"),
127
- };
128
- }
129
- // Shared migration function for legacy repositories
130
- async function handleLegacyRepositoryMigration(selectedRepo, gitSyncSettings, workspace, opts, operationName = "operation") {
131
- if (selectedRepo.settings) {
132
- return selectedRepo; // Already migrated
133
- }
134
- // This repository is in legacy format - handle migration
135
- if (!gitSyncSettings.include_path || !gitSyncSettings.include_type) {
136
- throw new Error(`Repository "${selectedRepo.git_repo_resource_path}" has legacy format but workspace-level include_path or include_type is missing. This indicates corrupted git-sync settings.`);
137
- }
138
- const workspaceIncludePath = gitSyncSettings.include_path;
139
- const workspaceIncludeType = gitSyncSettings.include_type;
140
- if (dntShim.Deno.stdout.isTerminal() && !opts.yes) {
141
- // Interactive mode - show migration prompt
142
- console.log(colors.yellow('\n⚠️ Legacy git-sync settings detected!'));
143
- console.log(`\nRepository "${selectedRepo.git_repo_resource_path}" has legacy settings format.`);
144
- console.log('The new format allows per-repository filter configuration.');
145
- if (operationName === "push") {
146
- console.log('This repository must be migrated before pushing settings.\n');
147
- }
148
- else {
149
- console.log('\n');
150
- }
151
- console.log(colors.bold('Current workspace-level settings:'));
152
- console.log(` Include paths: ${workspaceIncludePath.join(', ')}`);
153
- console.log(` Include types: ${workspaceIncludeType.join(', ')}\n`);
154
- // Show what the migration will do
155
- let finalIncludeType = [...workspaceIncludeType];
156
- if (selectedRepo.exclude_types_override && selectedRepo.exclude_types_override.length > 0) {
157
- const originalCount = finalIncludeType.length;
158
- finalIncludeType = finalIncludeType.filter(type => !selectedRepo.exclude_types_override.includes(type));
159
- const excludedCount = originalCount - finalIncludeType.length;
160
- console.log(colors.yellow(`Repository excludes ${excludedCount} types: ${selectedRepo.exclude_types_override.join(', ')}`));
161
- }
162
- console.log(colors.bold('\nAfter migration, repository will have:'));
163
- console.log(` Include paths: ${workspaceIncludePath.join(', ')}`);
164
- console.log(` Include types: ${finalIncludeType.join(', ')}\n`);
165
- const confirm = await Confirm.prompt({
166
- message: operationName === "push"
167
- ? 'Do you want to migrate this repository before pushing?'
168
- : 'Do you want to migrate this repository?',
169
- default: true
170
- });
171
- if (!confirm) {
172
- const message = operationName === "push"
173
- ? '\n⚠️ Migration skipped. Cannot push to legacy repository.'
174
- : '\n⚠️ Migration skipped. You can migrate later via the UI.';
175
- console.log(colors.yellow(message));
176
- if (operationName === "push") {
177
- return null; // Signal to exit push operation
178
- }
179
- throw new Error('Migration cancelled by user');
180
- }
181
- // Perform the migration
182
- let migratedIncludeType = [...workspaceIncludeType];
183
- if (selectedRepo.exclude_types_override && selectedRepo.exclude_types_override.length > 0) {
184
- migratedIncludeType = migratedIncludeType.filter(type => !selectedRepo.exclude_types_override.includes(type));
185
- }
186
- const migratedRepo = {
187
- ...selectedRepo,
188
- settings: {
189
- include_path: [...workspaceIncludePath],
190
- include_type: migratedIncludeType,
191
- exclude_path: [],
192
- extra_include_path: []
193
- }
194
- };
195
- // Remove the old field
196
- delete migratedRepo.exclude_types_override;
197
- // Update the backend with migrated repository
198
- const updatedRepositories = gitSyncSettings.repositories.map((repo) => {
199
- if (repo.git_repo_resource_path === selectedRepo.git_repo_resource_path) {
200
- return migratedRepo;
201
- }
202
- return repo;
203
- });
204
- await wmill.editWorkspaceGitSyncConfig({
205
- workspace: workspace.workspaceId,
206
- requestBody: {
207
- git_sync_settings: {
208
- repositories: updatedRepositories,
209
- // Keep workspace-level settings if other repos are still legacy
210
- ...(gitSyncSettings.repositories.some((r) => r.git_repo_resource_path !== selectedRepo.git_repo_resource_path && !r.settings) && {
211
- include_path: workspaceIncludePath,
212
- include_type: workspaceIncludeType
213
- })
214
- }
215
- }
216
- });
217
- console.log(colors.green('\n✓ Repository migration completed successfully!'));
218
- if (operationName === "push") {
219
- console.log('Now proceeding with push operation...\n');
220
- }
221
- return migratedRepo;
222
- }
223
- else {
224
- // Non-interactive mode - show error
225
- console.error(colors.red('\n❌ Legacy git-sync settings detected!'));
226
- console.error(`\nRepository "${selectedRepo.git_repo_resource_path}" has legacy settings format.`);
227
- if (operationName === "push") {
228
- console.error('This repository must be migrated before pushing settings.');
229
- }
230
- console.error('Please choose one of the following options:\n');
231
- console.error('1. Go to the Windmill UI > Workspace Settings > Git Sync');
232
- console.error(' Review and save this repository to migrate to the new format.\n');
233
- console.error('2. Run this command in interactive mode (with TTY) to migrate.');
234
- console.error(` Example: wmill gitsync-settings ${operationName}\n`);
235
- if (operationName === "push") {
236
- console.error('3. Pull settings first to migrate: wmill gitsync-settings pull\n');
237
- }
238
- else {
239
- console.error('3. Push local settings to override backend settings:');
240
- console.error(' wmill gitsync-settings push\n');
241
- }
242
- dntShim.Deno.exit(1);
243
- }
244
- }
245
- // Convert SyncOptions boolean flags to backend include_type array
246
- function syncOptionsToIncludeType(opts) {
247
- const includeTypes = [];
248
- if (!opts.skipScripts)
249
- includeTypes.push("script");
250
- if (!opts.skipFlows)
251
- includeTypes.push("flow");
252
- if (!opts.skipApps)
253
- includeTypes.push("app");
254
- if (!opts.skipFolders)
255
- includeTypes.push("folder");
256
- if (!opts.skipVariables)
257
- includeTypes.push("variable");
258
- if (!opts.skipResources)
259
- includeTypes.push("resource");
260
- if (!opts.skipResourceTypes)
261
- includeTypes.push("resourcetype");
262
- if (!opts.skipSecrets)
263
- includeTypes.push("secret");
264
- if (opts.includeSchedules)
265
- includeTypes.push("schedule");
266
- if (opts.includeTriggers)
267
- includeTypes.push("trigger");
268
- if (opts.includeUsers)
269
- includeTypes.push("user");
270
- if (opts.includeGroups)
271
- includeTypes.push("group");
272
- if (opts.includeSettings)
273
- includeTypes.push("settings");
274
- if (opts.includeKey)
275
- includeTypes.push("key");
276
- return includeTypes;
277
- }
278
- // Convert SyncOptions to backend format used by both Windmill backend and UI
279
- function syncOptionsToBackendFormat(opts) {
280
- return {
281
- include_path: opts.includes || [],
282
- exclude_path: opts.excludes || [],
283
- extra_include_path: opts.extraIncludes || [],
284
- include_type: syncOptionsToIncludeType(opts),
285
- };
286
- }
287
- // Select repository interactively if multiple exist
288
- // Generate structured diff showing field changes
289
- function generateStructuredDiff(current, backend) {
290
- const diff = {};
291
- // Get all unique keys from both objects
292
- const allKeys = new Set([...Object.keys(current), ...Object.keys(backend)]);
293
- for (const key of allKeys) {
294
- const currentValue = current[key];
295
- const backendValue = backend[key];
296
- if (!deepEqual(currentValue, backendValue)) {
297
- diff[key] = {
298
- from: currentValue,
299
- to: backendValue,
300
- };
301
- }
302
- }
303
- return diff;
304
- }
305
- // Helper to generate changes between two normalized SyncOptions objects
306
- function generateChanges(normalizedCurrent, normalizedNew) {
307
- const changes = {};
308
- for (const field of GIT_SYNC_FIELDS) {
309
- const currentValue = getFieldValue(normalizedCurrent, field);
310
- const newValue = getFieldValue(normalizedNew, field);
311
- if (!deepEqual(currentValue, newValue)) {
312
- changes[field] = {
313
- from: currentValue,
314
- to: newValue,
315
- };
316
- }
317
- }
318
- return changes;
319
- }
320
- // Helper to display changes in human-readable format
321
- function displayChanges(changes) {
322
- for (const [field, change] of Object.entries(changes)) {
323
- if (Array.isArray(change.from) ||
324
- Array.isArray(change.to)) {
325
- console.log(colors.yellow(` ${field}:`));
326
- console.log(colors.red(` - ${JSON.stringify(change.from)}`));
327
- console.log(colors.green(` + ${JSON.stringify(change.to)}`));
328
- }
329
- else {
330
- console.log(colors.yellow(` ${field}: `) +
331
- colors.red(`${change.from}`) +
332
- " → " +
333
- colors.green(`${change.to}`));
334
- }
335
- }
336
- }
337
- async function selectAndLogRepository(repositories, repository) {
338
- let selectedRepo;
339
- if (repository) {
340
- const found = repositories.find((r) => r.git_repo_resource_path === repository ||
341
- r.git_repo_resource_path === `$res:${repository}`);
342
- if (!found) {
343
- throw new Error(`Repository ${repository} not found`);
344
- }
345
- selectedRepo = found;
346
- const repoPath = selectedRepo.git_repo_resource_path.replace(/^\$res:/, "");
347
- log.info(colors.cyan(`Using repository: ${colors.bold(repoPath)}`));
348
- }
349
- else {
350
- selectedRepo = await selectRepository(repositories);
351
- }
352
- return selectedRepo;
353
- }
354
- async function pullGitSyncSettings(opts) {
355
- const workspace = await resolveWorkspace(opts);
356
- await requireLogin(opts);
357
- try {
358
- // Parse and validate --with-backend-settings if provided
359
- let settings;
360
- if (opts.withBackendSettings) {
361
- try {
362
- const parsedSettings = JSON.parse(opts.withBackendSettings);
363
- // Validate the structure matches expected test format (raw settings object)
364
- if (!parsedSettings.include_path ||
365
- !Array.isArray(parsedSettings.include_path)) {
366
- throw new Error("Invalid settings format. Expected include_path array");
367
- }
368
- if (!parsedSettings.include_type ||
369
- !Array.isArray(parsedSettings.include_type)) {
370
- throw new Error("Invalid settings format. Expected include_type array");
371
- }
372
- // Create mock backend response with single repository using provided settings
373
- const mockRepositoryPath = opts.repository || "u/mock/repo";
374
- settings = {
375
- git_sync: {
376
- repositories: [{
377
- git_repo_resource_path: mockRepositoryPath,
378
- settings: {
379
- include_path: parsedSettings.include_path,
380
- include_type: parsedSettings.include_type,
381
- exclude_path: parsedSettings.exclude_path || [],
382
- extra_include_path: parsedSettings.extra_include_path || [],
383
- },
384
- }],
385
- },
386
- };
387
- }
388
- catch (parseError) {
389
- const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
390
- if (opts.jsonOutput) {
391
- console.log(JSON.stringify({
392
- success: false,
393
- error: `Failed to parse --with-backend-settings JSON: ${errorMessage}`,
394
- }));
395
- }
396
- else {
397
- log.error(colors.red(`Failed to parse --with-backend-settings JSON: ${errorMessage}`));
398
- }
399
- return;
400
- }
401
- }
402
- else {
403
- // Fetch workspace settings to get git-sync configuration
404
- try {
405
- settings = await wmill.getSettings({
406
- workspace: workspace.workspaceId,
407
- });
408
- }
409
- catch (apiError) {
410
- const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
411
- if (opts.jsonOutput) {
412
- console.log(JSON.stringify({
413
- success: false,
414
- error: `Failed to fetch workspace settings: ${errorMessage}`,
415
- }));
416
- }
417
- else {
418
- log.error(colors.red(`Failed to fetch workspace settings: ${errorMessage}`));
419
- }
420
- return;
421
- }
422
- }
423
- if (!settings.git_sync?.repositories ||
424
- settings.git_sync.repositories.length === 0) {
425
- if (opts.jsonOutput) {
426
- console.log(JSON.stringify({
427
- success: false,
428
- error: "No git-sync repositories configured",
429
- }));
430
- }
431
- else {
432
- log.error(colors.red("No git-sync repositories configured in workspace"));
433
- }
434
- return;
435
- }
436
- // Find the repository to work with
437
- let selectedRepo = await selectAndLogRepository(settings.git_sync.repositories, opts.repository);
438
- // Check if the selected repository needs migration and handle it
439
- selectedRepo = await handleLegacyRepositoryMigration(selectedRepo, settings.git_sync, workspace, opts, "pull");
440
- // Convert backend settings to SyncOptions format
441
- const backendSyncOptions = {
442
- includes: selectedRepo.settings.include_path || [],
443
- excludes: selectedRepo.settings.exclude_path || [],
444
- extraIncludes: selectedRepo.settings.extra_include_path || [],
445
- ...includeTypeToSyncOptions(selectedRepo.settings.include_type || []),
446
- };
447
- // Check if wmill.yaml exists - require it for git-sync settings commands
448
- try {
449
- await dntShim.Deno.stat("wmill.yaml");
450
- }
451
- catch (error) {
452
- log.error(colors.red("No wmill.yaml file found. Please run 'wmill init' first to create the configuration file."));
453
- dntShim.Deno.exit(1);
454
- }
455
- // Read current local configuration
456
- const localConfig = await readConfigFile();
457
- // Determine where to write the settings for diff display
458
- let overrideKey;
459
- let writeMode = "replace";
460
- // For diff mode, determine what the write mode would be without interactive prompts
461
- if (opts.default) {
462
- writeMode = "replace";
463
- }
464
- else if (opts.replace) {
465
- writeMode = "replace";
466
- }
467
- else if (opts.override || opts.workspaceLevel) {
468
- writeMode = "override";
469
- if (opts.workspaceLevel) {
470
- overrideKey = constructOverrideKey(workspace.remote, workspace.workspaceId, "", true);
471
- }
472
- else {
473
- const repoPath = normalizeRepoPath(selectedRepo.git_repo_resource_path);
474
- overrideKey = constructOverrideKey(workspace.remote, workspace.workspaceId, repoPath);
475
- }
476
- }
477
- else {
478
- // Default behavior for existing files with no explicit flags
479
- // Use same logic as diff to determine if there's a real conflict
480
- const currentSettings = getCurrentSettings(localConfig, "replace", // Check against replace mode
481
- undefined);
482
- const gitSyncBackend = extractGitSyncFields(normalizeSyncOptions(backendSyncOptions));
483
- const gitSyncCurrent = extractGitSyncFields(normalizeSyncOptions(currentSettings));
484
- const hasConflict = !deepEqual(gitSyncBackend, gitSyncCurrent);
485
- if (hasConflict) {
486
- // For diff mode, show what override would look like
487
- writeMode = "override";
488
- const repoPath = normalizeRepoPath(selectedRepo.git_repo_resource_path);
489
- overrideKey = constructOverrideKey(workspace.remote, workspace.workspaceId, repoPath);
490
- }
491
- else {
492
- writeMode = "replace";
493
- }
494
- }
495
- if (opts.diff) {
496
- // Show differences between local and backend
497
- const currentSettings = getCurrentSettings(localConfig, writeMode, overrideKey);
498
- const normalizedCurrent = normalizeSyncOptions(currentSettings);
499
- const normalizedBackend = normalizeSyncOptions(backendSyncOptions);
500
- const gitSyncCurrent = extractGitSyncFields(normalizedCurrent);
501
- const gitSyncBackend = extractGitSyncFields(normalizedBackend);
502
- const hasChanges = !deepEqual(gitSyncBackend, gitSyncCurrent);
503
- if (opts.jsonOutput) {
504
- const repoPath = normalizeRepoPath(selectedRepo.git_repo_resource_path);
505
- // Generate structured diff using the same normalized objects
506
- const structuredDiff = hasChanges
507
- ? generateStructuredDiff(gitSyncCurrent, gitSyncBackend)
508
- : {};
509
- console.log(JSON.stringify({
510
- success: true,
511
- hasChanges,
512
- local: syncOptionsToBackendFormat(normalizedCurrent),
513
- backend: syncOptionsToBackendFormat(normalizedBackend),
514
- repository: selectedRepo.git_repo_resource_path,
515
- diff: structuredDiff,
516
- }));
517
- }
518
- else {
519
- if (hasChanges) {
520
- log.info("Changes that would be applied locally:");
521
- const changes = generateChanges(normalizedCurrent, normalizedBackend);
522
- if (Object.keys(changes).length === 0) {
523
- log.info(colors.green("No differences found"));
524
- }
525
- else {
526
- displayChanges(changes);
527
- }
528
- }
529
- else {
530
- log.info(colors.green("No differences found"));
531
- }
532
- }
533
- return;
534
- }
535
- // For non-diff mode, handle interactive logic if needed
536
- // Only show interactive prompts for existing files with conflicts
537
- if (!opts.diff &&
538
- !opts.default &&
539
- !opts.replace &&
540
- !opts.override &&
541
- !opts.workspaceLevel) {
542
- // Use the same logic as diff to determine current settings
543
- const currentSettings = getCurrentSettings(localConfig, writeMode, overrideKey);
544
- const gitSyncBackend = extractGitSyncFields(normalizeSyncOptions(backendSyncOptions));
545
- const gitSyncCurrent = extractGitSyncFields(normalizeSyncOptions(currentSettings));
546
- const hasConflict = !deepEqual(gitSyncBackend, gitSyncCurrent);
547
- if (hasConflict && !opts.yes && dntShim.Deno.stdin.isTerminal()) {
548
- // Show the diff first
549
- log.info("Changes that would be applied locally:");
550
- const changes = generateChanges(currentSettings, backendSyncOptions);
551
- if (Object.keys(changes).length > 0) {
552
- displayChanges(changes);
553
- }
554
- // Interactive mode - ask user
555
- const { Select } = await import("./deps.js");
556
- const choice = await Select.prompt({
557
- message: "Settings conflict detected. How would you like to proceed?",
558
- options: [
559
- {
560
- name: "Replace existing settings",
561
- value: "replace",
562
- },
563
- {
564
- name: "Add repository-specific override",
565
- value: "override",
566
- },
567
- { name: "Cancel", value: "cancel" },
568
- ],
569
- });
570
- if (choice === "cancel") {
571
- log.info("Operation cancelled");
572
- return;
573
- }
574
- writeMode = choice;
575
- if (writeMode === "override") {
576
- const repoPath = normalizeRepoPath(selectedRepo.git_repo_resource_path);
577
- overrideKey = constructOverrideKey(workspace.remote, workspace.workspaceId, repoPath);
578
- }
579
- }
580
- else if (hasConflict && opts.yes) {
581
- // --yes flag: default to override behavior for conflicts
582
- writeMode = "override";
583
- const repoPath = normalizeRepoPath(selectedRepo.git_repo_resource_path);
584
- overrideKey = constructOverrideKey(workspace.remote, workspace.workspaceId, repoPath);
585
- log.info(colors.yellow("Settings conflict detected. Using --override behavior (default for --yes)."));
586
- }
587
- else if (hasConflict) {
588
- // Non-interactive mode with conflicts - show message and exit
589
- if (opts.jsonOutput) {
590
- console.log(JSON.stringify({
591
- success: false,
592
- error: "Settings conflict detected. Use --replace or --override flags to resolve.",
593
- hasConflict: true,
594
- }));
595
- }
596
- else {
597
- log.error(colors.red("Settings conflict detected."));
598
- log.info("Use --replace to overwrite existing settings or --override to add repository-specific override.");
599
- }
600
- return;
601
- }
602
- }
603
- // Check if there are actually any changes before writing
604
- const currentSettingsForCheck = getCurrentSettings(localConfig, writeMode, overrideKey);
605
- const gitSyncBackend = extractGitSyncFields(normalizeSyncOptions(backendSyncOptions));
606
- const gitSyncCurrent = extractGitSyncFields(normalizeSyncOptions(currentSettingsForCheck));
607
- const hasActualChanges = !deepEqual(gitSyncBackend, gitSyncCurrent);
608
- if (!hasActualChanges) {
609
- if (opts.jsonOutput) {
610
- console.log(JSON.stringify({
611
- success: true,
612
- message: "No changes needed",
613
- repository: selectedRepo.git_repo_resource_path,
614
- }));
615
- }
616
- else {
617
- log.info(colors.green("No changes needed - settings are already up to date"));
618
- }
619
- return;
620
- }
621
- // Apply the settings based on write mode
622
- let updatedConfig;
623
- // Log which settings mode is being used
624
- const repoPath = selectedRepo.git_repo_resource_path.replace(/^\$res:/, "");
625
- if (writeMode === "override") {
626
- log.info(`Applied repository-specific overrides for repository "${repoPath}"`);
627
- }
628
- else {
629
- log.info(`Applied settings for repository "${repoPath}"`);
630
- }
631
- if (writeMode === "replace") {
632
- // Preserve existing local config and update only git-sync fields
633
- updatedConfig = { ...localConfig };
634
- // Clear overrides since we're in replace mode, but keep empty object for consistency
635
- updatedConfig.overrides = {};
636
- // Update with backend git-sync settings
637
- Object.assign(updatedConfig, backendSyncOptions);
638
- }
639
- else if (writeMode === "override" && overrideKey) {
640
- // Add repository-specific override
641
- updatedConfig = { ...localConfig };
642
- if (!updatedConfig.overrides) {
643
- updatedConfig.overrides = {};
644
- }
645
- // Only store the delta - settings that differ from current effective settings
646
- const currentEffective = getCurrentSettings(localConfig, "replace");
647
- const deltaSettings = {};
648
- // Compare each setting and only include differences
649
- for (const [key, value] of Object.entries(backendSyncOptions)) {
650
- if (key === "overrides")
651
- continue; // Skip overrides field
652
- const currentValue = currentEffective[key];
653
- const newValue = value;
654
- // Compare arrays by content, primitives by value
655
- const isDifferent = Array.isArray(currentValue) && Array.isArray(newValue)
656
- ? !arraysEqual(currentValue, newValue)
657
- : currentValue !== newValue;
658
- if (isDifferent) {
659
- deltaSettings[key] = newValue;
660
- }
661
- }
662
- updatedConfig.overrides[overrideKey] = deltaSettings;
663
- }
664
- else {
665
- // Replace top-level settings
666
- updatedConfig = { ...localConfig };
667
- // Copy all backend settings to top level, excluding overrides
668
- const { overrides, ...topLevelSettings } = backendSyncOptions;
669
- Object.assign(updatedConfig, topLevelSettings);
670
- }
671
- // Write updated configuration
672
- await dntShim.Deno.writeTextFile("wmill.yaml", yamlStringify(updatedConfig));
673
- if (opts.jsonOutput) {
674
- console.log(JSON.stringify({
675
- success: true,
676
- message: `Git-sync settings pulled successfully`,
677
- repository: selectedRepo.git_repo_resource_path,
678
- overrideKey,
679
- }));
680
- }
681
- else {
682
- log.info(colors.green(`Git-sync settings pulled successfully from ${selectedRepo.git_repo_resource_path}`));
683
- if (writeMode === "override" && overrideKey) {
684
- log.info(colors.gray(`Settings written to override key: ${overrideKey}`));
685
- }
686
- else if (writeMode === "replace") {
687
- log.info(colors.gray(`Settings written as simple configuration`));
688
- }
689
- else {
690
- log.info(colors.gray(`Settings written to top-level defaults`));
691
- }
692
- }
693
- }
694
- catch (error) {
695
- const errorMessage = error instanceof Error ? error.message : String(error);
696
- if (opts.jsonOutput) {
697
- console.log(JSON.stringify({ success: false, error: errorMessage }));
698
- }
699
- else {
700
- log.error(colors.red(`Failed to pull git-sync settings: ${errorMessage}`));
701
- }
702
- }
703
- }
704
- async function pushGitSyncSettings(opts) {
705
- const workspace = await resolveWorkspace(opts);
706
- await requireLogin(opts);
707
- try {
708
- // Check if wmill.yaml exists - require it for git-sync settings commands
709
- try {
710
- await dntShim.Deno.stat("wmill.yaml");
711
- }
712
- catch (error) {
713
- log.error(colors.red("No wmill.yaml file found. Please run 'wmill init' first to create the configuration file."));
714
- dntShim.Deno.exit(1);
715
- }
716
- // Read local configuration
717
- const localConfig = await readConfigFile();
718
- // Parse and validate --with-backend-settings if provided, otherwise fetch from backend
719
- let settings;
720
- if (opts.withBackendSettings) {
721
- try {
722
- const parsedSettings = JSON.parse(opts.withBackendSettings);
723
- // Validate the structure matches expected test format (raw settings object)
724
- if (!parsedSettings.include_path ||
725
- !Array.isArray(parsedSettings.include_path)) {
726
- throw new Error("Invalid settings format. Expected include_path array");
727
- }
728
- if (!parsedSettings.include_type ||
729
- !Array.isArray(parsedSettings.include_type)) {
730
- throw new Error("Invalid settings format. Expected include_type array");
731
- }
732
- // Create mock backend response with single repository using provided settings
733
- const mockRepositoryPath = opts.repository || "u/mock/repo";
734
- settings = {
735
- git_sync: {
736
- repositories: [{
737
- git_repo_resource_path: mockRepositoryPath,
738
- settings: {
739
- include_path: parsedSettings.include_path,
740
- include_type: parsedSettings.include_type,
741
- exclude_path: parsedSettings.exclude_path || [],
742
- extra_include_path: parsedSettings.extra_include_path || [],
743
- },
744
- }],
745
- },
746
- };
747
- }
748
- catch (parseError) {
749
- const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
750
- if (opts.jsonOutput) {
751
- console.log(JSON.stringify({
752
- success: false,
753
- error: `Failed to parse --with-backend-settings JSON: ${errorMessage}`,
754
- }));
755
- }
756
- else {
757
- log.error(colors.red(`Failed to parse --with-backend-settings JSON: ${errorMessage}`));
758
- }
759
- return;
760
- }
761
- }
762
- else {
763
- // Fetch current backend settings
764
- try {
765
- settings = await wmill.getSettings({
766
- workspace: workspace.workspaceId,
767
- });
768
- }
769
- catch (apiError) {
770
- const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
771
- if (opts.jsonOutput) {
772
- console.log(JSON.stringify({
773
- success: false,
774
- error: `Failed to fetch workspace settings: ${errorMessage}`,
775
- }));
776
- }
777
- else {
778
- log.error(colors.red(`Failed to fetch workspace settings: ${errorMessage}`));
779
- }
780
- return;
781
- }
782
- }
783
- if (!settings.git_sync?.repositories ||
784
- settings.git_sync.repositories.length === 0) {
785
- if (opts.jsonOutput) {
786
- console.log(JSON.stringify({
787
- success: false,
788
- error: "No git-sync repositories configured",
789
- }));
790
- }
791
- else {
792
- log.error(colors.red("No git-sync repositories configured in workspace"));
793
- }
794
- return;
795
- }
796
- // Find the repository to work with
797
- let selectedRepo = await selectAndLogRepository(settings.git_sync.repositories, opts.repository);
798
- // Check if the selected repository needs migration and handle it
799
- selectedRepo = await handleLegacyRepositoryMigration(selectedRepo, settings.git_sync, workspace, opts, "push");
800
- // If migration was cancelled, exit
801
- if (selectedRepo === null) {
802
- return;
803
- }
804
- // Get effective settings for this workspace/repo
805
- const repoPath = normalizeRepoPath(selectedRepo.git_repo_resource_path);
806
- const effectiveSettings = getEffectiveSettings(localConfig, workspace.remote, workspace.workspaceId, repoPath);
807
- // Convert to backend format
808
- const backendFormat = {
809
- include_path: effectiveSettings.includes || [],
810
- include_type: syncOptionsToIncludeType(effectiveSettings),
811
- exclude_path: effectiveSettings.excludes || [],
812
- extra_include_path: effectiveSettings.extraIncludes || [],
813
- };
814
- // Calculate diff for all modes
815
- const currentBackend = selectedRepo.settings;
816
- // Convert current backend settings to SyncOptions for user-friendly display
817
- const currentSyncOptions = {
818
- includes: currentBackend.include_path || [],
819
- excludes: currentBackend.exclude_path || [],
820
- extraIncludes: currentBackend.extra_include_path || [],
821
- ...includeTypeToSyncOptions(currentBackend.include_type || []),
822
- };
823
- const normalizedCurrent = normalizeSyncOptions(currentSyncOptions);
824
- const normalizedEffective = normalizeSyncOptions(effectiveSettings);
825
- const gitSyncCurrent = extractGitSyncFields(normalizedCurrent);
826
- const gitSyncEffective = extractGitSyncFields(normalizedEffective);
827
- const hasChanges = !deepEqual(gitSyncEffective, gitSyncCurrent);
828
- if (opts.diff) {
829
- // --diff flag: show differences and exit
830
- if (opts.jsonOutput) {
831
- // Generate structured diff using the same normalized objects
832
- const structuredDiff = hasChanges
833
- ? generateStructuredDiff(gitSyncCurrent, gitSyncEffective)
834
- : {};
835
- console.log(JSON.stringify({
836
- success: true,
837
- hasChanges,
838
- local: syncOptionsToBackendFormat(normalizedEffective),
839
- backend: syncOptionsToBackendFormat(normalizedCurrent),
840
- repository: selectedRepo.git_repo_resource_path,
841
- diff: structuredDiff,
842
- }));
843
- }
844
- else {
845
- if (hasChanges) {
846
- log.info("Changes that would be pushed to Windmill:");
847
- const changes = generateChanges(normalizedCurrent, normalizedEffective);
848
- if (Object.keys(changes).length === 0) {
849
- log.info(colors.green("No changes to push"));
850
- }
851
- else {
852
- displayChanges(changes);
853
- }
854
- }
855
- else {
856
- log.info(colors.green("No changes to push"));
857
- }
858
- }
859
- return;
860
- }
861
- // Default behavior: show changes and ask for confirmation (unless --yes is passed)
862
- if (hasChanges) {
863
- if (!opts.jsonOutput) {
864
- const changes = generateChanges(normalizedCurrent, normalizedEffective);
865
- if (Object.keys(changes).length === 0) {
866
- log.info(colors.green("No changes to push"));
867
- return;
868
- }
869
- else {
870
- log.info("Changes that would be pushed to Windmill:");
871
- displayChanges(changes);
872
- }
873
- }
874
- // Ask for confirmation unless --yes is passed or not in TTY
875
- if (!opts.yes && dntShim.Deno.stdin.isTerminal()) {
876
- const confirmed = await Confirm.prompt({
877
- message: `Do you want to apply these changes to the remote?`,
878
- default: true,
879
- });
880
- if (!confirmed) {
881
- return;
882
- }
883
- }
884
- }
885
- else {
886
- log.info(colors.green("No changes to push"));
887
- return;
888
- }
889
- if (opts.withBackendSettings) {
890
- // Skip backend update when using simulated settings
891
- if (opts.jsonOutput) {
892
- console.log(JSON.stringify({
893
- success: true,
894
- message: `Git-sync settings push simulated (--with-backend-settings used)`,
895
- repository: selectedRepo.git_repo_resource_path,
896
- simulated: true,
897
- }));
898
- }
899
- else {
900
- log.info(colors.green(`Git-sync settings push simulated for ${selectedRepo.git_repo_resource_path} (--with-backend-settings used)`));
901
- }
902
- }
903
- else {
904
- // Update the specific repository settings
905
- const updatedRepos = settings.git_sync.repositories.map((repo) => {
906
- if (repo.git_repo_resource_path ===
907
- selectedRepo.git_repo_resource_path) {
908
- return {
909
- ...repo,
910
- settings: backendFormat,
911
- };
912
- }
913
- return repo;
914
- });
915
- // Push updated settings to backend
916
- try {
917
- await wmill.editWorkspaceGitSyncConfig({
918
- workspace: workspace.workspaceId,
919
- requestBody: {
920
- git_sync_settings: {
921
- repositories: updatedRepos,
922
- },
923
- },
924
- });
925
- }
926
- catch (apiError) {
927
- const errorMessage = apiError instanceof Error ? apiError.message : String(apiError);
928
- if (opts.jsonOutput) {
929
- console.log(JSON.stringify({
930
- success: false,
931
- error: `Failed to update workspace git-sync config: ${errorMessage}`,
932
- }));
933
- }
934
- else {
935
- log.error(colors.red(`Failed to update workspace git-sync config: ${errorMessage}`));
936
- }
937
- return;
938
- }
939
- if (opts.jsonOutput) {
940
- console.log(JSON.stringify({
941
- success: true,
942
- message: `Git-sync settings pushed successfully`,
943
- repository: selectedRepo.git_repo_resource_path,
944
- }));
945
- }
946
- else {
947
- log.info(colors.green(`Git-sync settings pushed successfully to ${selectedRepo.git_repo_resource_path}`));
948
- }
949
- }
950
- }
951
- catch (error) {
952
- const errorMessage = error instanceof Error ? error.message : String(error);
953
- if (opts.jsonOutput) {
954
- console.log(JSON.stringify({ success: false, error: errorMessage }));
955
- }
956
- else {
957
- log.error(colors.red(`Failed to push git-sync settings: ${errorMessage}`));
958
- }
959
- }
960
- }
961
- const command = new Command()
962
- .description("Manage git-sync settings between local wmill.yaml and Windmill backend")
963
- .command("pull")
964
- .description("Pull git-sync settings from Windmill backend to local wmill.yaml")
965
- .option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo)")
966
- .option("--workspace-level", "Write settings to workspace:* override instead of workspace:repo")
967
- .option("--default", "Write settings to top-level defaults instead of overrides")
968
- .option("--replace", "Replace existing settings (non-interactive mode)")
969
- .option("--override", "Add repository-specific override (non-interactive mode)")
970
- .option("--diff", "Show differences without applying changes")
971
- .option("--json-output", "Output in JSON format")
972
- .option("--with-backend-settings <json:string>", "Use provided JSON settings instead of querying backend (for testing)")
973
- .option("--yes", "Skip interactive prompts and use default behavior")
974
- .action(pullGitSyncSettings)
975
- .command("push")
976
- .description("Push git-sync settings from local wmill.yaml to Windmill backend")
977
- .option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo)")
978
- .option("--diff", "Show what would be pushed without applying changes")
979
- .option("--json-output", "Output in JSON format")
980
- .option("--with-backend-settings <json:string>", "Use provided JSON settings instead of querying backend (for testing)")
981
- .option("--yes", "Skip interactive prompts and use default behavior")
982
- .action(pushGitSyncSettings);
983
- export { pullGitSyncSettings, pushGitSyncSettings };
984
- export default command;