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,80 +1,29 @@
1
- import * as dntShim from "./_dnt.shims.js";
2
- import { requireLogin } from "./auth.js";
3
- import { fetchVersion, resolveWorkspace } from "./context.js";
4
- import { colors, Command, Confirm, Select, ensureDir, minimatch, path, log, yamlStringify, yamlParseContent, SEP, } from "./deps.js";
5
- import * as wmill from "./gen/services.gen.js";
6
- import { getTypeStrFromPath, parseFromPath, pushObj, showConflict, showDiff, } from "./types.js";
1
+ import * as dntShim from "../../../_dnt.shims.js";
2
+ import { requireLogin } from "../../core/auth.js";
3
+ import { fetchVersion, resolveWorkspace } from "../../core/context.js";
4
+ import { colors, Command, Confirm, ensureDir, minimatch, path, log, yamlStringify, yamlParseContent, SEP, } from "../../../deps.js";
5
+ import * as wmill from "../../../gen/services.gen.js";
6
+ import { getTypeStrFromPath, parseFromPath, pushObj, showConflict, showDiff, } from "../../types.js";
7
7
  import { downloadZip } from "./pull.js";
8
- import { exts, findContentFile, findGlobalDeps, findResourceFile, handleScriptMetadata, removeExtensionToPath, } from "./script.js";
9
- import { handleFile } from "./script.js";
10
- import { deepEqual, isFileResource } from "./utils.js";
11
- import { readConfigFile, getEffectiveSettings } from "./conf.js";
12
- import { removePathPrefix } from "./types.js";
13
- import { listSyncCodebases } from "./codebase.js";
14
- import { generateFlowLockInternal, generateScriptMetadataInternal, readLockfile, } from "./metadata.js";
15
- import { pushResource } from "./resource.js";
16
- import { extractInlineScripts as extractInlineScriptsForFlows } from "./windmill-utils-internal/src/inline-scripts/extractor.js";
8
+ import { exts, findContentFile, findGlobalDeps, findResourceFile, handleScriptMetadata, removeExtensionToPath, } from "../script/script.js";
9
+ import { handleFile } from "../script/script.js";
10
+ import { deepEqual, isFileResource } from "../../utils/utils.js";
11
+ import { readConfigFile, getEffectiveSettings, validateBranchConfiguration, } from "../../core/conf.js";
12
+ import { removePathPrefix } from "../../types.js";
13
+ import { listSyncCodebases } from "../../utils/codebase.js";
14
+ import { generateFlowLockInternal, generateScriptMetadataInternal, readLockfile, } from "../../utils/metadata.js";
15
+ import { pushResource } from "../resource/resource.js";
16
+ import { newPathAssigner } from "../../../windmill-utils-internal/src/path-utils/path-assigner.js";
17
+ import { extractInlineScripts as extractInlineScriptsForFlows } from "../../../windmill-utils-internal/src/inline-scripts/extractor.js";
17
18
  // Merge CLI options with effective settings, preserving CLI flags as overrides
18
19
  function mergeCliWithEffectiveOptions(cliOpts, effectiveOpts) {
19
20
  // overlay CLI options on top (undefined cliOpts won't override effectiveOpts)
20
21
  return Object.assign({}, effectiveOpts, cliOpts);
21
22
  }
