Skip to content

Commit 9a82ec4

Browse files
authored
tools.func: prevent script crash when entering GitHub token after rate limit (#13638)
* fix(tools): prevent script crash when entering GitHub token after rate limit fetch_and_deploy_gh_release set attempt=0 after accepting a token, then immediately ran ((0++)) which evaluates to 0 (falsy) causing exit code 1 and killing the script under set -e. Fix: set attempt=1 and continue to restart the retry loop cleanly, giving the full max_retries budget with the new token. Also fix fetch_and_deploy_codeberg_release: replace ((attempt++)) with attempt=\ to avoid the same zero-evaluation crash on the first connection timeout (attempt starts at 0 in that loop). Fixes #13635 * feat(tools): add var_github_token support with token validation - Add var_github_token to all VAR_WHITELIST arrays in build.func so the token can be set via default.vars, app.vars, or environment variable - Map var_github_token -> GITHUB_TOKEN in default_var_settings() (env variable takes precedence over the var file value) - Add commented var_github_token example to the default.vars template - Add validate_github_token() to tools.func: * Calls GET /user to verify the token is accepted * Reports expiry date from x-oauth-expiry header (fine-grained PATs) * Warns when classic PAT is missing public_repo scope * Returns distinct exit codes: 0=valid, 1=invalid/expired, 2=no scope, 3=error - Update prompt_for_github_token(): * Non-interactive path now picks up var_github_token automatically * Interactive path also picks up var_github_token without prompting * Validates token immediately after entry; loops until valid or Ctrl+C
1 parent f2d46dd commit 9a82ec4

File tree

2 files changed

+95
-7
lines changed

2 files changed

+95
-7
lines changed

misc/build.func

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,7 +1054,7 @@ load_vars_file() {
10541054

10551055
# Allowed var_* keys
10561056
local VAR_WHITELIST=(
1057-
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl
1057+
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_keyctl
10581058
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
10591059
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
10601060
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage var_searchdomain
@@ -1255,7 +1255,7 @@ default_var_settings() {
12551255
# Allowed var_* keys (alphabetically sorted)
12561256
# Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique)
12571257
local VAR_WHITELIST=(
1258-
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl
1258+
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_keyctl
12591259
var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu
12601260
var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged
12611261
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage
@@ -1350,6 +1350,10 @@ var_verbose=no
13501350
13511351
# Security (root PW) – empty => autologin
13521352
# var_pw=
1353+
1354+
# GitHub Personal Access Token (optional – avoids API rate limits during installs)
1355+
# Create at https://github.com/settings/tokens – read-only public access is sufficient
1356+
# var_github_token=ghp_your_token_here
13531357
EOF
13541358

13551359
# Now choose storages (always prompt unless just one exists)
@@ -1387,6 +1391,11 @@ EOF
13871391
VERBOSE="no"
13881392
fi
13891393

1394+
# 4) Map var_github_token → GITHUB_TOKEN (only if not already set in environment)
1395+
if [[ -z "${GITHUB_TOKEN:-}" && -n "${var_github_token:-}" ]]; then
1396+
export GITHUB_TOKEN="${var_github_token}"
1397+
fi
1398+
13901399
# 4) Apply base settings and show summary
13911400
METHOD="mydefaults-global"
13921401
base_settings "$VERBOSE"
@@ -1419,7 +1428,7 @@ get_app_defaults_path() {
14191428
if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then
14201429
# Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique)
14211430
declare -ag VAR_WHITELIST=(
1422-
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu
1431+
var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu
14231432
var_gateway var_hostname var_ipv6_method var_mac var_mtu
14241433
var_net var_ns var_os var_pw var_ram var_tags var_tun var_unprivileged
14251434
var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage

misc/tools.func

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,15 +1117,87 @@ is_package_installed() {
11171117
fi
11181118
}
11191119

1120+
# ------------------------------------------------------------------------------
1121+
# validate_github_token()
1122+
# Checks a GitHub token via the /user endpoint.
1123+
# Prints a status message and returns:
1124+
# 0 - token is valid
1125+
# 1 - token is invalid / expired (HTTP 401)
1126+
# 2 - token has no public repo scope (HTTP 200 but missing scope)
1127+
# 3 - network/API error
1128+
# Also reports expiry date if the token carries an x-oauth-expiry header.
1129+
# ------------------------------------------------------------------------------
1130+
validate_github_token() {
1131+
local token="${1:-${GITHUB_TOKEN:-}}"
1132+
[[ -z "$token" ]] && return 3
1133+
1134+
local response headers http_code expiry_date scopes
1135+
headers=$(mktemp)
1136+
response=$(curl -sSL -w "%{http_code}" \
1137+
-D "$headers" \
1138+
-o /dev/null \
1139+
-H "Authorization: Bearer $token" \
1140+
-H "Accept: application/vnd.github+json" \
1141+
-H "X-GitHub-Api-Version: 2022-11-28" \
1142+
"https://api.github.com/user" 2>/dev/null) || { rm -f "$headers"; return 3; }
1143+
http_code="$response"
1144+
1145+
# Read expiry header (fine-grained PATs carry this)
1146+
expiry_date=$(grep -i '^github-authentication-token-expiration:' "$headers" \
1147+
| sed 's/.*: *//' | tr -d '\r\n' || true)
1148+
# Read token scopes (classic PATs)
1149+
scopes=$(grep -i '^x-oauth-scopes:' "$headers" \
1150+
| sed 's/.*: *//' | tr -d '\r\n' || true)
1151+
rm -f "$headers"
1152+
1153+
case "$http_code" in
1154+
200)
1155+
if [[ -n "$expiry_date" ]]; then
1156+
msg_ok "GitHub token is valid (expires: $expiry_date)."
1157+
else
1158+
msg_ok "GitHub token is valid (no expiry / fine-grained PAT)."
1159+
fi
1160+
# Warn if classic PAT has no public_repo scope
1161+
if [[ -n "$scopes" && "$scopes" != *"public_repo"* && "$scopes" != *"repo"* ]]; then
1162+
msg_warn "Token has no 'public_repo' scope - private repos and some release APIs may fail."
1163+
return 2
1164+
fi
1165+
return 0
1166+
;;
1167+
401)
1168+
msg_error "GitHub token is invalid or expired (HTTP 401)."
1169+
return 1
1170+
;;
1171+
*)
1172+
msg_warn "GitHub token validation returned HTTP $http_code - treating as valid."
1173+
return 0
1174+
;;
1175+
esac
1176+
}
1177+
11201178
# ------------------------------------------------------------------------------
11211179
# Prompt user to enter a GitHub Personal Access Token (PAT) interactively
11221180
# Returns 0 if a valid token was provided, 1 otherwise
11231181
# ------------------------------------------------------------------------------
11241182
prompt_for_github_token() {
11251183
if [[ ! -t 0 ]]; then
1184+
# Non-interactive: pick up var_github_token if set (from default.vars / app.vars / env)
1185+
if [[ -z "${GITHUB_TOKEN:-}" && -n "${var_github_token:-}" ]]; then
1186+
export GITHUB_TOKEN="${var_github_token}"
1187+
msg_ok "GitHub token loaded from var_github_token."
1188+
return 0
1189+
fi
11261190
return 1
11271191
fi
11281192

1193+
# Prefer var_github_token when already set and no interactive override needed
1194+
if [[ -z "${GITHUB_TOKEN:-}" && -n "${var_github_token:-}" ]]; then
1195+
export GITHUB_TOKEN="${var_github_token}"
1196+
msg_ok "GitHub token loaded from var_github_token."
1197+
validate_github_token || true
1198+
return 0
1199+
fi
1200+
11291201
local reply
11301202
read -rp "${TAB}Would you like to enter a GitHub Personal Access Token (PAT)? [y/N]: " reply
11311203
reply="${reply:-n}"
@@ -1147,10 +1219,16 @@ prompt_for_github_token() {
11471219
msg_warn "Token must not contain spaces. Please try again."
11481220
continue
11491221
fi
1150-
break
1222+
# Validate before accepting
1223+
export GITHUB_TOKEN="$token"
1224+
if validate_github_token "$token"; then
1225+
break
1226+
else
1227+
msg_warn "Please enter a valid token, or press Ctrl+C to abort."
1228+
unset GITHUB_TOKEN
1229+
fi
11511230
done
11521231

1153-
export GITHUB_TOKEN="$token"
11541232
msg_ok "GitHub token has been set."
11551233
return 0
11561234
}
@@ -2860,7 +2938,7 @@ function fetch_and_deploy_codeberg_release() {
28602938

28612939
while ((attempt < ${#api_timeouts[@]})); do
28622940
resp=$(curl --connect-timeout 10 --max-time "${api_timeouts[$attempt]}" -fsSL -w "%{http_code}" -o /tmp/codeberg_rel.json "$api_url") && success=true && break
2863-
((attempt++))
2941+
attempt=$((attempt + 1))
28642942
if ((attempt < ${#api_timeouts[@]})); then
28652943
msg_warn "API request timed out after ${api_timeouts[$((attempt - 1))]}s, retrying... (attempt $((attempt + 1))/${#api_timeouts[@]})"
28662944
fi
@@ -3370,7 +3448,8 @@ function fetch_and_deploy_gh_release() {
33703448
if prompt_for_github_token; then
33713449
header=(-H "Authorization: token $GITHUB_TOKEN")
33723450
retry_delay=2
3373-
attempt=0
3451+
attempt=1
3452+
continue
33743453
fi
33753454
fi
33763455
else

0 commit comments

Comments
 (0)