Skip to content

fix(electron): inherit full login shell environment instead of PATH only#62

Open
FourWindff wants to merge 2 commits intojohannesjo:mainfrom
FourWindff:fix/appimage-env-vars
Open

fix(electron): inherit full login shell environment instead of PATH only#62
FourWindff wants to merge 2 commits intojohannesjo:mainfrom
FourWindff:fix/appimage-env-vars

Conversation

@FourWindff
Copy link
Copy Markdown

Description

When launched from a .desktop file or AppImage, the app previously only inherited PATH from the user's login shell via fixPath(). All other environment variables were lost, which caused two issues:

  1. Missing environment variables in PTYs — Environment variables like GOPATH, JAVA_HOME, SSH_AUTH_SOCK, and NVM_DIR were not available in spawned PTYs, causing tools that depend on them to fail.
  2. Claude unusable with third-party API keys — Users who access Claude through a third-party API provider need custom environment variables (e.g. ANTHROPIC_BASE_URL) configured in their shell profile. Since only PATH was inherited, these variables were missing and Claude could not connect at all.

This PR replaces fixPath() with fixEnv(), which runs env -0 inside an interactive login shell to capture all environment variables (null-byte separated), then applies them to process.env. This ensures spawned PTYs have the same complete environment as the user's terminal.

Issues Resolved

  • AppImage / .desktop launcher only inherited PATH from the login shell; all other environment variables were lost in spawned PTYs.
  • Claude was unusable when configured with third-party API keys, because custom environment variables (e.g. ANTHROPIC_BASE_URL) were not passed through to the PTY.

…tion validation

Introduced a new function to check for the existence of local branches. Enhanced the worktree creation process by validating the start-point reference and handling cases for empty repositories and non-existent branches more gracefully.
When launched from a .desktop file or AppImage, the app previously only
inherited PATH from the user's login shell. Other environment variables
(e.g. GOPATH, JAVA_HOME, SSH_AUTH_SOCK, NVM_DIR) were missing, causing
tools that depend on them to malfunction inside spawned PTYs.

Replace fixPath() with fixEnv() which runs `env -0` in an interactive
login shell to capture all environment variables, then applies them to
process.env via null-byte splitting.

Made-with: Cursor
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates Electron startup to inherit a complete login-shell environment (not just PATH), and adds extra git-side safeguards around main-branch detection and worktree creation.

Changes:

  • Replace fixPath() with fixEnv() to import all environment variables from an interactive login shell into process.env.
  • Enhance default-branch detection by checking local branch refs when remote-tracking refs aren’t available.
  • Add pre-validation and clearer failures when creating git worktrees from missing/invalid start refs or empty repos.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
electron/main.ts Captures full login-shell environment via env -0 and applies it to process.env.
electron/ipc/git.ts Adds local-branch fallback for main-branch detection and validates worktree start refs with improved errors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 23 to +26
// welcome messages). Login-only (-lc) would be quieter but would miss tools
// that are only added to PATH in .bashrc/.zshrc (e.g. nvm). We accept the
// side effects since the sentinel-based parsing discards all other output.
function fixPath(): void {
function fixEnv(): void {
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The comment above this function still describes resolving only PATH and using sentinel-based parsing to isolate PATH output, but the implementation now captures and applies the full environment via env -0. Please update the comment to match the new behavior (and any updated trade-offs).

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +33
const result = execFileSync(
loginShell,
['-ilc', `printf '${sentinel}' && env -0 && printf '${sentinel}'`],
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

env -0 is a GNU extension and isn’t available on macOS’s default /usr/bin/env (BSD). Since fixEnv() runs on all non-Windows platforms, this will likely throw on darwin and silently skip inheriting env/PATH. Consider either gating this logic to Linux, or adding a fallback that doesn’t require env -0 (or uses a different portable strategy on macOS).

Suggested change
const result = execFileSync(
loginShell,
['-ilc', `printf '${sentinel}' && env -0 && printf '${sentinel}'`],
const envDumpCommand =
process.platform === 'darwin'
? `/usr/bin/perl -e 'while (($k, $v) = each %ENV) { print "${k}=${v}\\0" }'`
: 'env -0';
const result = execFileSync(
loginShell,
['-ilc', `printf '${sentinel}' && ${envDumpCommand} && printf '${sentinel}'`],

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +35
const result = execFileSync(
loginShell,
['-ilc', `printf '${sentinel}' && env -0 && printf '${sentinel}'`],
{ encoding: 'utf8', timeout: 5000 },
);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

execFileSync defaults to a ~1MB maxBuffer. Dumping the full login-shell environment (especially with large vars like certificates, tokens, kubeconfig, etc.) can exceed this and cause the call to throw, leaving process.env unchanged. Consider setting an explicit, larger maxBuffer (similar to MAX_BUFFER used in electron/ipc/git.ts) or handling this failure mode more explicitly.

Copilot uses AI. Check for mistakes.
Comment on lines +416 to +423
const isEmptyRepo = await exec('git', ['rev-list', '-n1', '--all'], { cwd: repoRoot })
.then(({ stdout }) => !stdout.trim())
.catch(() => true);
if (isEmptyRepo) {
throw new Error(
'Cannot create a worktree in a repository with no commits. ' +
'Please make an initial commit first.',
);
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

isEmptyRepo currently treats any failure of git rev-list -n1 --all as an empty repo (catch(() => true)). If repoRoot isn’t a git repo (or git fails for another reason), this will throw the “no commits” error, which is misleading and hides the real problem. Consider distinguishing “not a git repository” (or other exec errors) from a truly empty repo, e.g. by checking isGitRepo(repoRoot) or by inspecting stderr/exit status before deciding it’s empty.

Copilot uses AI. Check for mistakes.
Comment on lines +411 to +429
// Validate the start-point ref exists before attempting worktree creation
const startRef = baseBranch || 'HEAD';
try {
await exec('git', ['rev-parse', '--verify', startRef], { cwd: repoRoot });
} catch {
const isEmptyRepo = await exec('git', ['rev-list', '-n1', '--all'], { cwd: repoRoot })
.then(({ stdout }) => !stdout.trim())
.catch(() => true);
if (isEmptyRepo) {
throw new Error(
'Cannot create a worktree in a repository with no commits. ' +
'Please make an initial commit first.',
);
}
throw new Error(
`Branch "${startRef}" does not exist. ` +
'Please select a valid base branch or create the branch first.',
);
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

New behavior added here (validating startRef via rev-parse --verify and throwing user-facing errors for empty repos / missing base branches) isn’t covered by tests. Since electron/ipc/git.test.ts exists, it would be good to add unit tests that mock the git calls and assert the correct error is thrown for (1) empty repo and (2) nonexistent baseBranch.

Copilot uses AI. Check for mistakes.
Comment on lines +411 to +414
// Validate the start-point ref exists before attempting worktree creation
const startRef = baseBranch || 'HEAD';
try {
await exec('git', ['rev-parse', '--verify', startRef], { cwd: repoRoot });
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

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

The PR title/description focus on inheriting the full login-shell environment for Electron, but this file also adds new git behaviors (main branch detection changes and worktree start-ref validation with new user-facing errors). Please either update the PR description to cover these git changes (and why they’re included) or split them into a separate PR to keep scope clear.

Copilot uses AI. Check for mistakes.
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.

2 participants