22
- // Resolve effective sync options with smart repository detection
23
- async function resolveEffectiveSyncOptions(workspace, repositoryPath) {
23
+ // Resolve effective sync options using branch-based configuration
24
+ async function resolveEffectiveSyncOptions(workspace, promotion) {
24
25
  const localConfig = await readConfigFile();
25
- // If repository path is already specified, use it directly
26
- if (repositoryPath) {
27
- return getEffectiveSettings(localConfig, workspace.remote, workspace.workspaceId, repositoryPath);
28
- }
29
- // Auto-detect repository from overrides if not specified
30
- if (localConfig.overrides) {
31
- const prefix = `${workspace.remote}:${workspace.workspaceId}:`;
32
- const applicableRepos = [];
33
- // Find all repository-specific overrides for this workspace
34
- for (const key of Object.keys(localConfig.overrides)) {
35
- if (key.startsWith(prefix) && !key.endsWith(":*")) {
36
- const repo = key.substring(prefix.length);
37
- if (repo) {
38
- applicableRepos.push(repo);
39
- }
40
- }
41
- }
42
- if (applicableRepos.length === 1) {
43
- // Single repository found - auto-select it
44
- log.info(`Auto-selected repository: ${applicableRepos[0]}`);
45
- return getEffectiveSettings(localConfig, workspace.remote, workspace.workspaceId, applicableRepos[0]);
46
- }
47
- else if (applicableRepos.length > 1) {
48
- // Multiple repositories found - prompt for selection
49
- const isInteractive = dntShim.Deno.stdin.isTerminal() && dntShim.Deno.stdout.isTerminal();
50
- if (isInteractive) {
51
- const choices = [
52
- {
53
- name: "Use top-level settings (no repository-specific override)",
54
- value: "",
55
- },
56
- ...applicableRepos.map((repo) => ({ name: repo, value: repo })),
57
- ];
58
- const selectedRepo = await Select.prompt({
59
- message: "Multiple repository overrides found. Select which to use:",
60
- options: choices,
61
- });
62
- if (selectedRepo) {
63
- log.info(`Selected repository: ${selectedRepo}`);
64
- }
65
- return getEffectiveSettings(localConfig, workspace.remote, workspace.workspaceId, selectedRepo);
66
- }
67
- else {
68
- // Non-interactive mode - list options and use top-level
69
- log.warn(`Multiple repository overrides found: ${applicableRepos.join(", ")}`);
70
- log.warn(`Running in non-interactive mode. Use --repository flag to specify which one to use.`);
71
- log.info(`Falling back to top-level settings (no repository-specific overrides applied)`);
72
- }
73
- }
74
- }
75
- // No repository overrides found or selected - use top-level settings
76
- log.info(`No repository overrides found, using top-level settings`);
77
- return getEffectiveSettings(localConfig, workspace.remote, workspace.workspaceId, "");
26
+ return await getEffectiveSettings(localConfig, promotion);
78
27
  }
79
28
  export function findCodebase(path, codebases) {
80
29
  if (!path.endsWith(".ts")) {
@@ -283,7 +232,7 @@ function ZipFSElement(zip, useYaml, defaultTs, resourceTypeToFormatExtension, ig
283
232
  async *getChildren() {
284
233
  if (kind == "flow") {
285
234
  const flow = JSON.parse(await f.async("text"));
286
- const inlineScripts = extractInlineScriptsForFlows(flow.value.modules, {}, SEP, defaultTs, newPathAssigner(defaultTs));
235
+ const inlineScripts = extractInlineScriptsForFlows(flow.value.modules, {}, SEP, defaultTs);
287
236
  for (const s of inlineScripts) {
288
237
  yield {
289
238
  isDirectory: false,
@@ -917,13 +866,24 @@ async function buildTracker(changes) {
917
866
  return tracker;
918
867
  }
919
868
  export async function pull(opts) {
869
+ // Validate branch configuration early
870
+ try {
871
+ await validateBranchConfiguration(false, opts.yes);
872
+ }
873
+ catch (error) {
874
+ if (error instanceof Error && error.message.includes("overrides")) {
875
+ log.error(error.message);
876
+ dntShim.Deno.exit(1);
877
+ }
878
+ throw error;
879
+ }
920
880
  if (opts.stateful) {
921
881
  await ensureDir(path.join(dntShim.Deno.cwd(), ".wmill"));
922
882
  }
923
883
  const workspace = await resolveWorkspace(opts);
924
884
  await requireLogin(opts);
925
- // Resolve effective sync options with repository awareness
926
- const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts.repository);
885
+ // Resolve effective sync options with branch awareness
886
+ const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts.promotion);
927
887
  // Merge CLI flags with resolved settings (CLI flags take precedence only for explicit overrides)
928
888
  opts = mergeCliWithEffectiveOptions(opts, effectiveOpts);
929
889
  const codebases = await listSyncCodebases(opts);
@@ -1143,10 +1103,21 @@ function removeSuffix(str, suffix) {
1143
1103
  return str.slice(0, str.length - suffix.length);
1144
1104
  }
1145
1105
  export async function push(opts) {
1106
+ // Validate branch configuration early
1107
+ try {
1108
+ await validateBranchConfiguration(false, opts.yes);
1109
+ }
1110
+ catch (error) {
1111
+ if (error instanceof Error && error.message.includes("overrides")) {
1112
+ log.error(error.message);
1113
+ dntShim.Deno.exit(1);
1114
+ }
1115
+ throw error;
1116
+ }
1146
1117
  const workspace = await resolveWorkspace(opts);
1147
1118
  await requireLogin(opts);
1148
- // Resolve effective sync options with repository awareness
1149
- const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts.repository);
1119
+ // Resolve effective sync options with branch awareness
1120
+ const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts.promotion);
1150
1121
  // Merge CLI flags with resolved settings (CLI flags take precedence only for explicit overrides)
1151
1122
  opts = mergeCliWithEffectiveOptions(opts, effectiveOpts);
1152
1123
  const codebases = await listSyncCodebases(opts);
@@ -1556,6 +1527,7 @@ const command = new Command()
1556
1527
  .option("-e --excludes <patterns:file[]>", "Comma separated patterns to specify which file to NOT take into account. Overrides wmill.yaml excludes")
1557
1528
  .option("--extra-includes <patterns:file[]>", "Comma separated patterns to specify which file to take into account (among files that are compatible with windmill). Patterns can include * (any string until '/') and ** (any string). Useful to still take wmill.yaml into account and act as a second pattern to satisfy")
1558
1529
  .option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist")
1530
+ .option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides")
1559
1531
  // deno-lint-ignore no-explicit-any
1560
1532
  .action(pull)
1561
1533
  .command("push")
@@ -1588,75 +1560,4 @@ const command = new Command()
1588
1560
  .option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist")
1589
1561
  // deno-lint-ignore no-explicit-any
1590
1562
  .action(push);
1591
- const INLINE_SCRIPT = "inline_script";
1592
- export function newPathAssigner(defaultTs) {
1593
- let counter = 0;
1594
- const seen_names = new Set();
1595
- function assignPath(summary, language) {
1596
- let name;
1597
- name = summary?.toLowerCase()?.replaceAll(" ", "_") ?? "";
1598
- let original_name = name;
1599
- if (name == "") {
1600
- original_name = INLINE_SCRIPT;
1601
- name = `${INLINE_SCRIPT}_0`;
1602
- }
1603
- while (seen_names.has(name)) {
1604
- counter++;
1605
- name = `${original_name}_${counter}`;
1606
- }
1607
- seen_names.add(name);
1608
- let ext;
1609
- if (language == "python3")
1610
- ext = "py";
1611
- else if (language == defaultTs || language == "bunnative")
1612
- ext = "ts";
1613
- else if (language == "bun")
1614
- ext = "bun.ts";
1615
- else if (language == "deno")
1616
- ext = "deno.ts";
1617
- else if (language == "go")
1618
- ext = "go";
1619
- else if (language == "bash")
1620
- ext = "sh";
1621
- else if (language == "powershell")
1622
- ext = "ps1";
1623
- else if (language == "postgresql")
1624
- ext = "pg.sql";
1625
- else if (language == "mysql")
1626
- ext = "my.sql";
1627
- else if (language == "bigquery")
1628
- ext = "bq.sql";
1629
- else if (language == "oracledb")
1630
- ext = "odb.sql";
1631
- else if (language == "snowflake")
1632
- ext = "sf.sql";
1633
- else if (language == "mssql")
1634
- ext = "ms.sql";
1635
- else if (language == "graphql")
1636
- ext = "gql";
1637
- else if (language == "nativets")
1638
- ext = "native.ts";
1639
- else if (language == "frontend")
1640
- ext = "frontend.js";
1641
- else if (language == "php")
1642
- ext = "php";
1643
- else if (language == "rust")
1644
- ext = "rs";
1645
- else if (language == "csharp")
1646
- ext = "cs";
1647
- else if (language == "nu")
1648
- ext = "nu";
1649
- else if (language == "ansible")
1650
- ext = "playbook.yml";
1651
- else if (language == "java")
1652
- ext = "java";
1653
- else if (language == "duckdb")
1654
- ext = "duckdb.sql";
1655
- // for related places search: ADD_NEW_LANG
1656
- else
1657
- ext = "no_ext";
1658
- return [`${name}.inline_script.`, ext];
1659
- }
1660
- return { assignPath };
1661
- }
1662
1563
  export default command;
@@ -1,9 +1,9 @@
1
- import * as dntShim from "./_dnt.shims.js";
2
- import * as wmill from "./gen/services.gen.js";
3
- import { colors, Command, log, SEP, Table } from "./deps.js";
4
- import { isSuperset, parseFromFile, removeType, } from "./types.js";
5
- import { requireLogin } from "./auth.js";
6
- import { validatePath, resolveWorkspace } from "./context.js";
1
+ import * as dntShim from "../../../_dnt.shims.js";
2
+ import * as wmill from "../../../gen/services.gen.js";
3
+ import { colors, Command, log, SEP, Table } from "../../../deps.js";
4
+ import { isSuperset, parseFromFile, removeType, } from "../../types.js";
5
+ import { requireLogin } from "../../core/auth.js";
6
+ import { validatePath, resolveWorkspace } from "../../core/context.js";
7
7
  async function getTrigger(triggerType, workspace, path) {
8
8
  const triggerFunctions = {
9
9
  http: wmill.getHttpTrigger,
@@ -1,10 +1,10 @@
1
1
  // deno-lint-ignore-file no-explicit-any
2
- import * as dntShim from "./_dnt.shims.js";
3
- import { requireLogin } from "./auth.js";
4
- import { isSuperset, removeType, removePathPrefix, } from "./types.js";
5
- import { compareInstanceObjects } from "./instance.js";
6
- import { colors, Command, log, Table, yamlStringify, yamlParseFile, } from "./deps.js";
7
- import * as wmill from "./gen/services.gen.js";
2
+ import * as dntShim from "../../../_dnt.shims.js";
3
+ import { requireLogin } from "../../core/auth.js";
4
+ import { isSuperset, removeType, removePathPrefix, } from "../../types.js";
5
+ import { compareInstanceObjects } from "../instance/instance.js";
6
+ import { colors, Command, log, Table, yamlStringify, yamlParseFile, } from "../../../deps.js";
7
+ import * as wmill from "../../../gen/services.gen.js";
8
8
  const INSTANCE_USERS_PATH = "instance_users.yaml";
9
9
  let instanceUsersPath = INSTANCE_USERS_PATH;
10
10
  function checkInstanceUsersPath(opts) {
@@ -1,10 +1,10 @@
1
1
  // deno-lint-ignore-file no-explicit-any
2
- import * as dntShim from "./_dnt.shims.js";
3
- import { requireLogin } from "./auth.js";
4
- import { resolveWorkspace, validatePath } from "./context.js";
5
- import { isSuperset, parseFromFile, removeType, } from "./types.js";
6
- import { colors, Command, Confirm, log, SEP, Table } from "./deps.js";
7
- import * as wmill from "./gen/services.gen.js";
2
+ import * as dntShim from "../../../_dnt.shims.js";
3
+ import { requireLogin } from "../../core/auth.js";
4
+ import { resolveWorkspace, validatePath } from "../../core/context.js";
5
+ import { isSuperset, parseFromFile, removeType, } from "../../types.js";
6
+ import { colors, Command, Confirm, log, SEP, Table } from "../../../deps.js";
7
+ import * as wmill from "../../../gen/services.gen.js";
8
8
  async function list(opts) {
9
9
  const workspace = await resolveWorkspace(opts);
10
10
  await requireLogin(opts);
@@ -1,8 +1,8 @@
1
- import { Command, Confirm, setClient, Table } from "./deps.js";
2
- import { log } from "./deps.js";
3
- import { allInstances, getActiveInstance, pickInstance } from "./instance.js";
4
- import * as wmill from "./gen/services.gen.js";
5
- import { pullInstanceConfigs, pushInstanceConfigs } from "./settings.js";
1
+ import { Command, Confirm, setClient, Table } from "../../../deps.js";
2
+ import { log } from "../../../deps.js";
3
+ import { allInstances, getActiveInstance, pickInstance } from "../instance/instance.js";
4
+ import * as wmill from "../../../gen/services.gen.js";
5
+ import { pullInstanceConfigs, pushInstanceConfigs } from "../../core/settings.js";
6
6
  export async function getInstance(opts) {
7
7
  const instances = await allInstances();
8
8
  const instanceName = await getActiveInstance(opts);
@@ -1,7 +1,7 @@
1
- import { Command, Table } from "./deps.js";
2
- import { log } from "./deps.js";
3
- import * as wmill from "./gen/services.gen.js";
4
- import { pickInstance } from "./instance.js";
1
+ import { Command, Table } from "../../../deps.js";
2
+ import { log } from "../../../deps.js";
3
+ import * as wmill from "../../../gen/services.gen.js";
4
+ import { pickInstance } from "../instance/instance.js";
5
5
  function toPercent(value) {
6
6
  return value != undefined ? `${(value * 100).toFixed(1)}%` : '?%';
7
7
  }
@@ -1,13 +1,13 @@
1
1
  // deno-lint-ignore-file no-explicit-any
2
- import * as dntShim from "./_dnt.shims.js";
3
- import { getRootStore } from "./store.js";
4
- import { loginInteractive, tryGetLoginInfo } from "./login.js";
5
- import { colors, Command, Confirm, Input, log, setClient, Table } from "./deps.js";
6
- import { requireLogin } from "./auth.js";
7
- import * as wmill from "./gen/services.gen.js";
2
+ import * as dntShim from "../../../_dnt.shims.js";
3
+ import { getActiveWorkspaceConfigFilePath, getWorkspaceConfigFilePath } from "../../../windmill-utils-internal/src/config/config.js";
4
+ import { loginInteractive, tryGetLoginInfo } from "../../core/login.js";
5
+ import { colors, Command, Confirm, Input, log, setClient, Table } from "../../../deps.js";
6
+ import { requireLogin } from "../../core/auth.js";
7
+ import * as wmill from "../../../gen/services.gen.js";
8
8
  export async function allWorkspaces(configDirOverride) {
9
9
  try {
10
- const file = (await getRootStore(configDirOverride)) + "remotes.ndjson";
10
+ const file = await getWorkspaceConfigFilePath(configDirOverride);
11
11
  const txt = await dntShim.Deno.readTextFile(file);
12
12
  return txt
13
13
  .split("\n")
@@ -29,13 +29,14 @@ async function getActiveWorkspaceName(opts) {
29
29
  return opts?.workspace;
30
30
  }
31
31
  try {
32
- return await dntShim.Deno.readTextFile((await getRootStore(opts?.configDir)) + "/activeWorkspace");
32
+ const file = await getActiveWorkspaceConfigFilePath(opts?.configDir);
33
+ return await dntShim.Deno.readTextFile(file);
33
34
  }
34
35
  catch {
35
36
  return undefined;
36
37
  }
37
38
  }
38
- export async function getActiveWorkspace(opts) {
39
+ export async function getActiveWorkspace(opts = undefined) {
39
40
  const name = await getActiveWorkspaceName(opts);
40
41
  if (!name) {
41
42
  return undefined;
@@ -88,7 +89,8 @@ async function switchC(opts, workspaceName) {
88
89
  return;
89
90
  }
90
91
  export async function setActiveWorkspace(workspaceName, configDirOverride) {
91
- await dntShim.Deno.writeTextFile((await getRootStore(configDirOverride)) + "/activeWorkspace", workspaceName);
92
+ const file = await getActiveWorkspaceConfigFilePath(configDirOverride);
93
+ await dntShim.Deno.writeTextFile(file, workspaceName);
92
94
  }
93
95
  export async function add(opts, workspaceName, workspaceId, remote) {
94
96
  if (opts.workspace) {
@@ -214,41 +216,11 @@ export async function addWorkspace(workspace, opts) {
214
216
  }
215
217
  }
216
218
  }
217
- // Check 2: Same (remote, workspaceId) tuple already exists under different name
218
- const tupleConflict = existingWorkspaces.find(w => w.remote === workspace.remote &&
219
- w.workspaceId === workspace.workspaceId &&
220
- w.name !== workspace.name);
221
- if (tupleConflict) {
222
- log.info(colors.red.bold(`❌ Workspace ${workspace.workspaceId} on ${workspace.remote} already exists!`));
223
- log.info(` Existing name: "${tupleConflict.name}"`);
224
- log.info(` New name: "${workspace.name}"`);
225
- log.info(colors.yellow(`\nNote: Backend constraint prevents duplicate (remote, workspaceId) combinations.`));
226
- if (!isInteractive) {
227
- // In non-interactive mode (tests, scripts), auto-overwrite with force flag
228
- if (opts.force) {
229
- log.info(colors.yellow(`Force flag enabled, overwriting existing workspace "${tupleConflict.name}".`));
230
- }
231
- else {
232
- throw new Error(`Backend constraint violation: (${workspace.remote}, ${workspace.workspaceId}) already exists as "${tupleConflict.name}". Use --force to overwrite.`);
233
- }
234
- }
235
- else {
236
- const overwrite = await Confirm.prompt({
237
- message: `Do you want to overwrite the existing workspace "${tupleConflict.name}"?`,
238
- default: false,
239
- });
240
- if (!overwrite) {
241
- log.info(colors.yellow("Operation cancelled."));
242
- return;
243
- }
244
- }
245
- // Remove the conflicting workspace
246
- await removeWorkspace(tupleConflict.name, true, opts);
247
- }
248
219
  // Remove existing workspace with same name (if updating)
249
220
  await removeWorkspace(workspace.name, true, opts);
250
221
  // Add the new workspace
251
- const file = await dntShim.Deno.open((await getRootStore(opts.configDir)) + "remotes.ndjson", {
222
+ const filePath = await getWorkspaceConfigFilePath(opts.configDir);
223
+ const file = await dntShim.Deno.open(filePath, {
252
224
  append: true,
253
225
  write: true,
254
226
  read: true,
@@ -270,7 +242,8 @@ export async function removeWorkspace(name, silent, opts) {
270
242
  if (!silent) {
271
243
  log.info(colors.yellow(`Removing existing workspace ${name}`));
272
244
  }
273
- await dntShim.Deno.writeTextFile((await getRootStore(opts.configDir)) + "remotes.ndjson", orgWorkspaces
245
+ const filePath = await getWorkspaceConfigFilePath(opts.configDir);
246
+ await dntShim.Deno.writeTextFile(filePath, orgWorkspaces
274
247
  .filter((x) => x.name !== name)
275
248
  .map((x) => JSON.stringify(x))
276
249
  .join("\n") + "\n");
@@ -287,7 +260,60 @@ async function whoami(_opts) {
287
260
  const activeName = await getActiveWorkspaceName(_opts);
288
261
  log.info("Active: " + colors.green.bold(activeName || "none"));
289
262
  }
263
+ async function bind(opts, bindWorkspace) {
264
+ const { isGitRepository, getCurrentGitBranch } = await import("../../utils/git.js");
265
+ if (!isGitRepository()) {
266
+ log.error(colors.red("Not in a Git repository"));
267
+ return;
268
+ }
269
+ const branch = opts.branch || getCurrentGitBranch();
270
+ if (!branch) {
271
+ log.error(colors.red("Could not determine current Git branch"));
272
+ return;
273
+ }
274
+ const { readConfigFile } = await import("../../core/conf.js");
275
+ const config = await readConfigFile();
276
+ const activeWorkspace = await getActiveWorkspace(opts);
277
+ if (!activeWorkspace && bindWorkspace) {
278
+ log.error(colors.red("No active workspace. Use 'wmill workspace add' or 'wmill workspace switch' first"));
279
+ return;
280
+ }
281
+ // For unbind, check if branch exists
282
+ if (!bindWorkspace && (!config.git_branches || !config.git_branches[branch])) {
283
+ log.error(colors.red(`Branch '${branch}' not found in wmill.yaml git_branches`));
284
+ return;
285
+ }
286
+ // Update the branch configuration with workspace binding
287
+ if (!config.git_branches) {
288
+ config.git_branches = {};
289
+ }
290
+ if (!config.git_branches[branch]) {
291
+ config.git_branches[branch] = { overrides: {} };
292
+ }
293
+ if (bindWorkspace && activeWorkspace) {
294
+ config.git_branches[branch].baseUrl = activeWorkspace.remote;
295
+ config.git_branches[branch].workspaceId = activeWorkspace.workspaceId;
296
+ log.info(colors.green(`✓ Bound branch '${branch}' to workspace '${activeWorkspace.name}'\n` +
297
+ ` ${activeWorkspace.workspaceId} on ${activeWorkspace.remote}`));
298
+ }
299
+ else {
300
+ // Unbind
301
+ delete config.git_branches[branch].baseUrl;
302
+ delete config.git_branches[branch].workspaceId;
303
+ log.info(colors.green(`✓ Removed workspace binding from branch '${branch}'`));
304
+ }
305
+ // Write back the updated config
306
+ const { yamlStringify } = await import("../../../deps.js");
307
+ try {
308
+ await dntShim.Deno.writeTextFile("wmill.yaml", yamlStringify(config));
309
+ }
310
+ catch (error) {
311
+ log.error(colors.red(`Failed to save configuration: ${error.message}`));
312
+ return;
313
+ }
314
+ }
290
315
  const command = new Command()
316
+ .alias("profile")
291
317
  .description("workspace related commands")
292
318
  .action(list)
293
319
  .command("switch")
@@ -310,5 +336,13 @@ const command = new Command()
310
336
  .action(remove)
311
337
  .command("whoami")
312
338
  .description("Show the currently active user")
313
- .action(whoami);
339
+ .action(whoami)
340
+ .command("bind")
341
+ .description("Bind the current Git branch to the active workspace")
342
+ .option("--branch <branch:string>", "Specify branch (defaults to current)")
343
+ .action((opts) => bind(opts, true))
344
+ .command("unbind")
345
+ .description("Remove workspace binding from the current Git branch")
346
+ .option("--branch <branch:string>", "Specify branch (defaults to current)")
347
+ .action((opts) => bind(opts, false));
314
348
  export default command;
@@ -1,6 +1,6 @@
1
1
  // deno-lint-ignore-file no-explicit-any
2
- import { log, setClient } from "./deps.js";
3
- import * as wmill from "./gen/services.gen.js";
2
+ import { log, setClient } from "../../deps.js";
3
+ import * as wmill from "../../gen/services.gen.js";
4
4
  import { loginInteractive, tryGetLoginInfo } from "./login.js";
5
5
  /**
6
6
  * Main authentication function - moved from context.ts to break circular dependencies
@@ -30,7 +30,7 @@ export async function requireLogin(opts) {
30
30
  throw new Error("Unauthorized: Could not authenticate with the provided credentials");
31
31
  }
32
32
  // Update workspace token
33
- const { removeWorkspace, addWorkspace } = await import("./workspace.js");
33
+ const { removeWorkspace, addWorkspace } = await import("../commands/workspace/workspace.js");
34
34
  removeWorkspace(workspace.name, false, opts);
35
35
  workspace.token = newToken;
36
36
  addWorkspace(workspace, opts);
@@ -0,0 +1,46 @@
1
+ import * as dntShim from "../../_dnt.shims.js";
2
+ import { log } from "../../deps.js";
3
+ import { getStore } from "./store.js";
4
+ const BRANCH_PROFILES_FILE = "branch-profiles.json";
5
+ export async function getBranchProfilesPath(configDirOverride) {
6
+ return (await getStore("", configDirOverride)) + BRANCH_PROFILES_FILE;
7
+ }
8
+ export async function loadBranchProfiles(configDirOverride) {
9
+ try {
10
+ const path = await getBranchProfilesPath(configDirOverride);
11
+ const content = await dntShim.Deno.readTextFile(path);
12
+ return JSON.parse(content);
13
+ }
14
+ catch {
15
+ // File doesn't exist or invalid JSON - return empty mapping
16
+ return { lastUsed: {} };
17
+ }
18
+ }
19
+ export async function saveBranchProfiles(mapping, configDirOverride) {
20
+ const path = await getBranchProfilesPath(configDirOverride);
21
+ await dntShim.Deno.writeTextFile(path, JSON.stringify(mapping, null, 2));
22
+ }
23
+ export function getBranchProfileKey(branch, baseUrl, workspaceId) {
24
+ // Normalize baseUrl to ensure consistency
25
+ let normalizedUrl;
26
+ try {
27
+ normalizedUrl = new URL(baseUrl).toString();
28
+ }
29
+ catch {
30
+ // Fallback to non-normalized URL if parsing fails
31
+ normalizedUrl = baseUrl;
32
+ }
33
+ return `${branch}|${normalizedUrl}|${workspaceId}`;
34
+ }
35
+ export async function getLastUsedProfile(branch, baseUrl, workspaceId, configDirOverride) {
36
+ const mapping = await loadBranchProfiles(configDirOverride);
37
+ const key = getBranchProfileKey(branch, baseUrl, workspaceId);
38
+ return mapping.lastUsed[key];
39
+ }
40
+ export async function setLastUsedProfile(branch, baseUrl, workspaceId, profileName, configDirOverride) {
41
+ const mapping = await loadBranchProfiles(configDirOverride);
42
+ const key = getBranchProfileKey(branch, baseUrl, workspaceId);
43
+ mapping.lastUsed[key] = profileName;
44
+ await saveBranchProfiles(mapping, configDirOverride);
45
+ log.debug(`Saved last used profile for ${key}: ${profileName}`);
46
+ }