#!/usr/bin/env bash # shellcheck shell=bash # # Shared helpers sourced by every script in this directory. # . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/common.sh" # # Requires bash 4+ and coreutils. set -euo pipefail # ---- paths ---------------------------------------------------------- LOCAL_BIN="${HOME}/.local/bin" CRED_HELPER="${LOCAL_BIN}/git-credential-forge" # Canonical OAuth auth store: matches # forge-stack-devpi-gateway-gitea/client_auth.auth_store_path() exactly. FORGE_AUTH_DIR="${FSDGG_RUNTIME_DIR:-${HOME}/.forge-stack-devpi-gateway-gitea}" FORGE_AUTH_FILE="${FSDGG_AUTH_STORE_PATH:-${FORGE_AUTH_DIR}/client-auth.json}" # ---- colors / logging ---------------------------------------------- # ANSI enabled only when stderr is a TTY, NO_COLOR is unset, and # TERM != "dumb"; empty strings otherwise (piped output byte-identical). if [ -t 2 ] && [ -z "${NO_COLOR:-}" ] && [ "${TERM:-dumb}" != "dumb" ]; then _FC_RESET=$'\e[0m' _FC_BOLD=$'\e[1m' _FC_DIM=$'\e[2m' _FC_RED=$'\e[31m' _FC_GREEN=$'\e[32m' _FC_YELLOW=$'\e[33m' _FC_CYAN=$'\e[36m' _FC_MAGENTA=$'\e[35m' else _FC_RESET=''; _FC_BOLD=''; _FC_DIM='' _FC_RED=''; _FC_GREEN=''; _FC_YELLOW='' _FC_CYAN=''; _FC_MAGENTA='' fi # Width-6 tag inside ANSI envelope: pads "[ok]", "[err]" etc. to 6 # visible columns so subsequent %-wNs fields stay aligned. _fc_tag() { printf '%s%-6s%s' "$1" "[$2]" "$_FC_RESET"; } info() { printf '%s %s\n' "$(_fc_tag "$_FC_CYAN" info)" "$*" >&2; } ok() { printf '%s %s\n' "$(_fc_tag "$_FC_GREEN" ok )" "$*" >&2; } warn() { printf '%s %s\n' "$(_fc_tag "$_FC_YELLOW" warn)" "$*" >&2; } err() { printf '%s %s\n' "$(_fc_tag "$_FC_RED" err )" "$*" >&2; } step() { printf '\n%s==>%s %s%s%s\n' "$_FC_BOLD" "$_FC_RESET" "$_FC_BOLD" "$*" "$_FC_RESET" >&2; } note() { printf '%s%s%s\n' "$_FC_DIM" "$*" "$_FC_RESET" >&2; } die() { err "$@"; exit 1; } # ---- repo locator --------------------------------------------------- repo_root() { local here here="$(cd "$(dirname "${BASH_SOURCE[1]:-$0}")" && pwd -P)" local d="$here" for _ in 1 2 3; do if [ -f "$d/Justfile" ]; then printf '%s\n' "$d" return 0 fi d="$(dirname "$d")" done die "cannot locate repo root (Justfile) from $here" } # Load $repo/.env into the environment. # Existing environment values take precedence (matches just dotenv-load). load_env() { local root env line key rest root="$(repo_root)" env="$root/.env" [ -f "$env" ] || return 0 while IFS= read -r line || [ -n "$line" ]; do # Strip CR from CRLF files and leading whitespace. line="${line%$'\r'}" line="${line#"${line%%[![:space:]]*}"}" case "$line" in ''|'#'*) continue ;; *=*) ;; *) continue ;; esac # Optional leading `export `. line="${line#export }" key="${line%%=*}" rest="${line#*=}" # Skip malformed keys. case "$key" in *[!A-Za-z0-9_]*|'') continue ;; esac # Existing env values win. if [ -n "${!key+x}" ]; then continue fi # Strip matching surrounding single or double quotes. case "$rest" in \"*\") rest="${rest#\"}"; rest="${rest%\"}" ;; \'*\') rest="${rest#\'}"; rest="${rest%\'}" ;; esac export "$key=$rest" done < "$env" } # ---- prereq checks -------------------------------------------------- require_cmd() { local cmd="$1" hint="${2:-}" if ! command -v "$cmd" >/dev/null 2>&1; then [ -n "$hint" ] && warn "install hint: $hint" die "required command not found: $cmd" fi } require_env() { local name="$1" if [ -z "${!name:-}" ]; then die "environment variable '$name' is unset. Run 'just init-env' and set it in .env." fi }