mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 08:02:28 +00:00
fix(terminal): derive zsh wrapper ZDOTDIR from %x so non-ASCII WSL logins load .zshrc (#8003) (#8209)
The shell-ready zsh wrapper restored ZDOTDIR from the env-imported
`${ZDOTDIR}`. On Windows+WSL the wrappers are generated with a Windows
path baked in but sourced via /mnt/c, and for non-ASCII Windows
usernames (e.g. a Korean login) zsh corrupts environment values whose
UTF-8 bytes fall in its 0x84-0x9D token range while processing startup
files. The corrupted `${ZDOTDIR}` failed the self-check, so the wrapper
fell back to the unusable baked Windows literal and the user's ~/.zshrc
never loaded — a bare `HOSTNAME%` prompt with no theme/aliases/PATH.
Derive the wrapper dir from `${${(%):-%x}:h}` instead — %x is zsh's
internal script name for the file being sourced and is not subject to
the env-import corruption. `${ZDOTDIR}` is kept only as a fallback when
%x expansion yields nothing; the existing final restore still validates
with -f before trusting the value. On native macOS/Linux the derived
value equals the old one, so behavior there is unchanged.
Covers local PTYs, the daemon, and the Windows→WSL launch path, which
all share getZshEnvTemplate. Adds a live-zsh regression test that sources
the wrappers from a non-ASCII (token-range) runtime path.
Supersedes the ORCA_ORIG_ZDOTDIR path-normalization approach with a wrapper self-location fix that also covers the non-ASCII (token-range) corruption trigger.
Co-authored-by: OrcaWin <alpha-eng@stably.ai>
This commit is contained in:
@@ -734,7 +734,19 @@ describePosix('local PTY shell-ready launch config', () => {
|
||||
|
||||
const zshenv = readFileSync(join(userDataPath, 'shell-ready', 'zsh', '.zshenv'), 'utf8')
|
||||
|
||||
expect(zshenv).toContain('_orca_wrapper_zdotdir_self="${ZDOTDIR:-}"')
|
||||
// Why: derive the wrapper dir from %x (zsh's internal script name), not the
|
||||
// env-imported $ZDOTDIR — zsh corrupts environment values whose UTF-8 bytes
|
||||
// fall in its 0x84-0x9D token range (non-ASCII Windows usernames), which
|
||||
// would fail the self-check and fall back to the unusable baked literal.
|
||||
expect(zshenv).toContain('_orca_wrapper_zdotdir_self="${${(%):-%x}:h}"')
|
||||
// Keep $ZDOTDIR only as a fallback when %x expansion yields nothing. The
|
||||
// final restore below re-validates with -f before trusting the value, so no
|
||||
// stat is needed here.
|
||||
expect(zshenv).toContain(
|
||||
'if [[ -z "${_orca_wrapper_zdotdir_self:-}" ]]; then\n' +
|
||||
' _orca_wrapper_zdotdir_self="${ZDOTDIR:-}"\n' +
|
||||
'fi'
|
||||
)
|
||||
// The runtime path is only trusted when it still holds a wrapper .zshenv;
|
||||
// otherwise the generation-time literal remains as the fallback.
|
||||
expect(zshenv).toContain(
|
||||
@@ -745,7 +757,7 @@ describePosix('local PTY shell-ready launch config', () => {
|
||||
'fi'
|
||||
)
|
||||
// Capture must happen before the wrapper unsets ZDOTDIR to source user files.
|
||||
expect(zshenv.indexOf('_orca_wrapper_zdotdir_self="${ZDOTDIR:-}"')).toBeLessThan(
|
||||
expect(zshenv.indexOf('_orca_wrapper_zdotdir_self="${${(%):-%x}:h}"')).toBeLessThan(
|
||||
zshenv.indexOf('unset ZDOTDIR')
|
||||
)
|
||||
})
|
||||
@@ -892,6 +904,52 @@ path=(/custom/bin $path)
|
||||
}
|
||||
})
|
||||
|
||||
it('loads user .zshrc when the wrapper dir contains a non-ASCII (token-range) path', async () => {
|
||||
// Why: issue #8003 second trigger — a non-ASCII Windows username (e.g. a
|
||||
// Korean login) puts UTF-8 bytes in zsh's 0x84-0x9D token range into the
|
||||
// wrapper path. zsh corrupts the env-imported $ZDOTDIR while processing
|
||||
// startup files, so deriving the wrapper dir from $ZDOTDIR fails the
|
||||
// self-check and falls back to the unusable baked literal, leaving the
|
||||
// user's .zshrc unloaded. Deriving from %x sidesteps the corruption.
|
||||
writeFileSync(join(testHome, '.zshrc'), 'export USER_ZSHRC_LOADED=yes\n')
|
||||
|
||||
const { getShellReadyLaunchConfig } = await importFreshLocalPtyShellReady()
|
||||
getShellReadyLaunchConfig('/bin/zsh')
|
||||
|
||||
// Move the generated wrappers under a non-ASCII runtime root so the baked
|
||||
// literal is unusable and the runtime $ZDOTDIR gets corrupted on import.
|
||||
const nonAsciiUserData = join(dirname(userDataPath), '홍길동-wsl-view')
|
||||
renameSync(userDataPath, nonAsciiUserData)
|
||||
try {
|
||||
const cleanEnv: Record<string, string | undefined> = {
|
||||
...process.env,
|
||||
HOME: testHome,
|
||||
PATH: '/usr/bin:/bin'
|
||||
}
|
||||
delete cleanEnv.ZDOTDIR
|
||||
delete cleanEnv.ORCA_ORIG_ZDOTDIR
|
||||
delete cleanEnv.ORCA_ATTRIBUTION_SHIM_DIR
|
||||
delete cleanEnv.USER_ZSHRC_LOADED
|
||||
cleanEnv.ZDOTDIR = join(nonAsciiUserData, 'shell-ready', 'zsh')
|
||||
|
||||
for (const args of [['-i'], ['-l', '-i']] as const) {
|
||||
const result = spawnSync(
|
||||
'zsh',
|
||||
[...args, '-c', 'echo "USER_ZSHRC_LOADED=${USER_ZSHRC_LOADED:-no}"'],
|
||||
{
|
||||
env: cleanEnv as NodeJS.ProcessEnv,
|
||||
encoding: 'utf8'
|
||||
}
|
||||
)
|
||||
|
||||
expect(result.status, `zsh ${args.join(' ')} failed: ${result.stderr}`).toBe(0)
|
||||
expect(result.stdout).toContain('USER_ZSHRC_LOADED=yes')
|
||||
}
|
||||
} finally {
|
||||
rmSync(nonAsciiUserData, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('preserves top-level .zshenv path and function side effects', async () => {
|
||||
// Why: .zshenv is the normal place for always-on zsh env/path setup.
|
||||
// Dropping those side effects regresses non-Orca zsh startup semantics.
|
||||
|
||||
@@ -13,7 +13,17 @@ export function getZshEnvTemplate(zshDir: string, headerPrefix = ''): string {
|
||||
# Why: capture the runtime wrapper dir before it is unset below. On WSL this
|
||||
# file is generated with a Windows path but sourced via /mnt/c, so the baked
|
||||
# literal is unusable there and ZDOTDIR must be restored from this value.
|
||||
_orca_wrapper_zdotdir_self="\${ZDOTDIR:-}"
|
||||
# Derive it from the file being sourced (%x, zsh's internal script name) rather
|
||||
# than the env-imported $ZDOTDIR: zsh corrupts environment values whose UTF-8
|
||||
# bytes fall in its 0x84-0x9D token range (e.g. a non-ASCII Windows username
|
||||
# such as a Korean login), which would make the self-check below fail and fall
|
||||
# back to the unusable baked literal, so the user's .zshrc never loads (#8003).
|
||||
# %x is not subject to that corruption; keep $ZDOTDIR as a fallback for the
|
||||
# rare shell where %x prompt expansion yields nothing.
|
||||
_orca_wrapper_zdotdir_self="\${\${(%):-%x}:h}"
|
||||
if [[ -z "\${_orca_wrapper_zdotdir_self:-}" ]]; then
|
||||
_orca_wrapper_zdotdir_self="\${ZDOTDIR:-}"
|
||||
fi
|
||||
while [[ "\${_orca_wrapper_zdotdir_self:-}" == */ ]]; do
|
||||
_orca_wrapper_zdotdir_self="\${_orca_wrapper_zdotdir_self%/}"
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user