WSL: linking Windows-side bins (ghr link / ghr unlink)
When ghr is installed on Windows, a parallel WSL distribution can expose the
same tools without re-downloading them. ghr link <id> resolves the exact
Windows install from ghr's inventory and creates Linux symlinks in
~/.local/bin for that ID's persisted command names. The links point directly
at the Windows-side .exe (via /mnt/c/...); WSL interop runs the binary
transparently.
# Link an executable already on the Windows %PATH% (no install needed).
ghr link --path az # → bash wrapper that runs az.cmd via cmd.exe
ghr unlink --path az
Notes:
ghr linkis a reconciler. Without--bin, it makes the WSL link set match the current Windows install — adding new bins, updating moved ones, and removing entries that disappeared fromghr.json. With--bin <name>filters, only the named entries are touched.- The symlink target is the real
.exeunder the inventory-selected unit, not the Windows shim. AC:\…target would not trigger interop; the WSL path is required. - Both commands require
WSL_INTEROPto be set. They refuse to run outside WSL so you don't accidentally create dangling links on bare Linux or macOS. - IDs are canonicalized to lowercase. Multiple IDs sourced from the same repository remain independent.
- Raw wasm modules are refused because direct symlinks to
.wasmbytes are not executable through WSL interop.
Bare executable form
A one-segment argument is ambiguous: an existing install ID or ID manifest
wins; otherwise ghr retains the historical Windows-PATH behavior. Use --id
or --path to select explicitly. PATH mode resolves the name via where.exe
(honouring PATHEXT), converts the result with wslpath -u, and writes an
entry in ghr's bin directory based on the resolved file's extension:
.exe/.com→ symlink. WSL interop direct-executes the PE image, no extra process hop..cmd/.bat-> small bash wrapper that runscmd.exe /c '<windows-path>' "$@"via WSL interop. This is howghr link --path azexposes Azure CLI'saz.cmd, for example..ps1(and anything else) → rejected. Running PowerShell scripts safely needs a different launcher and arg-quoting model; installpwshseparately if you want it.
Other guardrails:
- The resolved path must live under
/mnt/<letter>/(drvfs); UNC and/mnt/wsl/paths are rejected. --binis not supported with the bare form (there is exactly one bin to link).ghr unlink --path <name>removes the symlink or wrapper created by PATH mode. Wrappers are only deleted when their magic comment and embedded Windows target match what the manifest recorded, so a user-edited wrapper is left alone.- Automatic one-segment disambiguation requires canonical
%APPDATA%discovery. If discovery fails, ghr requires--pathorGHR_WIN_TOOLS_DIRinstead of guessing and possibly selecting the wrong ownership mode.
Recipe: Git Credential Manager from WSL
Use this when Git runs natively in WSL but credentials should be prompted for and stored by Windows Git Credential Manager.
In Windows PowerShell:
ghr install git-ecosystem/git-credential-manager
Inside WSL:
ghr link git-ecosystem/git-credential-manager
git config --global credential.helper manager
ghr link exposes the Windows git-credential-manager.exe as
git-credential-manager on the WSL PATH. Native WSL Git remains in use; when
Git needs credentials, it runs the linked Windows helper through WSL interop, so
the sign-in flow and credential storage happen on Windows.
Do not link the bare Windows Git executable for this setup: that would expose
Windows Git itself, not just the credential helper. Git authentication through
GCM does not require a separate keyring install.
For Azure Repos, also configure WSL Git to pass the organization path:
git config --global credential.https://dev.azure.com.useHttpPath true
To prefer Microsoft Entra OAuth tokens for Azure Repos, configure the Windows Git/GCM settings from Windows PowerShell:
git config --global credential.azreposCredentialType oauth
If ghr link cannot find the Windows install, see
Locating the Windows tools dir.
Recipe: uv with Azure DevOps feeds from WSL
Use this when uv runs in WSL but Azure DevOps authentication should reuse the
Windows Microsoft identity cache.
In Windows PowerShell:
ghr install cataggar/keyring
Inside WSL:
ghr link cataggar/keyring
ghr install astral-sh/uv
keyring -b ado diagnose
export UV_KEYRING_PROVIDER=subprocess
Use an Azure Artifacts index URL that includes the conventional
VssSessionToken username:
uv pip install \
--index-url "https://VssSessionToken@pkgs.dev.azure.com/<organization>/<project>/_packaging/<feed>/pypi/simple/" \
<package>
UV_KEYRING_PROVIDER=subprocess tells uv to invoke the keyring command.
Because ghr link cataggar/keyring points to the Windows keyring.exe, the ado
backend can reuse the Windows Microsoft identity cache and return the session
token to uv; the token is not written into the URL or config. This path does
not require Python artifacts-keyring or a .NET runtime inside WSL.
For troubleshooting, run keyring -b ado diagnose, then see
cataggar/keyring Azure DevOps
and uv keyring providers.
Locating the Windows tools dir
ghr link resolves the Windows-side tools dir in this order:
GHR_WIN_TOOLS_DIR— explicit override. Accepts either a WSL path (/mnt/c/Users/x/AppData/Roaming/ghr/data/tools) or a Windows path (C:\Users\x\AppData\Roaming\ghr\data\tools).cmd.exe /c echo %APPDATA%, run throughwslpath -u. This is the canonical lookup and handles non-default Windows usernames and redirected APPDATA.- For explicit ID operations, fallback to
/mnt/c/Users/$USER/AppData/Roaming/ghr/data/toolswith a warning, assuming the WSL username matches the Windows one. Automatic one-segment disambiguation does not use this heuristic.
Per-link manifest
For each linked ID, ghr records what it created at
$XDG_DATA_HOME/ghr/links/by-id/u-<segment>/.../_manifest.json (or under
~/.local/share). The prefixed segment encoding keeps IDs such as a and
a/b independent. Bare-executable links live in the sibling
by-path/<name>.json tree.
The manifest records the canonical ID and exact published command names.
ghr unlink verifies each live symlink still points where the manifest
recorded before deleting, so a user-rewritten symlink is never clobbered.
Validated legacy owner/repo manifests migrate lazily during a full
reconciliation and can still be unlinked after the Windows install disappears.
Recipe: Microsoft Authentication CLI from WSL
Use this when Microsoft Authentication CLI is installed on Windows and only the
azureauth binary should be exposed inside WSL.
In Windows PowerShell:
ghr install azuread/microsoft-authentication-cli@0.9.6 --bin azureauth
Inside WSL:
ghr link azuread/microsoft-authentication-cli --bin azureauth
The release archive also contains createdump.exe. Install-time --bin
selection keeps that command out of the Windows publication set, while
link-time --bin limits which already-owned Windows commands are exposed in
WSL.