Skip to content

fix: read the workspaces key from wmill.yaml - #43

Merged
rubenfiszel merged 6 commits into
mainfrom
fix/vscode-workspaces-key
Aug 5, 2026
Merged

rubenfiszel merged 6 commits into
mainfrom
fix/vscode-workspaces-key

Conversation

@rubenfiszel

@rubenfiszel rubenfiszel commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Reported by a customer: after moving their wmill.yaml to the new workspaces key, the extension stopped picking up their workspace, logging No gitBranches configuration found in wmill.yaml and staying on whichever workspace the CLI had active.

workspaces:
  cm:
    baseUrl: https://windmill.pagtech.net/
    gitBranch: main
    workspaceId: cm

The bug

The extension read exactly one key, config["gitBranches"], and looked the branch up as a map key. Both are wrong against today's CLI (cli/src/core/conf.ts):

  • Key name — the primary key is workspaces; gitBranches / environments / git_branches are deprecated aliases the CLI normalizes in readConfigFile. The extension only knew the first alias, so the config was invisible.
  • Lookup — in the new format the key is the workspace name; the branch is entry.gitBranch ?? name. Even via the legacy key, config["main"] would have missed an entry named cm.
  • workspaceId now defaults to the workspace name (getEffectiveWorkspaceId); the extension required it explicitly.
  • Remote comparison normalized only the config side, so a remote entered without a trailing slash never matched.

I verified the published 0.2.60 build has the same code — the exact string the customer saw is in the shipped bundle, so nothing was lost in an unreleased change.

Also in here

Fork branches. On wm-fork/<base>/<id> the CLI targets the fork workspace wm-fork-<id>, resolved from the branch name; wmill.yaml is consulted only for the base branch's entry, which supplies the remote, and auth is reused from that workspace's profile. The CLI never persists a fork profile (it derives one in memory per command, core/context.ts), so there's nothing for syncVSCodeConfigFromCLI to pull in — the extension registers a derived entry itself, named <parent>/wm-fork-<id>, re-added after each sync since additionalWorkspaces is rewritten wholesale.

Ambiguous profiles. When several profiles share a remote and workspace id — the same workspace as different identities — the extension silently took the first. It now reads the choice the CLI remembered in branch-profiles.json. It deliberately does not prompt when there's no remembered choice: this runs from the .git/HEAD watcher and on activation, so a QuickPick would appear because you ran git checkout. It falls back to the first match and names it in the notification; Windmill: Switch workspace remains for choosing deliberately.

Stale config. The workspaces config was only reloaded when the active editor changed, so editing wmill.yaml had no effect until you switched files — plausibly the next thing to hit anyone who has just corrected that file. Now watched.

findCodebase returned undefined at the first codebase that didn't match, making every codebase after the first unreachable. The CLI's own findCodebase continues the loop; this now does too. Found while covering the module — the regression test fails against the previous behaviour.

Testing

161 tests (from 46). tsc --noEmit clean, npm run lint 0 errors with no new warnings, and both bundles build (esbuild node + webpack web).

Statement coverage of the touched modules went from 73% to 99% (100% functions, 95% branches):

module statements branches
config/config-manager.ts 100% 100%
config/branch-profiles.ts 100% 87%
utils/git-utils.ts 100% 100%
workspace/workspace-manager.ts 99% 92%

Git detection and config loading go through the vscode filesystem API rather than node's, so the test stub grew an in-memory filesystem, open-document set and workspace folders. syncVSCodeConfigFromCLI does use node fs, so it runs against a real temporary config dir laid out the way the CLI writes it — no fs mocking needed.

