Rewrite onboarding prose to a neutral voice
This commit is contained in:
@@ -63,7 +63,7 @@ repo_root() {
|
||||
}
|
||||
|
||||
# Load $repo/.env into the environment.
|
||||
# Existing environment values take precedence (matches just's dotenv-load).
|
||||
# Existing environment values take precedence (matches dotenv-load in just).
|
||||
load_env() {
|
||||
local root env line key rest
|
||||
root="$(repo_root)"
|
||||
@@ -111,6 +111,6 @@ require_cmd() {
|
||||
require_env() {
|
||||
local name="$1"
|
||||
if [ -z "${!name:-}" ]; then
|
||||
die "environment variable '$name' is unset. Run 'just init-env' and fill .env."
|
||||
die "environment variable '$name' is unset. Run 'just init-env'. Fill .env."
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ if command -v xdg-open >/dev/null 2>&1 || command -v open >/dev/null 2>&1; then
|
||||
printf ' %s %-12s -> (detected)\n' "$(_fc_tag "$_FC_GREEN" ok)" 'web browser'
|
||||
else
|
||||
printf ' %s %-12s -> no xdg-open/open on PATH.\n' "$(_fc_tag "$_FC_YELLOW" warn)" 'web browser'
|
||||
printf ' if this is a headless machine, run: just login-headless\n'
|
||||
printf ' headless mode: just login-headless\n'
|
||||
case "$pm" in
|
||||
apt) printf ' otherwise install: sudo apt-get install -y xdg-utils\n' ;;
|
||||
dnf) printf ' otherwise install: sudo dnf install -y xdg-utils\n' ;;
|
||||
@@ -214,7 +214,7 @@ if [ "$missing" -gt 0 ]; then
|
||||
printf ' %s\n' "$c"
|
||||
done
|
||||
printf '\n'
|
||||
warn 'then re-run: just doctor'
|
||||
warn 're-run: just doctor'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -282,11 +282,12 @@ def _http_post_form(
|
||||
|
||||
def discover_endpoints(config: ForgeAuthConfig) -> dict[str, str]:
|
||||
"""Call Gitea's ``/.well-known/openid-configuration`` and return the
|
||||
three endpoints we care about, validated against the issuer.
|
||||
three required endpoints, validated against the issuer.
|
||||
"""
|
||||
url = f"{config.gitea_base_url}/.well-known/openid-configuration"
|
||||
payload = _http_get_json(url, insecure_tls=config.insecure_tls)
|
||||
issuer = payload.get("issuer")
|
||||
|
||||
if not isinstance(issuer, str) or issuer.rstrip("/") != config.gitea_base_url.rstrip("/"):
|
||||
raise AuthError(
|
||||
f"OIDC discovery issuer {issuer!r} does not match "
|
||||
@@ -401,7 +402,7 @@ class _CallbackHandler(BaseHTTPRequestHandler):
|
||||
self.send_header("Content-Type", "text/html; charset=utf-8")
|
||||
self.end_headers()
|
||||
self.wfile.write(
|
||||
b"<html><body><h1>Login failed</h1><p>You can close this window.</p></body></html>"
|
||||
b"<html><body><h1>Login failed</h1><p>Window close is safe.</p></body></html>"
|
||||
)
|
||||
server.result_queue.put(
|
||||
_build_authorize_error(
|
||||
@@ -426,7 +427,7 @@ class _CallbackHandler(BaseHTTPRequestHandler):
|
||||
self.wfile.write(
|
||||
b"<html><body style='font-family:sans-serif'>"
|
||||
b"<h1>Login complete.</h1>"
|
||||
b"<p>You can close this window and return to your terminal.</p>"
|
||||
b"<p>Window close is safe. Terminal focus can resume.</p>"
|
||||
b"</body></html>"
|
||||
)
|
||||
server.result_queue.put((code, state))
|
||||
@@ -471,7 +472,7 @@ def _build_authorize_error(
|
||||
gitea_base_url : str
|
||||
Base URL of the Gitea server. Used to build the user-settings
|
||||
URL in the remediation message. Empty string yields a
|
||||
``<your-gitea-url>`` placeholder.
|
||||
``<gitea-base-url>`` placeholder.
|
||||
client_id : str, optional
|
||||
OAuth client id requesting the authorization. Surfaced in the
|
||||
"different scope" message to disambiguate the grant row in
|
||||
@@ -493,7 +494,7 @@ def _build_authorize_error(
|
||||
settings_url = (
|
||||
f"{base}/user/settings/applications"
|
||||
if base
|
||||
else "<your-gitea-url>/user/settings/applications"
|
||||
else "<gitea-base-url>/user/settings/applications"
|
||||
)
|
||||
|
||||
if "different scope" in low or ("scope" in low and "grant" in low):
|
||||
@@ -554,8 +555,8 @@ def wait_for_callback(
|
||||
except OSError as exc:
|
||||
raise AuthError(
|
||||
f"cannot bind loopback server on {host}:{port}: {exc}. "
|
||||
f"Is another 'just login' still running? "
|
||||
f"If port {port} is held by an unrelated process, override "
|
||||
f"Another 'just login' instance may still be running. "
|
||||
f"If port {port} is held by an unrelated process, set "
|
||||
f"FSDGG_CLI_REDIRECT_URI in .env (note: the port must match "
|
||||
f"the OAuth app registered in Gitea)."
|
||||
) from exc
|
||||
@@ -566,7 +567,7 @@ def wait_for_callback(
|
||||
except Exception as exc:
|
||||
raise AuthError(
|
||||
f"timed out after {int(timeout_seconds)}s waiting for OAuth "
|
||||
f"callback. Did you complete the browser login?"
|
||||
f"callback. Browser login completion was not detected."
|
||||
) from exc
|
||||
finally:
|
||||
server.server_close()
|
||||
@@ -763,18 +764,17 @@ def _print_headless_guidance(auth_url: str, redirect) -> None:
|
||||
]
|
||||
if in_ssh:
|
||||
lines += [
|
||||
f"{info_tag} this process is running inside an SSH session. From the",
|
||||
" machine where you will open the browser, run:",
|
||||
f"{info_tag} SSH session detected. From the browser-side machine run:",
|
||||
"",
|
||||
f" ssh -L {cb_port}:127.0.0.1:{cb_port} {user}@{hostname}",
|
||||
"",
|
||||
" and paste the URL above into THAT machine's browser.",
|
||||
" Paste the URL above into that machine's browser.",
|
||||
"",
|
||||
]
|
||||
else:
|
||||
lines += [
|
||||
f"{info_tag} if this machine is remote, from the machine with the",
|
||||
" browser run (before pasting the URL):",
|
||||
f"{info_tag} Remote-host case: from the browser-side machine run",
|
||||
" the following command before pasting the URL:",
|
||||
"",
|
||||
f" ssh -L {cb_port}:127.0.0.1:{cb_port} {user}@{hostname}",
|
||||
"",
|
||||
@@ -825,8 +825,8 @@ def run_login(
|
||||
if print_authorize_url:
|
||||
if open_browser:
|
||||
cli_info(
|
||||
f"open this URL in your browser if it does not open "
|
||||
f"automatically:\n {auth_url}"
|
||||
f"authorization URL (fallback when automatic browser open fails):\n"
|
||||
f" {auth_url}"
|
||||
)
|
||||
else:
|
||||
_print_headless_guidance(auth_url, redirect)
|
||||
@@ -882,7 +882,7 @@ def run_login(
|
||||
f"'{config.expected_username}'. The stored auth file has "
|
||||
"NOT been updated.\n"
|
||||
" To fix:\n"
|
||||
f" 1. Sign out of Gitea in your browser: {logout_url}\n"
|
||||
f" 1. Sign out of Gitea in the browser: {logout_url}\n"
|
||||
f" 2. Sign back in as '{config.expected_username}'\n"
|
||||
" 3. Re-run 'just login'"
|
||||
)
|
||||
@@ -905,8 +905,7 @@ def run_refresh(config: ForgeAuthConfig, *, must_refresh: bool = False) -> AuthF
|
||||
If ``must_refresh`` is False and the access token is still live,
|
||||
this is a no-op. If refresh fails, raises ``AuthError`` (the
|
||||
credential helper surfaces this to git as "fall through to prompt",
|
||||
and the CLI ``just refresh`` surfaces it with an instruction to
|
||||
re-run ``just login``).
|
||||
and the CLI ``just refresh`` surfaces the failure to stderr).
|
||||
"""
|
||||
store = auth_store_path()
|
||||
existing = AuthFile.read(store)
|
||||
@@ -934,7 +933,7 @@ def run_refresh(config: ForgeAuthConfig, *, must_refresh: bool = False) -> AuthF
|
||||
def run_logout() -> Path | None:
|
||||
"""Remove every codevalet-managed field from the auth file.
|
||||
|
||||
If the file only ever held our fields, delete it entirely. If the
|
||||
If the file only ever held repo-managed fields, delete it entirely. If the
|
||||
gateway has already populated its own fields (access_token etc.),
|
||||
wipe only the Gitea + welcome-repo keys and leave the rest.
|
||||
"""
|
||||
|
||||
@@ -24,6 +24,6 @@ python3 "$here/forge_auth.py" login "$@"
|
||||
"$here/install-git-credential-helper.sh"
|
||||
|
||||
ok "login complete"
|
||||
note "git operations against your Gitea server now authenticate silently."
|
||||
note "Test with:"
|
||||
note "git operations against FORGE_GITEA_URL now authenticate without prompts."
|
||||
note "Verification command:"
|
||||
note " git ls-remote \"\$FORGE_ORCHESTRATOR_REPO_URL\""
|
||||
|
||||
@@ -16,7 +16,7 @@ On every ``get`` the helper:
|
||||
unchanged so the normal prompt chain continues.
|
||||
2. Checks that the git request host matches the host recorded in
|
||||
``_forge_gitea_base_url`` (or ``FORGE_GITEA_URL``). If not, passes
|
||||
through: this ensures we never hand OAuth tokens to unrelated
|
||||
through: this prevents OAuth token disclosure to unrelated
|
||||
hosts even if git mis-scopes its lookup.
|
||||
3. If ``gitea_access_token`` is live, emits
|
||||
``username=<stored-user>`` and ``password=<gitea_access_token>``.
|
||||
@@ -29,7 +29,7 @@ Security notes
|
||||
--------------
|
||||
* The helper never writes to stdout except the credential key=value
|
||||
block. Logs go to stderr.
|
||||
* On OAuth refresh failure we exit **non-zero** rather than silently
|
||||
* On OAuth refresh failure the process exits **non-zero** rather than silently
|
||||
returning stale credentials.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
@@ -93,7 +93,7 @@ def _configured_host() -> tuple[str, str, int | None] | None:
|
||||
|
||||
Priority:
|
||||
1. The ``_forge_gitea_base_url`` field inside the stored auth
|
||||
file: that is the host we actually authenticated against.
|
||||
file: the authenticated host.
|
||||
2. The ``FORGE_GITEA_URL`` env var (pre-login override).
|
||||
Returns None if neither is set; the helper then passes through.
|
||||
"""
|
||||
@@ -150,7 +150,7 @@ def _request_matches(
|
||||
def cmd_get(fields: dict[str, str]) -> int:
|
||||
configured = _configured_host()
|
||||
if configured is None:
|
||||
emit(fields) # pass-through: we don't know who we answer for
|
||||
emit(fields) # pass-through: helper scope is undefined
|
||||
return 0
|
||||
if not _request_matches(fields, configured):
|
||||
emit(fields) # pass-through: request is for a different host
|
||||
|
||||
@@ -37,11 +37,11 @@ python3 -c 'import sys; compile(open(sys.argv[1]).read(), sys.argv[1], "exec")'
|
||||
key="credential.${FORGE_GITEA_URL}.helper"
|
||||
use_path_key="credential.${FORGE_GITEA_URL}.useHttpPath"
|
||||
|
||||
# Wipe any previous helpers we may have set so we do not stack them.
|
||||
# Wipe previously configured helpers to avoid stacking entries.
|
||||
git config --global --unset-all "$key" 2>/dev/null || true
|
||||
git config --global --unset-all "$use_path_key" 2>/dev/null || true
|
||||
|
||||
# Username is derived from the OAuth token at runtime by the helper; we
|
||||
# Username is derived from the OAuth token at runtime by the helper; do
|
||||
# deliberately do NOT set credential.<URL>.username here, to avoid
|
||||
# pinning an old login. The helper emits username=<stored> on every
|
||||
# `get`, which git respects.
|
||||
|
||||
@@ -66,7 +66,7 @@ orchestrator="$workspace_root/$repo_name"
|
||||
|
||||
if [ ! -d "$orchestrator/.git" ]; then
|
||||
err "orchestrator checkout not found at $orchestrator"
|
||||
note "run 'just clone-orchestrator' (or 'just setup') first."
|
||||
note "required command: just clone-orchestrator"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -119,9 +119,9 @@ if [ "$MODE" != "run" ]; then
|
||||
if [ "${#forward_args[@]}" -gt 0 ]; then
|
||||
fwd_suffix=" ${forward_args[*]}"
|
||||
fi
|
||||
note "Execute via:"
|
||||
note "Command:"
|
||||
note " just run-next-steps${fwd_suffix}"
|
||||
note "Execute inside the orchestrator:"
|
||||
note "Orchestrator command:"
|
||||
note " cd $orchestrator && just contributor-setup${fwd_suffix}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -84,7 +84,7 @@ fi
|
||||
load_env
|
||||
|
||||
if [ -z "${FORGE_GITEA_USERNAME:-}" ]; then
|
||||
note "FORGE_GITEA_USERNAME is blank. Enter your Gitea username exactly as it appears in your profile URL."
|
||||
note "FORGE_GITEA_USERNAME is blank. Enter the Gitea username from the profile URL."
|
||||
new_user="$(prompt_line "Gitea username")"
|
||||
if [ -z "$new_user" ]; then
|
||||
die "no username entered: aborting. Edit .env by hand and re-run 'just setup'."
|
||||
@@ -180,7 +180,7 @@ if [ "$run_login" = "1" ]; then
|
||||
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 you paste it into a browser).
|
||||
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.
|
||||
@@ -202,7 +202,7 @@ GIT_TERMINAL_PROMPT=0 GCM_INTERACTIVE=Never \
|
||||
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. Your account may not yet be in the org, or FORGE_ORCHESTRATOR_REPO_URL is wrong."
|
||||
|| 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)"
|
||||
|
||||
@@ -10,7 +10,7 @@ here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
|
||||
load_env
|
||||
|
||||
# 1. Log out at the OAuth layer (clears our managed fields from
|
||||
# 1. Log out at the OAuth layer (clears repo-managed fields from
|
||||
# client-auth.json and keeps any fields owned by the orchestrator
|
||||
# gateway untouched).
|
||||
if [ -f "$here/forge_auth.py" ]; then
|
||||
@@ -37,4 +37,4 @@ done
|
||||
|
||||
info 'uninstall complete.'
|
||||
info "note: $FORGE_AUTH_FILE is left in place if the orchestrator wrote it;"
|
||||
info ' delete it manually if you want a completely clean state.'
|
||||
info ' delete it manually for a fully clean state.'
|
||||
|
||||
Reference in New Issue
Block a user