windmill-cli 1.608.0 → 1.610.1

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.
@@ -32,7 +32,7 @@ export const OpenAPI = {
32
32
  PASSWORD: undefined,
33
33
  TOKEN: getEnv("WM_TOKEN"),
34
34
  USERNAME: undefined,
35
- VERSION: '1.608.0',
35
+ VERSION: '1.610.1',
36
36
  WITH_CREDENTIALS: true,
37
37
  interceptors: {
38
38
  request: new Interceptors(),
@@ -1973,6 +1973,25 @@ export const editErrorHandler = (data) => {
1973
1973
  mediaType: 'application/json'
1974
1974
  });
1975
1975
  };
1976
+ /**
1977
+ * edit success handler
1978
+ * @param data The data for the request.
1979
+ * @param data.workspace
1980
+ * @param data.requestBody WorkspaceSuccessHandler
1981
+ * @returns string status
1982
+ * @throws ApiError
1983
+ */
1984
+ export const editSuccessHandler = (data) => {
1985
+ return __request(OpenAPI, {
1986
+ method: 'POST',
1987
+ url: '/w/{workspace}/workspaces/edit_success_handler',
1988
+ path: {
1989
+ workspace: data.workspace
1990
+ },
1991
+ body: data.requestBody,
1992
+ mediaType: 'application/json'
1993
+ });
1994
+ };
1976
1995
  /**
1977
1996
  * edit large file storage settings
1978
1997
  * @param data The data for the request.
@@ -3456,6 +3475,88 @@ export const listResourceTypeNames = (data) => {
3456
3475
  }
3457
3476
  });
3458
3477
  };
3478
+ /**
3479
+ * get npm package metadata from private registry
3480
+ * @param data The data for the request.
3481
+ * @param data.workspace
3482
+ * @param data._package npm package name
3483
+ * @returns unknown package metadata
3484
+ * @throws ApiError
3485
+ */
3486
+ export const getNpmPackageMetadata = (data) => {
3487
+ return __request(OpenAPI, {
3488
+ method: 'GET',
3489
+ url: '/w/{workspace}/npm_proxy/metadata/{package}',
3490
+ path: {
3491
+ workspace: data.workspace,
3492
+ package: data._package
3493
+ }
3494
+ });
3495
+ };
3496
+ /**
3497
+ * resolve npm package version from private registry
3498
+ * @param data The data for the request.
3499
+ * @param data.workspace
3500
+ * @param data._package npm package name
3501
+ * @param data.tag version tag or reference
3502
+ * @returns unknown resolved version
3503
+ * @throws ApiError
3504
+ */
3505
+ export const resolveNpmPackageVersion = (data) => {
3506
+ return __request(OpenAPI, {
3507
+ method: 'GET',
3508
+ url: '/w/{workspace}/npm_proxy/resolve/{package}',
3509
+ path: {
3510
+ workspace: data.workspace,
3511
+ package: data._package
3512
+ },
3513
+ query: {
3514
+ tag: data.tag
3515
+ }
3516
+ });
3517
+ };
3518
+ /**
3519
+ * get npm package file tree from private registry
3520
+ * @param data The data for the request.
3521
+ * @param data.workspace
3522
+ * @param data._package npm package name
3523
+ * @param data.version package version
3524
+ * @returns unknown package file tree
3525
+ * @throws ApiError
3526
+ */
3527
+ export const getNpmPackageFiletree = (data) => {
3528
+ return __request(OpenAPI, {
3529
+ method: 'GET',
3530
+ url: '/w/{workspace}/npm_proxy/filetree/{package}/{version}',
3531
+ path: {
3532
+ workspace: data.workspace,
3533
+ package: data._package,
3534
+ version: data.version
3535
+ }
3536
+ });
3537
+ };
3538
+ /**
3539
+ * get specific file from npm package in private registry
3540
+ * @param data The data for the request.
3541
+ * @param data.workspace
3542
+ * @param data._package npm package name
3543
+ * @param data.version package version
3544
+ * @param data.filepath file path within package
3545
+ * @returns string file content
3546
+ * @throws ApiError
3547
+ */
3548
+ export const getNpmPackageFile = (data) => {
3549
+ return __request(OpenAPI, {
3550
+ method: 'GET',
3551
+ url: '/w/{workspace}/npm_proxy/file/{package}/{version}/{filepath}',
3552
+ path: {
3553
+ workspace: data.workspace,
3554
+ package: data._package,
3555
+ version: data.version,
3556
+ filepath: data.filepath
3557
+ }
3558
+ });
3559
+ };
3459
3560
  /**
3460
3561
  * query resource types by similarity
3461
3562
  * @param data The data for the request.
@@ -172,6 +172,7 @@ export async function handleFile(path, workspace, alreadySynced, message, opts,
172
172
  continue;
173
173
  }
174
174
  log.info(`Adding file: ${file.path.substring(1)}`);
175
+ // deno-lint-ignore no-explicit-any
175
176
  const fil = new File([file.contents], file.path.substring(1));
176
177
  tarball.append(fil);
177
178
  }
@@ -350,7 +351,7 @@ async function streamToBlob(stream) {
350
351
  // Push the chunk to the array
351
352
  chunks.push(value);
352
353
  }
353
- // Create a Blob from the chunks
354
+ // deno-lint-ignore no-explicit-any
354
355
  const blob = new Blob(chunks);
355
356
  return blob;
356
357
  }
@@ -10,7 +10,7 @@ import { handleFile } from "../script/script.js";
10
10
  import { deepEqual, fetchRemoteVersion, isFileResource, isRawAppFile, isWorkspaceDependencies, } from "../../utils/utils.js";
11
11
  import { getEffectiveSettings, mergeConfigWithConfigFile, validateBranchConfiguration, } from "../../core/conf.js";
12
12
  import { fromBranchSpecificPath, getBranchSpecificPath, getSpecificItemsForCurrentBranch, isBranchSpecificFile, isCurrentBranchFile, isSpecificItem, } from "../../core/specific_items.js";
13
- import { getCurrentGitBranch } from "../../utils/git.js";
13
+ import { getCurrentGitBranch, isGitRepository } from "../../utils/git.js";
14
14
  import { removePathPrefix } from "../../types.js";
15
15
  import { listSyncCodebases } from "../../utils/codebase.js";
16
16
  import { generateScriptMetadataInternal, getRawWorkspaceDependencies, readLockfile, workspaceDependenciesPathToLanguageAndFilename, } from "../../utils/metadata.js";
@@ -27,8 +27,8 @@ function mergeCliWithEffectiveOptions(cliOpts, effectiveOpts) {
27
27
  return Object.assign({}, effectiveOpts, cliOpts);
28
28
  }
29
29
  // Resolve effective sync options using branch-based configuration
30
- async function resolveEffectiveSyncOptions(workspace, localConfig, promotion) {
31
- return await getEffectiveSettings(localConfig, promotion);
30
+ async function resolveEffectiveSyncOptions(workspace, localConfig, promotion, branchOverride) {
31
+ return await getEffectiveSettings(localConfig, promotion, false, false, branchOverride);
32
32
  }
33
33
  export function findCodebase(path, codebases) {
34
34
  if (!path.endsWith(".ts")) {
@@ -1014,7 +1014,7 @@ export async function* readDirRecursiveWithIgnore(ignore, root) {
1014
1014
  }
1015
1015
  }
1016
1016
  }
1017
- export async function elementsToMap(els, ignore, json, skips, specificItems) {
1017
+ export async function elementsToMap(els, ignore, json, skips, specificItems, branchOverride) {
1018
1018
  const map = {};
1019
1019
  const processedBasePaths = new Set();
1020
1020
  for await (const entry of readDirRecursiveWithIgnore(ignore, els)) {
@@ -1115,8 +1115,7 @@ export async function elementsToMap(els, ignore, json, skips, specificItems) {
1115
1115
  }
1116
1116
  // Handle branch-specific files - skip files for other branches
1117
1117
  if (specificItems && isBranchSpecificFile(path)) {
1118
- const currentBranch = getCurrentGitBranch();
1119
- if (!currentBranch || !isCurrentBranchFile(path)) {
1118
+ if (!isCurrentBranchFile(path, branchOverride)) {
1120
1119
  // Skip branch-specific files for other branches
1121
1120
  continue;
1122
1121
  }
@@ -1153,9 +1152,10 @@ export async function elementsToMap(els, ignore, json, skips, specificItems) {
1153
1152
  }
1154
1153
  // Handle branch-specific path mapping after all filtering
1155
1154
  if (specificItems) {
1156
- const currentBranch = getCurrentGitBranch();
1157
- if (currentBranch && isCurrentBranchFile(path)) {
1155
+ if (isCurrentBranchFile(path, branchOverride)) {
1158
1156
  // This is a branch-specific file for current branch
1157
+ // Safe to compute branch here since isCurrentBranchFile already validated it exists
1158
+ const currentBranch = branchOverride || getCurrentGitBranch();
1159
1159
  const basePath = fromBranchSpecificPath(path, currentBranch);
1160
1160
  if (isSpecificItem(basePath, specificItems)) {
1161
1161
  // Map to base path for push operations
@@ -1183,13 +1183,13 @@ export async function elementsToMap(els, ignore, json, skips, specificItems) {
1183
1183
  }
1184
1184
  return map;
1185
1185
  }
1186
- async function compareDynFSElement(els1, els2, ignore, json, skips, ignoreMetadataDeletion, codebases, ignoreCodebaseChanges, specificItems) {
1186
+ async function compareDynFSElement(els1, els2, ignore, json, skips, ignoreMetadataDeletion, codebases, ignoreCodebaseChanges, specificItems, branchOverride) {
1187
1187
  const [m1, m2] = els2
1188
1188
  ? await Promise.all([
1189
- elementsToMap(els1, ignore, json, skips, specificItems),
1190
- elementsToMap(els2, ignore, json, skips, specificItems),
1189
+ elementsToMap(els1, ignore, json, skips, specificItems, branchOverride),
1190
+ elementsToMap(els2, ignore, json, skips, specificItems, branchOverride),
1191
1191
  ])
1192
- : [await elementsToMap(els1, ignore, json, skips, specificItems), {}];
1192
+ : [await elementsToMap(els1, ignore, json, skips, specificItems, branchOverride), {}];
1193
1193
  const changes = [];
1194
1194
  function parseYaml(k, v) {
1195
1195
  if (k.endsWith(".script.yaml")) {
@@ -1573,12 +1573,12 @@ export async function pull(opts) {
1573
1573
  if (opts.stateful) {
1574
1574
  await ensureDir(path.join(dntShim.Deno.cwd(), ".wmill"));
1575
1575
  }
1576
- const workspace = await resolveWorkspace(opts);
1576
+ const workspace = await resolveWorkspace(opts, opts.branch);
1577
1577
  await requireLogin(opts);
1578
1578
  // Resolve effective sync options with branch awareness
1579
- const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts, opts.promotion);
1579
+ const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts, opts.promotion, opts.branch);
1580
1580
  // Extract specific items configuration before merging overwrites gitBranches
1581
- const specificItems = getSpecificItemsForCurrentBranch(opts);
1581
+ const specificItems = getSpecificItemsForCurrentBranch(opts, opts.branch);
1582
1582
  // Merge CLI flags with resolved settings (CLI flags take precedence only for explicit overrides)
1583
1583
  opts = mergeCliWithEffectiveOptions(originalCliOpts, effectiveOpts);
1584
1584
  const codebases = await listSyncCodebases(opts);
@@ -1597,7 +1597,7 @@ export async function pull(opts) {
1597
1597
  const local = !opts.stateful
1598
1598
  ? await FSFSElement(dntShim.Deno.cwd(), codebases, true)
1599
1599
  : await FSFSElement(path.join(dntShim.Deno.cwd(), ".wmill"), [], true);
1600
- const changes = await compareDynFSElement(remote, local, await ignoreF(opts), opts.json ?? false, opts, false, codebases, true, specificItems);
1600
+ const changes = await compareDynFSElement(remote, local, await ignoreF(opts), opts.json ?? false, opts, false, codebases, true, specificItems, opts.branch);
1601
1601
  log.info(`remote (${workspace.name}) -> local: ${changes.length} changes to apply`);
1602
1602
  // Handle JSON output for dry-run
1603
1603
  if (opts.dryRun && opts.jsonOutput) {
@@ -1612,7 +1612,7 @@ export async function pull(opts) {
1612
1612
  ...(specificItems && isSpecificItem(change.path, specificItems)
1613
1613
  ? {
1614
1614
  branch_specific: true,
1615
- branch_specific_path: getBranchSpecificPath(change.path, specificItems),
1615
+ branch_specific_path: getBranchSpecificPath(change.path, specificItems, opts.branch),
1616
1616
  }
1617
1617
  : {}),
1618
1618
  })),
@@ -1623,7 +1623,7 @@ export async function pull(opts) {
1623
1623
  }
1624
1624
  if (changes.length > 0) {
1625
1625
  if (!opts.jsonOutput) {
1626
- prettyChanges(changes, specificItems);
1626
+ prettyChanges(changes, specificItems, opts.branch);
1627
1627
  }
1628
1628
  if (opts.dryRun) {
1629
1629
  log.info(colors.gray(`Dry run complete.`));
@@ -1642,7 +1642,7 @@ export async function pull(opts) {
1642
1642
  // Determine if this file should be written to a branch-specific path
1643
1643
  let targetPath = change.path;
1644
1644
  if (specificItems && isSpecificItem(change.path, specificItems)) {
1645
- const branchSpecificPath = getBranchSpecificPath(change.path, specificItems);
1645
+ const branchSpecificPath = getBranchSpecificPath(change.path, specificItems, opts.branch);
1646
1646
  if (branchSpecificPath) {
1647
1647
  targetPath = branchSpecificPath;
1648
1648
  }
@@ -1776,7 +1776,7 @@ export async function pull(opts) {
1776
1776
  ...(specificItems && isSpecificItem(change.path, specificItems)
1777
1777
  ? {
1778
1778
  branch_specific: true,
1779
- branch_specific_path: getBranchSpecificPath(change.path, specificItems),
1779
+ branch_specific_path: getBranchSpecificPath(change.path, specificItems, opts.branch),
1780
1780
  }
1781
1781
  : {}),
1782
1782
  })),
@@ -1792,13 +1792,13 @@ export async function pull(opts) {
1792
1792
  console.log(JSON.stringify({ success: true, message: "No changes to apply", total: 0 }, null, 2));
1793
1793
  }
1794
1794
  }
1795
- function prettyChanges(changes, specificItems) {
1795
+ function prettyChanges(changes, specificItems, branchOverride) {
1796
1796
  for (const change of changes) {
1797
1797
  let displayPath = change.path;
1798
1798
  let branchNote = "";
1799
1799
  // Check if this will be written as a branch-specific file
1800
1800
  if (specificItems && isSpecificItem(change.path, specificItems)) {
1801
- const branchSpecificPath = getBranchSpecificPath(change.path, specificItems);
1801
+ const branchSpecificPath = getBranchSpecificPath(change.path, specificItems, branchOverride);
1802
1802
  if (branchSpecificPath) {
1803
1803
  displayPath = branchSpecificPath;
1804
1804
  branchNote = " (branch-specific)";
@@ -1875,12 +1875,12 @@ export async function push(opts) {
1875
1875
  }
1876
1876
  throw error;
1877
1877
  }
1878
- const workspace = await resolveWorkspace(opts);
1878
+ const workspace = await resolveWorkspace(opts, opts.branch);
1879
1879
  await requireLogin(opts);
1880
1880
  // Resolve effective sync options with branch awareness
1881
- const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts, opts.promotion);
1881
+ const effectiveOpts = await resolveEffectiveSyncOptions(workspace, opts, opts.promotion, opts.branch);
1882
1882
  // Extract specific items configuration BEFORE merging overwrites gitBranches
1883
- const specificItems = getSpecificItemsForCurrentBranch(opts);
1883
+ const specificItems = getSpecificItemsForCurrentBranch(opts, opts.branch);
1884
1884
  // Merge CLI flags with resolved settings (CLI flags take precedence only for explicit overrides)
1885
1885
  opts = mergeCliWithEffectiveOptions(originalCliOpts, effectiveOpts);
1886
1886
  const codebases = await listSyncCodebases(opts);
@@ -1907,7 +1907,7 @@ export async function push(opts) {
1907
1907
  }
1908
1908
  const remote = ZipFSElement((await downloadZip(workspace, opts.plainSecrets, opts.skipVariables, opts.skipResources, opts.skipResourceTypes, opts.skipSecrets, opts.includeSchedules, opts.includeTriggers, opts.includeUsers, opts.includeGroups, opts.includeSettings, opts.includeKey, opts.skipWorkspaceDependencies, opts.defaultTs)), !opts.json, opts.defaultTs ?? "bun", resourceTypeToFormatExtension, false);
1909
1909
  const local = await FSFSElement(path.join(dntShim.Deno.cwd(), ""), codebases, false);
1910
- const changes = await compareDynFSElement(local, remote, await ignoreF(opts), opts.json ?? false, opts, true, codebases, false, specificItems);
1910
+ const changes = await compareDynFSElement(local, remote, await ignoreF(opts), opts.json ?? false, opts, true, codebases, false, specificItems, opts.branch);
1911
1911
  const rawWorkspaceDependencies = await getRawWorkspaceDependencies();
1912
1912
  const tracker = await buildTracker(changes);
1913
1913
  const staleScripts = [];
@@ -1974,7 +1974,7 @@ export async function push(opts) {
1974
1974
  ...(specificItems && isSpecificItem(change.path, specificItems)
1975
1975
  ? {
1976
1976
  branch_specific: true,
1977
- branch_specific_path: getBranchSpecificPath(change.path, specificItems),
1977
+ branch_specific_path: getBranchSpecificPath(change.path, specificItems, opts.branch),
1978
1978
  }
1979
1979
  : {}),
1980
1980
  })),
@@ -1985,7 +1985,7 @@ export async function push(opts) {
1985
1985
  }
1986
1986
  if (changes.length > 0) {
1987
1987
  if (!opts.jsonOutput) {
1988
- prettyChanges(changes, specificItems);
1988
+ prettyChanges(changes, specificItems, opts.branch);
1989
1989
  }
1990
1990
  if (opts.dryRun) {
1991
1991
  log.info(colors.gray(`Dry run complete.`));
@@ -2083,7 +2083,7 @@ export async function push(opts) {
2083
2083
  // For branch-specific resources, push to the base path on the workspace server
2084
2084
  // This ensures branch-specific files are stored with their base names in the workspace
2085
2085
  let serverPath = resourceFilePath;
2086
- const currentBranch = getCurrentGitBranch();
2086
+ const currentBranch = opts.branch || (isGitRepository() ? getCurrentGitBranch() : null);
2087
2087
  if (currentBranch && isBranchSpecificFile(resourceFilePath)) {
2088
2088
  serverPath = fromBranchSpecificPath(resourceFilePath, currentBranch);
2089
2089
  }
@@ -2099,7 +2099,7 @@ export async function push(opts) {
2099
2099
  // Check if this is a branch-specific item and get the original branch-specific path
2100
2100
  let originalBranchSpecificPath;
2101
2101
  if (specificItems && isSpecificItem(change.path, specificItems)) {
2102
- originalBranchSpecificPath = getBranchSpecificPath(change.path, specificItems);
2102
+ originalBranchSpecificPath = getBranchSpecificPath(change.path, specificItems, opts.branch);
2103
2103
  }
2104
2104
  await pushObj(workspace.workspaceId, change.path, oldObj, newObj, opts.plainSecrets ?? false, alreadySynced, opts.message, originalBranchSpecificPath);
2105
2105
  if (stateTarget) {
@@ -2125,7 +2125,7 @@ export async function push(opts) {
2125
2125
  // For branch-specific items, we read from branch-specific files but push to base server paths
2126
2126
  let localFilePath = change.path;
2127
2127
  if (specificItems && isSpecificItem(change.path, specificItems)) {
2128
- const branchSpecificPath = getBranchSpecificPath(change.path, specificItems);
2128
+ const branchSpecificPath = getBranchSpecificPath(change.path, specificItems, opts.branch);
2129
2129
  if (branchSpecificPath) {
2130
2130
  localFilePath = branchSpecificPath;
2131
2131
  }
@@ -2330,7 +2330,7 @@ export async function push(opts) {
2330
2330
  ...(specificItems && isSpecificItem(change.path, specificItems)
2331
2331
  ? {
2332
2332
  branch_specific: true,
2333
- branch_specific_path: getBranchSpecificPath(change.path, specificItems),
2333
+ branch_specific_path: getBranchSpecificPath(change.path, specificItems, opts.branch),
2334
2334
  }
2335
2335
  : {}),
2336
2336
  })),
@@ -2379,6 +2379,7 @@ const command = new Command()
2379
2379
  .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")
2380
2380
  .option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist")
2381
2381
  .option("--promotion <branch:string>", "Use promotionOverrides from the specified branch instead of regular overrides")
2382
+ .option("--branch <branch:string>", "Override the current git branch (works even outside a git repository)")
2382
2383
  // deno-lint-ignore no-explicit-any
2383
2384
  .action(pull)
2384
2385
  .command("push")
@@ -2411,6 +2412,7 @@ const command = new Command()
2411
2412
  .option("--message <message:string>", "Include a message that will be added to all scripts/flows/apps updated during this push")
2412
2413
  .option("--parallel <number>", "Number of changes to process in parallel")
2413
2414
  .option("--repository <repo:string>", "Specify repository path (e.g., u/user/repo) when multiple repositories exist")
2415
+ .option("--branch <branch:string>", "Override the current git branch (works even outside a git repository)")
2414
2416
  // deno-lint-ignore no-explicit-any
2415
2417
  .action(push);
2416
2418
  export default command;
@@ -268,60 +268,67 @@ export async function validateBranchConfiguration(opts) {
268
268
  }
269
269
  }
270
270
  // Get effective settings by merging top-level settings with branch-specific overrides
271
- export async function getEffectiveSettings(config, promotion, skipBranchValidation, suppressLogs) {
271
+ export async function getEffectiveSettings(config, promotion, skipBranchValidation, suppressLogs, branchOverride) {
272
272
  // Start with top-level settings from config
273
273
  const { gitBranches, ...topLevelSettings } = config;
274
274
  const effective = { ...topLevelSettings };
275
- if (isGitRepository()) {
276
- const branch = getCurrentGitBranch();
277
- const originalBranchIfForked = getOriginalBranchForWorkspaceForks(branch);
278
- let currentBranch;
275
+ // Determine the branch to use: branchOverride takes precedence, then git detection
276
+ let currentBranch = null;
277
+ let originalBranchIfForked = null;
278
+ let rawGitBranch = null;
279
+ if (branchOverride) {
280
+ currentBranch = branchOverride;
281
+ // Note: "Using branch override" is logged in context.ts when resolving workspace
282
+ }
283
+ else if (isGitRepository()) {
284
+ rawGitBranch = getCurrentGitBranch();
285
+ originalBranchIfForked = getOriginalBranchForWorkspaceForks(rawGitBranch);
279
286
  if (originalBranchIfForked) {
280
287
  log.info(`Using overrides from original branch \`${originalBranchIfForked}\``);
281
288
  currentBranch = originalBranchIfForked;
282
289
  }
283
290
  else {
284
- currentBranch = branch;
291
+ currentBranch = rawGitBranch;
285
292
  }
286
- // If promotion is specified, use that branch's promotionOverrides or overrides
287
- if (promotion && gitBranches && gitBranches[promotion]) {
288
- const targetBranch = gitBranches[promotion];
289
- // First try promotionOverrides, then fall back to overrides
290
- if (targetBranch.promotionOverrides) {
291
- Object.assign(effective, targetBranch.promotionOverrides);
292
- if (!suppressLogs) {
293
- log.info(`Applied promotion settings from branch: ${promotion}`);
294
- }
295
- }
296
- else if (targetBranch.overrides) {
297
- Object.assign(effective, targetBranch.overrides);
298
- if (!suppressLogs) {
299
- log.info(`Applied settings from branch: ${promotion} (no promotionOverrides found)`);
300
- }
301
- }
302
- else {
303
- log.debug(`No promotion or regular overrides found for branch '${promotion}', using top-level settings`);
293
+ }
294
+ else {
295
+ log.debug("Not in a Git repository and no branch override provided, using top-level settings");
296
+ }
297
+ // If promotion is specified, use that branch's promotionOverrides or overrides
298
+ if (promotion && gitBranches && gitBranches[promotion]) {
299
+ const targetBranch = gitBranches[promotion];
300
+ // First try promotionOverrides, then fall back to overrides
301
+ if (targetBranch.promotionOverrides) {
302
+ Object.assign(effective, targetBranch.promotionOverrides);
303
+ if (!suppressLogs) {
304
+ log.info(`Applied promotion settings from branch: ${promotion}`);
304
305
  }
305
306
  }
306
- // Otherwise use current branch overrides (existing behavior)
307
- else if (currentBranch &&
308
- gitBranches &&
309
- gitBranches[currentBranch] &&
310
- gitBranches[currentBranch].overrides) {
311
- Object.assign(effective, gitBranches[currentBranch].overrides);
307
+ else if (targetBranch.overrides) {
308
+ Object.assign(effective, targetBranch.overrides);
312
309
  if (!suppressLogs) {
313
- const extraLog = originalBranchIfForked
314
- ? ` (because it is the origin of the workspace fork branch \`${branch}\`)`
315
- : "";
316
- log.info(`Applied settings for Git branch: ${currentBranch}${extraLog}`);
310
+ log.info(`Applied settings from branch: ${promotion} (no promotionOverrides found)`);
317
311
  }
318
312
  }
319
- else if (currentBranch) {
320
- log.debug(`No branch-specific overrides found for '${currentBranch}', using top-level settings`);
313
+ else {
314
+ log.debug(`No promotion or regular overrides found for branch '${promotion}', using top-level settings`);
321
315
  }
322
316
  }
323
- else {
324
- log.debug("Not in a Git repository, using top-level settings");
317
+ // Otherwise use current branch overrides (existing behavior)
318
+ else if (currentBranch &&
319
+ gitBranches &&
320
+ gitBranches[currentBranch] &&
321
+ gitBranches[currentBranch].overrides) {
322
+ Object.assign(effective, gitBranches[currentBranch].overrides);
323
+ if (!suppressLogs) {
324
+ const extraLog = originalBranchIfForked
325
+ ? ` (because it is the origin of the workspace fork branch \`${rawGitBranch}\`)`
326
+ : "";
327
+ log.info(`Applied settings for Git branch: ${currentBranch}${extraLog}`);
328
+ }
329
+ }
330
+ else if (currentBranch) {
331
+ log.debug(`No branch-specific overrides found for '${currentBranch}', using top-level settings`);
325
332
  }
326
333
  return effective;
327
334
  }
@@ -114,24 +114,35 @@ async function tryResolveWorkspace(opts) {
114
114
  error: colors.red.underline("No explicit workspace given."),
115
115
  };
116
116
  }
117
- export async function tryResolveBranchWorkspace(opts) {
118
- // Only try branch-based resolution if in a Git repository
119
- if (!isGitRepository()) {
120
- return undefined;
121
- }
122
- const rawBranch = getCurrentGitBranch();
123
- if (!rawBranch) {
124
- return undefined;
125
- }
117
+ export async function tryResolveBranchWorkspace(opts, branchOverride) {
118
+ let rawBranch = null;
126
119
  let currentBranch;
127
- const originalBranchIfForked = getOriginalBranchForWorkspaceForks(rawBranch);
128
- const workspaceIdIfForked = getWorkspaceIdForWorkspaceForkFromBranchName(rawBranch);
129
- if (originalBranchIfForked) {
130
- log.info(`Using original branch \`${originalBranchIfForked}\` for finding workspace profile from gitBranches section in wmill.yaml`);
131
- currentBranch = originalBranchIfForked;
120
+ let originalBranchIfForked = null;
121
+ let workspaceIdIfForked = null;
122
+ if (branchOverride) {
123
+ // Use branch override directly
124
+ currentBranch = branchOverride;
125
+ log.info(`Using branch override: ${branchOverride}`);
132
126
  }
133
127
  else {
134
- currentBranch = rawBranch;
128
+ // Only try branch-based resolution if in a Git repository
129
+ if (!isGitRepository()) {
130
+ return undefined;
131
+ }
132
+ rawBranch = getCurrentGitBranch();
133
+ if (!rawBranch) {
134
+ return undefined;
135
+ }
136
+ originalBranchIfForked = getOriginalBranchForWorkspaceForks(rawBranch);
137
+ workspaceIdIfForked =
138
+ getWorkspaceIdForWorkspaceForkFromBranchName(rawBranch);
139
+ if (originalBranchIfForked) {
140
+ log.info(`Using original branch \`${originalBranchIfForked}\` for finding workspace profile from gitBranches section in wmill.yaml`);
141
+ currentBranch = originalBranchIfForked;
142
+ }
143
+ else {
144
+ currentBranch = rawBranch;
145
+ }
135
146
  }
136
147
  // Read wmill.yaml to check for branch workspace configuration
137
148
  const config = await readConfigFile();
@@ -155,7 +166,7 @@ export async function tryResolveBranchWorkspace(opts) {
155
166
  const matchingProfiles = allProfiles.filter((w) => w.remote === normalizedBaseUrl && w.workspaceId === workspaceId);
156
167
  if (matchingProfiles.length === 0) {
157
168
  // No matching profile exists - prompt to create one
158
- return await createWorkspaceProfileInteractively(normalizedBaseUrl, workspaceId, currentBranch, opts, { rawBranch, isForked: !!originalBranchIfForked });
169
+ return await createWorkspaceProfileInteractively(normalizedBaseUrl, workspaceId, currentBranch, opts, { rawBranch: rawBranch ?? currentBranch, isForked: !!originalBranchIfForked });
159
170
  }
160
171
  // Handle multiple profiles - use special branch-aware logic
161
172
  let selectedProfile;
@@ -186,7 +197,7 @@ export async function tryResolveBranchWorkspace(opts) {
186
197
  }
187
198
  return selectedProfile;
188
199
  }
189
- export async function resolveWorkspace(opts) {
200
+ export async function resolveWorkspace(opts, branchOverride) {
190
201
  const cache = opts.__secret_workspace;
191
202
  if (cache)
192
203
  return cache;
@@ -239,12 +250,12 @@ export async function resolveWorkspace(opts) {
239
250
  return dntShim.Deno.exit(-1);
240
251
  }
241
252
  }
242
- const branch = getCurrentGitBranch();
253
+ const branch = branchOverride ?? getCurrentGitBranch();
243
254
  // Try explicit workspace flag first (should override branch-based resolution). Unless it's a
244
- // forked workspace, that we detect through the branch name
255
+ // forked workspace, that we detect through the branch name (only when not using branchOverride)
245
256
  const res = await tryResolveWorkspace(opts);
246
257
  if (!res.isError) {
247
- if (!branch || !branch.startsWith(WM_FORK_PREFIX)) {
258
+ if (branchOverride || !branch || !branch.startsWith(WM_FORK_PREFIX)) {
248
259
  return res.value;
249
260
  }
250
261
  else {
@@ -252,12 +263,13 @@ export async function resolveWorkspace(opts) {
252
263
  }
253
264
  }
254
265
  // Try branch-based resolution (medium priority)
255
- const branchWorkspace = await tryResolveBranchWorkspace(opts);
266
+ const branchWorkspace = await tryResolveBranchWorkspace(opts, branchOverride);
256
267
  if (branchWorkspace) {
257
268
  opts.__secret_workspace = branchWorkspace;
258
269
  return branchWorkspace;
259
270
  }
260
- else {
271
+ else if (!branchOverride) {
272
+ // Only check for fork errors when not using branchOverride
261
273
  const originalBranch = getOriginalBranchForWorkspaceForks(branch);
262
274
  if (originalBranch) {
263
275
  log.error(colors.red.bold(`Failed to resolve workspace profile for workspace fork. This most likely means that the original branch \`${originalBranch}\` where \`${branch}\` is originally forked from, is not setup in the wmill.yaml. You need to update the \`gitBranches\` section for \`${originalBranch}\` to include workspaceId and baseUrl.`));
@@ -44,11 +44,18 @@ function buildYamlTypePattern() {
44
44
  * Get the specific items configuration for the current git branch
45
45
  * Merges commonSpecificItems with branch-specific specificItems
46
46
  */
