microsoft / microsoft/apm

[BUG] apm install fails on Windows: shutil.which(str(directory / name)) never resolves git.exe (PATHEXT not applied)

Open Beginner friendly
#2,977 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area/cli help wanted status/accepted theme/portability triage/recommended type/bug
Dominant language
Python
Stars
3.8k
Forks
362
Avg merge
1d 17h
Merged PRs (30d)
132

Description

What happened

apm install/apm update fails on Windows with:

Failed to download dependency <owner>/<repo>: Failed to clone repository: git executable not found on PATH. Please install git: https://git-scm.com/downloads

...even when git is installed and resolvable on PATH (verified with git --version / (Get-Command git).Source in the same shell, in both Git Bash and native PowerShell).

Root cause

apm_cli/utils/git_env.py, in _resolve_trusted_executable():

candidate = shutil.which(str(directory / name))

On Windows, shutil.which() only appends PATHEXT extensions (.EXE, .CMD, …) when the given command has no directory component. Once you join a directory onto the name (str(directory / name)), shutil.which takes the "already a path" branch and checks the literal string for an exact, executable match — it never tries git.exe, git.EXE, etc. Since the file on disk is git.exe and never a bare git, the check always fails, for every directory on PATH, regardless of whether Git is installed.

Minimal repro (no APM involved):

import shutil
from pathlib import Path

d = Path(r"C:\Program Files\Git\cmd")
shutil.which(str(d / "git"))        # -> None  (this is the current code's call shape)
shutil.which("git", path=str(d))    # -> 'C:\Program Files\Git\cmd\git.EXE'  (correct)

Same failure mode affects get_gh_executable() (gh resolution) for the same reason.

Fix

Pass the command name and directory separately so shutil.which's Windows PATHEXT handling actually runs:

# apm_cli/utils/git_env.py, _resolve_trusted_executable()
- candidate = shutil.which(str(directory / name))
+ candidate = shutil.which(name, path=str(directory))

Verified this one-line change resolves git/gh correctly via the existing per-directory loop, with the existing trusted-directory exclusion logic (_executable_exclusion_root) untouched.

Environment

  • apm-cli 0.30.0 (pip install apm-cli)
  • Windows 11, git 2.43.0.windows.1 (C:\Program Files\Git\cmd\git.exe), gh CLI installed
  • Reproduced identically in Git Bash and native powershell.exe

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in apm_cli/utils/git_env.py at _resolve_trusted_executable(), then inspect the existing per-directory resolution loop and its callers for git and gh. Verify on Windows that installed git and gh executables resolve through PATH directories while the trusted-directory exclusion logic remains unchanged, and run the relevant existing tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
git, python
Domain
cli, devtools
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.