Unverified Commit 6794e66c authored by Minseong Choi's avatar Minseong Choi 💬
Browse files

fix(bootstrap): fail a tokenless private clone instead of prompting

A source build against a private repository with no FELIS_GITHUB_TOKEN,
or a wrong one, made git ask for a username on /dev/tty, and a piped
install sat there waiting.

git_auth now runs git with GIT_TERMINAL_PROMPT=0 on both arms, so git
fails at once with "terminal prompts disabled". Both fetch_source
failures name FELIS_GITHUB_TOKEN in their message: the fresh clone,
and the fetch into an existing checkout, which had no message of its
own before.
parent 34f73ba1
Loading
Loading
Loading
Loading
+9 −4
Changes for deploy/bootstrap.sh: 9 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -988,12 +988,16 @@ download_release_binary() {
# approve that follows a successful auth, so it outlives the install after all. An empty
# value is git's documented list reset. Verified on Fedora: with store configured the single
# -c form persists the token to disk, the reset form writes nothing.
#
# GIT_TERMINAL_PROMPT=0 on both arms: GitHub answers a private repo with no or a bad token
# by asking for credentials, and git would put that prompt on /dev/tty, where a piped
# install sits waiting instead of failing with the FELIS_GITHUB_TOKEN hint.
git_auth() {
  if [ -n "$FELIS_GITHUB_TOKEN" ]; then
    git -c 'credential.helper=' \
    GIT_TERMINAL_PROMPT=0 git -c 'credential.helper=' \
        -c 'credential.helper=!f() { printf "username=x-access-token\npassword=%s\n" "$FELIS_GITHUB_TOKEN"; }; f' "$@"
  else
    git "$@"
    GIT_TERMINAL_PROMPT=0 git "$@"
  fi
}

@@ -1070,7 +1074,8 @@ fetch_source() {
  resolve_install_ref
  if [ -d "${SRC_DIR}/.git" ]; then
    log "updating source in ${SRC_DIR}"
    git_auth -C "$SRC_DIR" fetch --depth 1 origin "$FELIS_REF"
    git_auth -C "$SRC_DIR" fetch --depth 1 origin "$FELIS_REF" \
      || die "could not fetch ${FELIS_REF} from ${FELIS_REPO_URL}; if the repository is private, set FELIS_GITHUB_TOKEN to a token with read access to it"
    git -C "$SRC_DIR" checkout -f FETCH_HEAD
  else
    log "cloning ${FELIS_REPO_URL} (${FELIS_REF})"
@@ -1082,7 +1087,7 @@ fetch_source() {
    git_auth clone --depth 1 --branch "$FELIS_REF" "$FELIS_REPO_URL" "$SRC_DIR" 2>/dev/null \
      || { git_auth clone "$FELIS_REPO_URL" "$SRC_DIR" \
           && git_auth -C "$SRC_DIR" checkout -f "$FELIS_REF"; } \
      || die "could not check out ${FELIS_REF} from ${FELIS_REPO_URL}"
      || die "could not check out ${FELIS_REF} from ${FELIS_REPO_URL}; if the repository is private, set FELIS_GITHUB_TOKEN to a token with read access to it"
  fi
  stamp_version
  ok "source ready at ${SRC_DIR}"
+43 −0
Changes for deploy/bootstrap_test.sh: 43 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -426,6 +426,49 @@ gtmp="$(printf '%s\n' "$out" | sed -n 's/^TEMP: //p')"
expect "the Go download is staged in a directory the cleanup removes" \
  "CURL: ${gtmp:-<none>}/go1.26.4.linux-amd64.tar.gz" "$out"

# --- a private repo without a token fails with the hint instead of prompting -------------
# git asks for credentials on /dev/tty, where a piped install would sit waiting. Every
# network git call goes through git_auth, so the switch belongs there.

gablock="$(awk '/^git_auth\(\) \{/,/^}/' "$BS")"
[ -n "$gablock" ] || { echo "FAIL: no git_auth found in $BS"; exit 1; }
[ "$(printf '%s\n' "$gablock" | wc -l)" -lt 15 ] \
  || { echo "FAIL: the extracted block is not the function -- did its closing brace move?"; exit 1; }

run_git_auth() { # token
  FELIS_GITHUB_TOKEN="$1" bash -c '
    unset GIT_TERMINAL_PROMPT # whatever runs this harness may have set it already
    git() { printf "GIT: prompt=%s\n" "${GIT_TERMINAL_PROMPT:-<unset>}"; }
    '"$gablock"'
    git_auth clone https://example.invalid/felis.git'
}

expect "git never prompts without a token" "GIT: prompt=0" "$(run_git_auth '')"
expect "git never prompts with a token" "GIT: prompt=0" "$(run_git_auth ghp_example)"

fblock="$(awk '/^fetch_source\(\) \{/,/^}/' "$BS")"
[ -n "$fblock" ] || { echo "FAIL: no fetch_source found in $BS"; exit 1; }
[ "$(printf '%s\n' "$fblock" | wc -l)" -lt 40 ] \
  || { echo "FAIL: the extracted block is not the function -- did its closing brace move?"; exit 1; }

run_fetch() { # src-dir
  SRC_DIR="$1" FELIS_REF=main FELIS_REPO_URL=https://example.invalid/felis.git bash -c '
    die() { printf "DIE: %s\n" "$*"; exit 1; }
    log() { :; }
    ok() { :; }
    resolve_install_ref() { :; }
    stamp_version() { :; }
    git_auth() { return 128; }
    git() { :; }
    '"$fblock"'
    fetch_source'
}

expect "a failed clone names the token" "set FELIS_GITHUB_TOKEN" "$(run_fetch "$sdir/src")"
mkdir -p "$sdir/src/.git"
expect "a failed fetch into an existing checkout names the token" "set FELIS_GITHUB_TOKEN" \
  "$(run_fetch "$sdir/src")"

# ---------------------------------------------------------------------------------------
if [ "$fails" -eq 0 ]; then
  echo "ALL PASS"