47
- export function getSpecificItemsForCurrentBranch(config) {
48
- if (!isGitRepository() || !config.gitBranches) {
47
+ export function getSpecificItemsForCurrentBranch(config, branchOverride) {
48
+ if (!config.gitBranches) {
49
49
  return undefined;
50
50
  }
51
- const currentBranch = getCurrentGitBranch();
51
+ // Use branch override if provided, otherwise detect from git
52
+ let currentBranch = null;
53
+ if (branchOverride) {
54
+ currentBranch = branchOverride;
55
+ }
56
+ else if (isGitRepository()) {
57
+ currentBranch = getCurrentGitBranch();
58
+ }
52
59
  if (!currentBranch) {
53
60
  return undefined;
54
61
  }
@@ -173,11 +180,18 @@ export function fromBranchSpecificPath(branchSpecificPath, branchName) {
173
180
  /**
174
181
  * Get the branch-specific path for the current branch if the item should be branch-specific
175
182
  */
176
- export function getBranchSpecificPath(basePath, specificItems) {
177
- if (!isGitRepository() || !specificItems) {
183
+ export function getBranchSpecificPath(basePath, specificItems, branchOverride) {
184
+ if (!specificItems) {
178
185
  return undefined;
179
186
  }
180
- const currentBranch = getCurrentGitBranch();
187
+ // Use branch override if provided, otherwise detect from git
188
+ let currentBranch = null;
189
+ if (branchOverride) {
190
+ currentBranch = branchOverride;
191
+ }
192
+ else if (isGitRepository()) {
193
+ currentBranch = getCurrentGitBranch();
194
+ }
181
195
  if (!currentBranch) {
182
196
  return undefined;
183
197
  }
@@ -191,11 +205,15 @@ const branchPatternCache = new Map();
191
205
  /**
192
206
  * Check if a path is a branch-specific file for the current branch
193
207
  */
194
- export function isCurrentBranchFile(path) {
195
- if (!isGitRepository()) {
196
- return false;
208
+ export function isCurrentBranchFile(path, branchOverride) {
209
+ // Use branch override if provided, otherwise detect from git
210
+ let currentBranch = null;
211
+ if (branchOverride) {
212
+ currentBranch = branchOverride;
213
+ }
214
+ else if (isGitRepository()) {
215
+ currentBranch = getCurrentGitBranch();
197
216
  }
198
- const currentBranch = getCurrentGitBranch();
199
217
  if (!currentBranch) {
200
218
  return false;
201
219
  }
package/esm/src/main.js CHANGED
@@ -40,7 +40,7 @@ export { flow, app, script, workspace, resource, resourceType, user, variable, h
40
40
  // console.error(JSON.stringify(event.error, null, 4));
41
41
  // }
42
42
  // });
43
- export const VERSION = "1.608.0";
43
+ export const VERSION = "1.610.1";
44
44
  // Re-exported from constants.ts to maintain backwards compatibility
45
45
  export { WM_FORK_PREFIX } from "./core/constants.js";
46
46
  const command = new Command()