From df75b12c60e3ce8489f07d36fbdfd0934d8852d1 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:33:29 +0200 Subject: [PATCH] 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 --- misc/build.func | 15 +++++++-- misc/tools.func | 82 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 92 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index fe89b3f26..4af0752dc 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1054,7 +1054,7 @@ load_vars_file() { # Allowed var_* keys local VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_keyctl var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged 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() { # Allowed var_* keys (alphabetically sorted) # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique) local VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu var_keyctl + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_keyctl var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu var_net var_nesting var_ns var_os var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged 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 # Security (root PW) – empty => autologin # var_pw= + +# GitHub Personal Access Token (optional – avoids API rate limits during installs) +# Create at https://github.com/settings/tokens – read-only public access is sufficient +# var_github_token=ghp_your_token_here EOF # Now choose storages (always prompt unless just one exists) @@ -1387,6 +1391,11 @@ EOF VERBOSE="no" fi + # 4) Map var_github_token → GITHUB_TOKEN (only if not already set in environment) + if [[ -z "${GITHUB_TOKEN:-}" && -n "${var_github_token:-}" ]]; then + export GITHUB_TOKEN="${var_github_token}" + fi + # 4) Apply base settings and show summary METHOD="mydefaults-global" base_settings "$VERBOSE" @@ -1419,7 +1428,7 @@ get_app_defaults_path() { if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique) declare -ag VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_gpu + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_github_token var_gpu var_gateway var_hostname var_ipv6_method var_mac var_mtu var_net var_ns var_os var_pw var_ram var_tags var_tun var_unprivileged var_verbose var_version var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage diff --git a/misc/tools.func b/misc/tools.func index add8af830..4c03d4468 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1117,15 +1117,87 @@ is_package_installed() { fi } +# ------------------------------------------------------------------------------ +# validate_github_token() +# Checks a GitHub token via the /user endpoint. +# Prints a status message and returns: +# 0 - token is valid +# 1 - token is invalid / expired (HTTP 401) +# 2 - token has no public repo scope (HTTP 200 but missing scope) +# 3 - network/API error +# Also reports expiry date if the token carries an x-oauth-expiry header. +# ------------------------------------------------------------------------------ +validate_github_token() { + local token="${1:-${GITHUB_TOKEN:-}}" + [[ -z "$token" ]] && return 3 + + local response headers http_code expiry_date scopes + headers=$(mktemp) + response=$(curl -sSL -w "%{http_code}" \ + -D "$headers" \ + -o /dev/null \ + -H "Authorization: Bearer $token" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/user" 2>/dev/null) || { rm -f "$headers"; return 3; } + http_code="$response" + + # Read expiry header (fine-grained PATs carry this) + expiry_date=$(grep -i '^github-authentication-token-expiration:' "$headers" \ + | sed 's/.*: *//' | tr -d '\r\n' || true) + # Read token scopes (classic PATs) + scopes=$(grep -i '^x-oauth-scopes:' "$headers" \ + | sed 's/.*: *//' | tr -d '\r\n' || true) + rm -f "$headers" + + case "$http_code" in + 200) + if [[ -n "$expiry_date" ]]; then + msg_ok "GitHub token is valid (expires: $expiry_date)." + else + msg_ok "GitHub token is valid (no expiry / fine-grained PAT)." + fi + # Warn if classic PAT has no public_repo scope + if [[ -n "$scopes" && "$scopes" != *"public_repo"* && "$scopes" != *"repo"* ]]; then + msg_warn "Token has no 'public_repo' scope - private repos and some release APIs may fail." + return 2 + fi + return 0 + ;; + 401) + msg_error "GitHub token is invalid or expired (HTTP 401)." + return 1 + ;; + *) + msg_warn "GitHub token validation returned HTTP $http_code - treating as valid." + return 0 + ;; + esac +} + # ------------------------------------------------------------------------------ # Prompt user to enter a GitHub Personal Access Token (PAT) interactively # Returns 0 if a valid token was provided, 1 otherwise # ------------------------------------------------------------------------------ prompt_for_github_token() { if [[ ! -t 0 ]]; then + # Non-interactive: pick up var_github_token if set (from default.vars / app.vars / env) + if [[ -z "${GITHUB_TOKEN:-}" && -n "${var_github_token:-}" ]]; then + export GITHUB_TOKEN="${var_github_token}" + msg_ok "GitHub token loaded from var_github_token." + return 0 + fi return 1 fi + # Prefer var_github_token when already set and no interactive override needed + if [[ -z "${GITHUB_TOKEN:-}" && -n "${var_github_token:-}" ]]; then + export GITHUB_TOKEN="${var_github_token}" + msg_ok "GitHub token loaded from var_github_token." + validate_github_token || true + return 0 + fi + local reply read -rp "${TAB}Would you like to enter a GitHub Personal Access Token (PAT)? [y/N]: " reply reply="${reply:-n}" @@ -1147,10 +1219,16 @@ prompt_for_github_token() { msg_warn "Token must not contain spaces. Please try again." continue fi - break + # Validate before accepting + export GITHUB_TOKEN="$token" + if validate_github_token "$token"; then + break + else + msg_warn "Please enter a valid token, or press Ctrl+C to abort." + unset GITHUB_TOKEN + fi done - export GITHUB_TOKEN="$token" msg_ok "GitHub token has been set." return 0 }