Covered: config key precedence across all three deprecated aliases; branch matching via gitBranch and via the name; switchWorkspaceForBranch end to end (profile match/non-match, workspaceId defaulting, trailing-slash and unparseable-URL normalization, missing baseUrl, already-on-target no-op, synthetic main, failed settings write); the fork cases (derived entry contents, re-registration after a sync wipe, parent token rotation, unconfigured base branch, malformed fork branch not falling through to the parent); the ambiguity cases (remembered choice honored, configFolder passed through, fallbacks, fork inheriting the chosen identity's token); .git/HEAD parsing including worktree gitdir indirection both absolute and relative, detached HEAD, and unreadable HEAD; the root-first wmill.yaml search and its precedence, plus unsaved editor buffers winning over disk; the CLI sync and its failure modes; checkAndSwitchWorkspaceForGitBranch including a wmill.yaml that isn't valid YAML; the web-environment guards that keep the webpack build from touching node fs; and the status bar.

The branch-profiles.json location (<configDir>/0/, from the CLI's getStore("")) is exercised against a real file written in the CLI's layout, and was additionally verified against the actual file the CLI wrote on my machine.

Three defects the tests caught and I fixed rather than shipped: the two fork-branch helpers disagreed on wm-fork/main/ (empty id), which would have silently targeted the parent workspace — they now derive from one parser; a failed CLI-config read aborted the whole switch through the outer catch, now best-effort; and the findCodebase scan above.

Uncovered by design: workspace-manager.ts:233-234, a "no workspace folder" guard only reachable after getCurrentGitBranch already succeeded — which itself requires a workspace folder; and the default-config-dir branch of getLastUsedProfile, which would read the developer's real ~/.config/windmill.

🤖 Generated with Claude Code

rubenfiszel and others added 6 commits August 5, 2026 16:07
The extension only ever read `gitBranches` and looked branches up as map
keys. The CLI's primary key is now `workspaces`, where the key is the
workspace name and the branch comes from `gitBranch ?? name`, so a config
like `workspaces: { cm: { gitBranch: main, workspaceId: cm } }` was
invisible to the extension and no workspace switch happened.

Mirror the CLI resolution (windmill/cli/src/core/conf.ts):
- read `workspaces`, falling back to the deprecated `gitBranches`,
  `environments` and `git_branches` keys in the CLI's priority order
- resolve the entry by effective git branch, skipping the reserved
  `commonSpecificItems` key
- default `workspaceId` to the workspace name
- normalize both sides of the remote comparison via `new URL()`, the same
  normalization the CLI applies when storing workspace profiles

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
On a `wm-fork/<base>/<id>` branch the CLI targets the fork workspace
`wm-fork-<id>`, resolved from the branch name itself; wmill.yaml is only
consulted for the base branch's entry, which supplies the remote, and the
auth is reused from that workspace's saved profile. Mirror that.

The CLI never persists a profile for a fork (it derives one in memory per
command, see core/context.ts), so nothing gets synced into
`additionalWorkspaces` for the extension to switch to. Register a derived
entry instead — parent remote and token, fork workspace id, named
`<parent>/wm-fork-<id>`. `syncVSCodeConfigFromCLI` rewrites
`additionalWorkspaces` wholesale on every sync, so this re-runs after each
one, which the existing call order already guarantees.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The existing tests only exercised pure functions; the decision logic in
switchWorkspaceForBranch — profile matching, remote normalization, fork
entry registration, the already-on-target early return — was untested,
which is where the reported bug actually lived.

Back the vscode stub's configuration with a mutable store so tests can
drive the settings the extension reads and writes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two behaviours found while testing the branch switch.

When several profiles share a remote and workspace id — the same workspace
reached as different identities — the extension silently took the first.
The CLI prompts once and remembers the answer in branch-profiles.json
(core/branch-profiles.ts), so read that and follow it. Don't prompt when
there's no remembered choice: this runs from the .git/HEAD watcher and on
activation, so a QuickPick would appear because you ran `git checkout`, and
`Windmill: Switch workspace` already exists for choosing deliberately.
Fall back to the first match and name it in the notification instead.

The workspaces config was only reloaded when the active editor changed, so
editing wmill.yaml had no effect until you switched files — likely the next
thing to hit anyone who has just corrected that file. Watch it and drop the
cache.

Reading the CLI config is best-effort: a failure there falls back to the
first match rather than aborting the switch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
findCodebase returned undefined as soon as the first codebase in the list
did not match, so with several codebases configured only the first was
ever reachable — a file belonging to the second got no codebase and so no
bundling. The CLI's own findCodebase (commands/sync/sync.ts) continues the
loop; do the same.

Found while covering config-manager; the regression test fails against the
previous behaviour.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fills the three gaps called out in the PR. Both git detection and config
loading go through the vscode filesystem API rather than node's, so the
stub grows an in-memory filesystem, open-document set and workspace
folders; syncVSCodeConfigFromCLI does use node fs, so it runs against a
real temporary config dir laid out the way the CLI writes it.

Covers .git/HEAD parsing including worktree gitdir indirection (absolute
and relative) and detached HEAD; the root-first wmill.yaml search and its
precedence; unsaved editor buffers winning over disk; the CLI sync and its
failure modes; getCurrentWorkspaceConfig and the synthetic main workspace;
checkAndSwitchWorkspaceForGitBranch including a wmill.yaml that is not
valid YAML; the web-environment guards that keep the webpack build from
touching node fs; and the status bar.

Statement coverage of the touched modules goes from 73% to 99%.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rubenfiszel
rubenfiszel merged commit 5ddb15d into main Aug 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant