mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-02-18 23:41:48 +01:00
285 lines
6.6 KiB
Bash
Executable File
285 lines
6.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# HTTP helpers for Pulse shell scripts (downloads, API calls, GitHub queries).
|
|
|
|
set -euo pipefail
|
|
|
|
# Ensure common library is loaded when sourced directly.
|
|
if ! declare -F common::log_info >/dev/null 2>&1; then
|
|
# shellcheck disable=SC1091
|
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh"
|
|
fi
|
|
|
|
declare -g HTTP_DEFAULT_RETRIES="${HTTP_DEFAULT_RETRIES:-3}"
|
|
declare -g HTTP_DEFAULT_BACKOFF="${HTTP_DEFAULT_BACKOFF:-1 3 5}"
|
|
|
|
# http::detect_download_tool
|
|
# Emits the preferred download tool (curl/wget) or fails if neither exist.
|
|
http::detect_download_tool() {
|
|
if command -v curl >/dev/null 2>&1; then
|
|
printf 'curl\n'
|
|
return 0
|
|
fi
|
|
if command -v wget >/dev/null 2>&1; then
|
|
printf 'wget\n'
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# http::download --url URL --output PATH [--insecure] [--quiet] [--header "Name: value"]
|
|
# [--retries N] [--backoff "1 3 5"]
|
|
# Downloads the specified URL to PATH using curl or wget.
|
|
http::download() {
|
|
local url=""
|
|
local output=""
|
|
local insecure=false
|
|
local quiet=false
|
|
local retries="${HTTP_DEFAULT_RETRIES}"
|
|
local backoff="${HTTP_DEFAULT_BACKOFF}"
|
|
local -a headers=()
|
|
|
|
while (($#)); do
|
|
case "$1" in
|
|
--url)
|
|
url="$2"
|
|
shift 2
|
|
;;
|
|
--output)
|
|
output="$2"
|
|
shift 2
|
|
;;
|
|
--insecure)
|
|
insecure=true
|
|
shift
|
|
;;
|
|
--quiet)
|
|
quiet=true
|
|
shift
|
|
;;
|
|
--header)
|
|
headers+=("$2")
|
|
shift 2
|
|
;;
|
|
--retries)
|
|
retries="$2"
|
|
shift 2
|
|
;;
|
|
--backoff)
|
|
backoff="$2"
|
|
shift 2
|
|
;;
|
|
*)
|
|
common::log_warn "Unknown flag for http::download: $1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "${url}" || -z "${output}" ]]; then
|
|
common::fail "http::download requires --url and --output"
|
|
fi
|
|
|
|
if common::is_dry_run; then
|
|
common::log_info "[dry-run] Download ${url} -> ${output}"
|
|
return 0
|
|
fi
|
|
|
|
local tool
|
|
if ! tool="$(http::detect_download_tool)"; then
|
|
common::fail "No download tool available (install curl or wget)"
|
|
fi
|
|
|
|
mkdir -p "$(dirname "${output}")"
|
|
|
|
local -a cmd
|
|
if [[ "${tool}" == "curl" ]]; then
|
|
cmd=(curl -fL --connect-timeout 15)
|
|
if [[ "${quiet}" == true ]]; then
|
|
cmd+=(-sS)
|
|
else
|
|
cmd+=(--progress-bar)
|
|
fi
|
|
if [[ "${insecure}" == true ]]; then
|
|
cmd+=(-k)
|
|
fi
|
|
local header
|
|
for header in "${headers[@]}"; do
|
|
cmd+=(-H "${header}")
|
|
done
|
|
cmd+=(-o "${output}" "${url}")
|
|
else
|
|
cmd=(wget --tries=3)
|
|
if [[ "${quiet}" == true ]]; then
|
|
cmd+=(-q)
|
|
else
|
|
cmd+=(--progress=bar:force)
|
|
fi
|
|
if [[ "${insecure}" == true ]]; then
|
|
cmd+=(--no-check-certificate)
|
|
fi
|
|
local header
|
|
for header in "${headers[@]}"; do
|
|
cmd+=(--header="${header}")
|
|
done
|
|
cmd+=(-O "${output}" "${url}")
|
|
fi
|
|
|
|
local -a run_args=(--label "download ${url}")
|
|
[[ -n "${retries}" ]] && run_args+=(--retries "${retries}")
|
|
[[ -n "${backoff}" ]] && run_args+=(--backoff "${backoff}")
|
|
|
|
common::run "${run_args[@]}" -- "${cmd[@]}"
|
|
}
|
|
|
|
# http::api_call --url URL [--method METHOD] [--token TOKEN] [--bearer TOKEN]
|
|
# [--body DATA] [--header "Name: value"] [--insecure]
|
|
# Performs an API request and prints the response body.
|
|
http::api_call() {
|
|
local url=""
|
|
local method="GET"
|
|
local token=""
|
|
local bearer=""
|
|
local body=""
|
|
local insecure=false
|
|
local -a headers=()
|
|
|
|
while (($#)); do
|
|
case "$1" in
|
|
--url)
|
|
url="$2"
|
|
shift 2
|
|
;;
|
|
--method)
|
|
method="$2"
|
|
shift 2
|
|
;;
|
|
--token)
|
|
token="$2"
|
|
shift 2
|
|
;;
|
|
--bearer)
|
|
bearer="$2"
|
|
shift 2
|
|
;;
|
|
--body)
|
|
body="$2"
|
|
shift 2
|
|
;;
|
|
--header)
|
|
headers+=("$2")
|
|
shift 2
|
|
;;
|
|
--insecure)
|
|
insecure=true
|
|
shift
|
|
;;
|
|
*)
|
|
common::log_warn "Unknown flag for http::api_call: $1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "${url}" ]]; then
|
|
common::fail "http::api_call requires --url"
|
|
fi
|
|
|
|
if common::is_dry_run; then
|
|
common::log_info "[dry-run] API ${method} ${url}"
|
|
return 0
|
|
fi
|
|
|
|
local tool
|
|
if ! tool="$(http::detect_download_tool)"; then
|
|
common::fail "No HTTP client available (install curl or wget)"
|
|
fi
|
|
|
|
local -a cmd
|
|
if [[ "${tool}" == "curl" ]]; then
|
|
cmd=(curl -fsSL)
|
|
[[ "${insecure}" == true ]] && cmd+=(-k)
|
|
[[ -n "${method}" ]] && cmd+=(-X "${method}")
|
|
if [[ -n "${body}" ]]; then
|
|
cmd+=(-d "${body}")
|
|
fi
|
|
if [[ -n "${token}" ]]; then
|
|
headers+=("X-API-Token: ${token}")
|
|
fi
|
|
if [[ -n "${bearer}" ]]; then
|
|
headers+=("Authorization: Bearer ${bearer}")
|
|
fi
|
|
local header
|
|
for header in "${headers[@]}"; do
|
|
cmd+=(-H "${header}")
|
|
done
|
|
cmd+=("${url}")
|
|
else
|
|
cmd=(wget -qO-)
|
|
[[ "${insecure}" == true ]] && cmd+=(--no-check-certificate)
|
|
[[ -n "${method}" ]] && cmd+=(--method="${method}")
|
|
if [[ -n "${body}" ]]; then
|
|
cmd+=(--body-data="${body}")
|
|
fi
|
|
if [[ -n "${token}" ]]; then
|
|
cmd+=(--header="X-API-Token: ${token}")
|
|
fi
|
|
if [[ -n "${bearer}" ]]; then
|
|
cmd+=(--header="Authorization: Bearer ${bearer}")
|
|
fi
|
|
local header
|
|
for header in "${headers[@]}"; do
|
|
cmd+=(--header="${header}")
|
|
done
|
|
cmd+=("${url}")
|
|
fi
|
|
|
|
common::run_capture --label "api ${method} ${url}" -- "${cmd[@]}"
|
|
}
|
|
|
|
# http::get_github_latest_release owner/repo
|
|
# Echoes the latest release tag for a GitHub repository.
|
|
http::get_github_latest_release() {
|
|
local repo="${1:-}"
|
|
if [[ -z "${repo}" ]]; then
|
|
common::fail "http::get_github_latest_release requires owner/repo argument"
|
|
fi
|
|
|
|
local response
|
|
response="$(http::api_call --url "https://api.github.com/repos/${repo}/releases/latest" --header "Accept: application/vnd.github+json" 2>/dev/null || true)"
|
|
if [[ -z "${response}" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
if [[ "${response}" =~ \"tag_name\"[[:space:]]*:[[:space:]]*\"([^\"]+)\" ]]; then
|
|
printf '%s\n' "${BASH_REMATCH[1]}"
|
|
return 0
|
|
fi
|
|
|
|
if [[ "${response}" =~ \"name\"[[:space:]]*:[[:space:]]*\"([^\"]+)\" ]]; then
|
|
printf '%s\n' "${BASH_REMATCH[1]}"
|
|
return 0
|
|
fi
|
|
|
|
common::log_warn "Unable to parse GitHub release tag for ${repo}"
|
|
return 1
|
|
}
|
|
|
|
# http::parse_bool value
|
|
# Parses truthy/falsy strings and prints canonical true/false.
|
|
http::parse_bool() {
|
|
local input="${1:-}"
|
|
local lowered="${input,,}"
|
|
case "${lowered}" in
|
|
1|true|yes|y|on)
|
|
printf 'true\n'
|
|
return 0
|
|
;;
|
|
0|false|no|n|off|"")
|
|
printf 'false\n'
|
|
return 0
|
|
;;
|
|
esac
|
|
return 1
|
|
}
|