npm packages specify git dependencies using SSH URLs for GitHub authentication. Inside the proot environment, there is no SSH agent, no SSH keys, and no way to authenticate to GitHub over SSH. All git operations must use HTTPS (for public repos) or HTTPS with a token (for private repos).
The initial git wrapper used a single bash string substitution: ${URL/git@github.com:/https://github.com/}. This handled only the git@github.com:user/repo SCP-style format. In practice, npm and its transitive dependencies use at least three distinct URL formats:
git+ssh://git@github.com/user/repo.git -- the most common format in package.jsonssh://git@github.com/user/repo.git -- used by some older packagesgit@github.com:user/repo.git -- SCP-style, used by git submodules and some dependenciesA single sed command with multiple -e expressions handles all three in one pass. This is a LEGACY hack, but the URL rewriting logic was carried forward into the native migration's git configuration.
The sed rewrite function, used inside the git wrapper (see hack 4 for the full argument parser):
#!/data/data/com.termux/files/usr/bin/bash
# Git wrapper — URL rewriting via sed
# Deployed to: $ROOTFS/usr/bin/git
REAL_GIT="/termux-prefix/bin/git"
rewrite_url() {
echo "$1" | sed \
-e 's|^git+ssh://git@github.com/|https://github.com/|' \
-e 's|^git+ssh://git@github\.com:|https://github.com/|' \
-e 's|^ssh://git@github.com/|https://github.com/|' \
-e 's|^git@github\.com:|https://github.com/|'
}The four sed expressions cover all observed patterns, including the rare variant where git+ssh:// uses a colon instead of a slash after the hostname.
For private repos, inject a GitHub personal access token into the URL:
rewrite_url_with_auth() {
local url="$1"
local token="${GITHUB_TOKEN:-}"
local rewritten
rewritten=$(echo "$url" | sed \
-e 's|^git+ssh://git@github.com/|https://github.com/|' \
-e 's|^git+ssh://git@github\.com:|https://github.com/|' \
-e 's|^ssh://git@github.com/|https://github.com/|' \
-e 's|^git@github\.com:|https://github.com/|')
if [ -n "$token" ]; then
rewritten=$(echo "$rewritten" | sed "s|https://github.com/|https://$token@github.com/|")
fi
echo "$rewritten"
}Integrated into the full wrapper with argument parsing (hack 4):
#!/data/data/com.termux/files/usr/bin/bash
REAL_GIT="/termux-prefix/bin/git"
if [ "$1" = "clone" ]; then
shift
URL="" ; DEST="" ; FLAGS=()
while [ $# -gt 0 ]; do
case "$1" in
--depth|--branch|-b|-c|--reference|--origin|-o)
FLAGS+=("$1" "$2"); shift 2 ;;
--mirror|--bare|--recursive|--no-checkout)
FLAGS+=("$1"); shift ;;
-*) FLAGS+=("$1"); shift ;;
*) if [ -z "$URL" ]; then URL="$1"; elif [ -z "$DEST" ]; then DEST="$1"; fi; shift ;;
esac
done
# Rewrite all SSH URL formats to HTTPS
URL=$(echo "$URL" | sed \
-e 's|^git+ssh://git@github.com/|https://github.com/|' \
-e 's|^git+ssh://git@github\.com:|https://github.com/|' \
-e 's|^ssh://git@github.com/|https://github.com/|' \
-e 's|^git@github\.com:|https://github.com/|')
if [ -n "$DEST" ]; then
exec "$REAL_GIT" clone "${FLAGS[@]}" "$URL" "$DEST"
else
exec "$REAL_GIT" clone "${FLAGS[@]}" "$URL"
fi
else
exec "$REAL_GIT" "$@"
fiDeploy into the proot rootfs:
ROOTFS="$PREFIX/var/lib/proot-distro/installed-rootfs/ubuntu"
# Write the script (use scp or cat > from Termux)
chmod +x "$ROOTFS/usr/bin/git"Test each URL format directly:
# From inside proot, or test the function in isolation
rewrite_url "git+ssh://git@github.com/user/repo.git"
# Output: https://github.com/user/repo.git
rewrite_url "ssh://git@github.com/user/repo.git"
# Output: https://github.com/user/repo.git
rewrite_url "git@github.com:user/repo.git"
# Output: https://github.com/user/repo.git
# HTTPS URLs pass through unchanged
rewrite_url "https://github.com/user/repo.git"
# Output: https://github.com/user/repo.gitEnd-to-end verification with npm:
# Inside proot, install a package known to have git dependencies
npm install some-package-with-git-deps 2>&1 | grep -i "clone"
# All clone URLs in the output should show https://git+ssh:// format sometimes uses a colon instead of a slash after the hostname: git+ssh://git@github.com:user/repo. This is technically invalid per RFC but appears in real package.json files. The second sed expression (\.com:) handles this edge case.-e flags for each pattern. Extended regex (-E) works but is not needed here since all patterns are simple fixed prefixes..git suffix may or may not be present on the original URL. The sed rewrite does not add or remove it. Suffix normalization is handled separately in the argument parser (hack 4).git@gitlab.com:), these sed rules do not match and the URL passes through unchanged. This is correct behavior since only GitHub URLs need rewriting in this setup.-e expressions does not matter here because the four patterns are mutually exclusive (different prefixes). Only one can match per URL.$(echo ... | sed ...)), which costs one fork. On proot where forks are expensive (ptrace overhead), this adds a few milliseconds per git clone. Acceptable since clones are infrequent.All three SSH URL formats are transparently rewritten to HTTPS. npm install succeeds for packages with git dependencies regardless of which URL format their maintainers chose. The rewriting is invisible to npm and to the git operations themselves. Combined with the argument parser (hack 4), this gives proot a fully functional git layer without any SSH infrastructure on the device.