Skip to content

detectShellProfile() falls back to ~/.bashrc on Windows, where the login shell never reads it #682

Description

@CarlosWonMore

On Windows SHELL is never set, so detectShellProfile() always falls back to ~/.bashrc. Git Bash — the shell Windows users actually run teamai's block under — starts as a login shell, and a login shell reads ~/.bash_profile, ~/.bash_login or ~/.profile, never ~/.bashrc. The block gets written, the file looks correct, and the variables are never loaded.

Filed separately at @jeff-r2026's request in #661.

Where

src/resources/env.ts:

private detectShellProfile(): string {
  const home = getUserHome();
  const shell = process.env.SHELL ?? '';

  if (shell.includes('zsh')) {
    return path.join(home, '.zshrc');
  }
  return path.join(home, '.bashrc');
}

SHELL is a POSIX convention. Windows defines it neither at the user nor the machine level, so the zsh branch is unreachable there and .bashrc is the only possible outcome.

Reproduction on Windows 11

$ node -e "console.log(JSON.stringify(process.env.SHELL))"
undefined

$ reg query HKCU\Environment | findstr /i shell
(no SHELL value)

After teamai pull with an env/env.yaml that has at least one variable, ~/.bashrc receives:

# [teamai:env:start]
# DO NOT EDIT: This section is auto-managed by teamai
[ -f "/c/Users/<user>/.teamai/env.sh" ] && source "/c/Users/<user>/.teamai/env.sh"
# [teamai:env:end]

while ~/.profile gets no block at all. The two shell types then diverge:

$ bash -lc 'echo ${MY_VAR:-<unset>}'
<unset>        # login shell — reads .bash_profile / .bash_login / .profile only

$ bash -c '. ~/.bashrc; echo ${MY_VAR:-<unset>}'
set            # the block itself is fine; it is simply never read

git-bash.exe, and Git Bash launched from Windows Terminal, pass --login, so the first case is the one users get.

Git for Windows tries to paper over this in /etc/profile.d/bash_profile.sh, but only when none of the three files exist:

if [ -e ~/.bashrc -a ! -e ~/.bash_profile -a ! -e ~/.bash_login -a ! -e ~/.profile ]; then
  # ...generates a ~/.bash_profile that sources ~/.profile and ~/.bashrc
fi

A ~/.profile is common — many installers create one, and ~/.local/bin/env adds a line to it. The moment it exists, Git for Windows does not create the .bash_profile that would have sourced .bashrc.

Why nothing catches it

doctor checks that the block is present in the profile file, and it is. Whether that file is one the user's shell actually reads is a different question, and once source is skipped there is no output at all — [ -f ... ] returns false and && short-circuits.

So the failure mode reads as success from every angle: the pull reports fine, the profile contains the block, and no variable is ever exported.

Suggested fix

Choose the profile the shell will actually read, per platform:

  • Windows — prefer an existing ~/.bash_profile, then ~/.bash_login, then ~/.profile; use ~/.bashrc only when none of them exists (which is exactly the case where Git for Windows generates a .bash_profile that sources it, so both choices work).
  • POSIX — keep the current SHELL-based behaviour.

If the file that ends up chosen is not one the current SHELL would read, a log.warn naming it would make the situation visible without changing the default.

Two things to keep in mind while fixing this:

  1. src/uninstall.ts carries a second, independent copy of detectShellProfile() (around line 133). It has to resolve to the same file, otherwise teamai uninstall leaves a block behind — or removes the wrong one.
  2. sharing.env.shellProfilePath overrides this path entirely and must keep taking precedence.

Environment

  • teamai-cli 0.24.0
  • Windows 11 Pro 25H2, Git for Windows 2.45.2 (git-bash.exe, launched with --login)
  • ~/.bashrc present, ~/.bash_profile absent, ~/.profile present (two lines, no teamai block)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions