#!/usr/bin/env bash # shellcheck shell=bash # # Interactive onboarding driver. Sequences doctor, init-env, # check-gitea, login, check-access, and clone-orchestrator, prompting # only when a decision requires a human. set -euo pipefail here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" # shellcheck disable=SC1091 . "$here/common.sh" root="$(repo_root)" cd "$root" headless=0 usage() { cat <<'USAGE' Usage: just setup [--headless|--no-browser] Options: --headless Do not open the browser during login. Prints the --no-browser authorisation URL to stderr instead; paste it into any browser that can reach the loopback callback port (typically via SSH port-forward, see README). -h, --help Show this message. Environment: FORGE_SETUP_YES=1 Accept every default; do not prompt. Safe only when FORGE_GITEA_USERNAME is already set in .env. USAGE } while [ $# -gt 0 ]; do case "$1" in --headless|--no-browser) headless=1; shift;; -h|--help) usage; exit 0;; --) shift; break;; -*) die "unknown option: $1 (try 'just setup --help')";; *) die "unexpected positional argument: $1 (try 'just setup --help')";; esac done # info/ok/warn/err/step/note/die: see scripts/common.sh. # Stdout = chosen reply; stderr = prompt/echo. prompt_choice() { local msg="$1" default="$2" reply if [ "${FORGE_SETUP_YES:-0}" = "1" ] || [ ! -t 0 ]; then printf '%s %s [%s] (auto: %s)\n' \ "$(_fc_tag "$_FC_MAGENTA" '?')" "$msg" "$default" "$default" >&2 reply="$default" else printf '%s %s [%s] ' \ "$(_fc_tag "$_FC_MAGENTA" '?')" "$msg" "$default" >/dev/tty IFS= read -r reply /dev/tty IFS= read -r reply /dev/null || true)" if python3 "$here/forge_auth.py" status >/dev/null 2>&1; then have_live=1 fi fi if [ "$have_stored" = "1" ] && [ "$have_live" = "0" ] \ && [ -n "$existing_user" ] && [ -n "${FORGE_GITEA_USERNAME:-}" ] \ && [ "${existing_user,,}" = "${FORGE_GITEA_USERNAME,,}" ]; then note "stored session for '$existing_user' is not live; attempting silent refresh..." if python3 "$here/forge_auth.py" refresh --force >/dev/null 2>&1; then ok "refreshed stored session without a browser" have_live=1 else note "refresh failed (refresh token likely expired); a fresh login is required" fi fi run_login=1 if [ "$have_live" = "1" ] && [ -n "$existing_user" ]; then if [ -n "${FORGE_GITEA_USERNAME:-}" ] \ && [ "${existing_user,,}" != "${FORGE_GITEA_USERNAME,,}" ]; then note "stored token is for '$existing_user' but .env configures '$FORGE_GITEA_USERNAME'" reply="$(prompt_choice "Log out and sign in as '$FORGE_GITEA_USERNAME'? [Y/n]" "Y")" case "$reply" in [Nn]*) run_login=0; note "keeping the existing '$existing_user' session (forge_login may still reject it)";; *) python3 "$here/forge_auth.py" logout >/dev/null; ok "cleared stored Gitea tokens";; esac else ok "stored token is live for '$existing_user'" reply="$(prompt_choice "Reuse this session, or log out and re-authenticate? [R]euse / [L]ogout [R]" "R")" case "$reply" in [Ll]*) python3 "$here/forge_auth.py" logout >/dev/null; ok "cleared stored Gitea tokens";; *) run_login=0; ok "reusing the stored session";; esac fi elif [ "$have_stored" = "1" ] && [ -n "$existing_user" ]; then note "stored session for '$existing_user' is stale and could not be refreshed silently; re-authenticating." fi if [ "$run_login" = "1" ]; then if [ "$headless" = "1" ] && [ "${FORGE_SETUP_YES:-0}" = "1" ]; then die "cannot complete a fresh login under --headless + FORGE_SETUP_YES=1: a browser-based OAuth step is required but both flags forbid any interaction. Choose one: 1. Drop FORGE_SETUP_YES and re-run 'just setup --headless' (setup will print the authorisation URL and wait while the URL is pasted into a browser). 2. Run 'just login' once on a machine with a browser to populate the stored refresh token, then re-run 'just setup --headless' from the headless host. 3. Run 'just setup' (with a browser available) instead." fi if [ "$headless" = "1" ]; then bash "$here/forge_login.sh" --no-browser else bash "$here/forge_login.sh" fi else bash "$here/install-git-credential-helper.sh" >/dev/null fi step "5/7 confirming silent git access (just check-access)" GIT_TERMINAL_PROMPT=0 GCM_INTERACTIVE=Never \ GIT_ASKPASS="" SSH_ASKPASS="" \ VSCODE_GIT_ASKPASS_MAIN="" VSCODE_GIT_ASKPASS_NODE="" \ VSCODE_GIT_ASKPASS_EXTRA_ARGS="" VSCODE_GIT_IPC_HANDLE="" \ DISPLAY="" WAYLAND_DISPLAY="" \ git ls-remote "$FORGE_ORCHESTRATOR_REPO_URL" HEAD >/dev/null 2>&1 \ || die "git ls-remote failed. Account membership may be missing, or FORGE_ORCHESTRATOR_REPO_URL is wrong." ok "silent clone access confirmed" step "6/7 cloning the orchestrator (just clone-orchestrator)" workspace_root="${FORGE_WORKSPACE_ROOT:-.}" repo_name="$(basename "$FORGE_ORCHESTRATOR_REPO_URL" .git)" dest="$workspace_root/$repo_name" if [ -d "$dest/.git" ]; then note "orchestrator already present at $dest" reply="$(prompt_choice "Reuse existing checkout, or wipe and re-clone? [R]euse / [W]ipe [R]" "R")" case "$reply" in [Ww]*) note "removing $dest" rm -rf "$dest" ;; *) ok "keeping existing checkout" ;; esac fi if [ ! -d "$dest/.git" ]; then mkdir -p "$workspace_root" export GIT_TERMINAL_PROMPT=0 GCM_INTERACTIVE=Never unset GIT_ASKPASS SSH_ASKPASS \ VSCODE_GIT_ASKPASS_MAIN VSCODE_GIT_ASKPASS_NODE \ VSCODE_GIT_ASKPASS_EXTRA_ARGS VSCODE_GIT_IPC_HANDLE \ DISPLAY WAYLAND_DISPLAY if [ -n "${FORGE_ORCHESTRATOR_BRANCH:-}" ]; then git clone --branch "$FORGE_ORCHESTRATOR_BRANCH" \ "$FORGE_ORCHESTRATOR_REPO_URL" "$dest" else git clone "$FORGE_ORCHESTRATOR_REPO_URL" "$dest" fi ok "cloned into $dest" fi step "7/7 next steps (from the orchestrator's manifest)" manifest_path="$dest/scripts/contributor_setup_steps.json" if [ -f "$manifest_path" ]; then # Authoritative step list: $manifest_path in the orchestrator checkout. # This welcome repo never hardcodes the sequence. bash "$here/next_steps.sh" || true # Offer to run the plan now (respect --headless and FORGE_SETUP_YES). forward=() [ "$headless" = "1" ] && forward+=("--headless") [ "${FORGE_SETUP_YES:-0}" = "1" ] && forward+=("--yes") reply="$(prompt_choice "Run the orchestrator's contributor-setup now? [y/N]" "N")" case "$reply" in [Yy]*) step "handing off to the orchestrator" exec bash "$here/next_steps.sh" --run "${forward[@]}" ;; *) note "skipping auto-run. To execute later:" if [ "${#forward[@]}" -gt 0 ]; then note " just run-next-steps ${forward[*]}" else note " just run-next-steps" fi note "or, inside the orchestrator checkout:" note " cd $dest && just contributor-setup" ;; esac else warn "orchestrator at $dest does not ship contributor_setup_steps.json" warn "(older checkout?). See the orchestrator's README for onboarding:" note " $dest/README.md" fi