Skip to content

feat: support configurable CLI global config#1011

Open
EhabY wants to merge 4 commits into
mainfrom
feat/global-config
Open

feat: support configurable CLI global config#1011
EhabY wants to merge 4 commits into
mainfrom
feat/global-config

Conversation

@EhabY

@EhabY EhabY commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds coder.globalConfig as a machine-scoped directory setting for the CLI --global-config path, with CODER_CONFIG_DIR fallback and existing per-deployment storage as the default.
  • Applies the resolved config directory consistently through PathResolver, CLI auth flags, credential file reads/writes/deletes, active remote reload prompts, and support bundle settings.
  • Preserves keyring precedence while allowing file-backed CLI credentials to be reused when keyring auth is not active.

Closes #185.

Testing

  • pnpm test:extension ./test/unit/cliConfig.test.ts ./test/unit/core/pathResolver.test.ts ./test/unit/core/cliCredentialManager.test.ts ./test/unit/login/loginCoordinator.test.ts ./test/unit/supportBundle/settings.test.ts
  • pnpm test
  • pnpm typecheck
  • pnpm lint
  • pnpm format:check
  • pnpm build
  • git diff --check

Generated by Coder Agents.

Implementation plan
  • Add a dedicated coder.globalConfig setting for a directory path and keep coder.globalFlags from overriding managed --global-config / --use-keyring flags.
  • Resolve global config path in PathResolver: coder.globalConfig first, then CODER_CONFIG_DIR, then the existing <globalStorage>/<safeHostname> default.
  • Preserve keyring precedence in CLI auth resolution; use --url for active supported keyring auth, otherwise --global-config <resolvedDir>.
  • Read file-backed CLI credentials from the resolved config directory when keyring auth is not active so CLI login can be shared with the extension.
  • Add reload prompt/support bundle handling for the setting.
  • Cover behavior with targeted unit tests for path resolution, CLI flags, credential manager, login fallback, and support bundle settings.

@EhabY EhabY self-assigned this Jun 17, 2026
@EhabY EhabY requested a review from code-asher June 17, 2026 12:46
Comment thread src/core/cliCredentialManager.ts Outdated
* Throws AbortError when the signal is aborted.
* Read a token from CLI-managed credentials. Uses `coder login token --url`
* when keyring auth is active, otherwise reads the file credentials under
* --global-config. Returns undefined on any failure (resolver, CLI, empty

@code-asher code-asher Jun 17, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use coder login token for both paths? It works for file-based storage as well. We will have call it with the global flags of course so it reads the right files.

This would also have the advantage of being able to get the token from the default config location, since right now we require the user to fill out a separate variable. nvm idk why I said that, we do still read credentials from the default location with the current setup.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coder login token was only added in 2.31.0 (January of this year), we still need handle the older deployments and thus we need to read the file system

@EhabY EhabY Jun 18, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do something like this:

  • Is binary resolvable and >= 2.31?
    • Use coder login token for both paths (delegates format knowledge to the CLI).
    • Otherwise keep the direct file read.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is reasonable to scope the feature to 2.31.0 personally, unless product has asked us to make older versions work?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're keeping the direct file read to support deployments older than 2.31.0, where coder login token doesn't exist. Even scoped to 2.31+, shelling out to read the url/session files the extension wrote is more work (binary resolution + process spawn).

I am confused by the "scope to 2.31.0" since this would affect all reads and not just the when users have a custom global-config

@code-asher code-asher Jun 23, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shelling out to read the url/session files the extension wrote is more work

We should really be using coder login to write these as well IMO (this is how the Toolbox extension does it). As far as work, I suppose it is more overhead, but to me it feels like an anti-pattern for one tool to read/write the config of another tool, there is no public API or guarantee so we rely on internal knowledge of how the configs happen to work.

We're keeping the direct file read to support deployments older than 2.31.

If we have to do this, then we gotta do it. But if not, we could have a single code path and only support this token-sharing feature for 2.31.0 and above.

I am confused by the "scope to 2.31.0" since this would affect all reads and not just the when users have a custom global-config

Oh yeah this was unclear, I just meant scoping the token read feature, not the whole thing. So for versions lower than 2.31.0, readToken would always return undefined. In other words, if a user wants the plugin to read credentials created by the CLI, they would need to update to 2.31.0 or higher.

Actually, I guess we have two features mixed up in this PR, the global config, and also reading the token from the CLI when using file storage.

Comment thread src/core/cliCredentialManager.ts Outdated
Comment thread src/core/cliCredentialManager.ts Outdated
Comment thread src/core/pathResolver.ts
public getGlobalConfigDir(safeHostname: string): string {
return path.join(this.basePath, safeHostname);
return (
PathResolver.resolveOverride("coder.globalConfig", "CODER_CONFIG_DIR") ||

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of separate variables could we just have users add the flag to coder.globalFlags? Then we change the order so theirs takes precedence.

If we combine this with always running coder login token then we never need to actually know where the global dir is ourselves.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case all deployments would share the same location by default which is why we introduced this per-deployment storage and the internal use of the global-config

@code-asher code-asher Jun 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I follow, we should keep our current default. The simplest solution would be to just prepend our default so the user's takes precedence when the CLI parses the flags (at least, I am pretty sure when the flag is duplicated the last takes precedence).

We could also check for the flag and avoid adding our default if we wanted the flags to be a bit "cleaner" without repeats.

That does make me think though, I wonder if we should provide a variable so users can configure their own multi-deployment setup with different paths to avoid collisions. So they could do something like --global-config /path/to/my/global/configs/${env:CODER_URL} or something like that.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In file mode the extension reads/writes the url+session files itself (and defaults the binary cache under this dir), so it needs the path directly, a flag in coder.globalFlags only reaches CLI invocations, not our own fs calls, which is exactly why --global-config is stripped from user flags today.

We could potentially extract the global-config from the globalFlags but this means we need to parse arguments and deal with that headache. Also, we do actually support ${env:XXX} format here anyway!

@EhabY EhabY requested a review from code-asher June 18, 2026 10:00
@EhabY EhabY force-pushed the feat/global-config branch 2 times, most recently from 019b22d to 9a72ccf Compare June 22, 2026 13:42
EhabY and others added 3 commits June 23, 2026 09:58
Split src/util.ts into focused modules: src/util/uri.ts (toSafeHost,
removeTrailingSlashes, resolveUiUrl, openInBrowser) and
src/util/authority.ts (Remote SSH authority helpers), replacing
src/uri/utils.ts. Migrate all importers and mirror the unit tests.

Make CliCredentialManager.readToken return the token together with its
source ("keyring" | "files") so LoginCoordinator labels the login
method from the actual source instead of re-deriving it from whether
keyring is enabled.
@EhabY EhabY force-pushed the feat/global-config branch from 9a72ccf to 6d03c00 Compare June 23, 2026 06:59
…malization

- Add loginCoordinator test for the keyring_token method (source: "keyring")
- Add comprehensive toRemoteAuthority tests over varied URI types
- Test toSafeHost/normalizeUrl with Arabic alongside Japanese IDNs
- Consolidate the trim + strip-trailing-slashes logic into a shared
  normalizeUrl in util/uri, reused by resolveUiUrl and cliCredentialManager
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.

Add support for sharing login/auth between Coder CLI and VS Code extension

2 participants