#!/usr/bin/env bash set -euo pipefail DOMAIN="${GODADDY_DOMAIN:-bytelyst.com}" TTL="${GODADDY_DNS_TTL:-600}" TARGET_IP="${GODADDY_DNS_TARGET_IP:-}" AUTO_IP=false DRY_RUN=false VALIDATE=false HOSTS=("api" "gitea" "admin" "tracker" "llmlab" "ollama") CONFIG_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/godaddypy/credentials.yaml" usage() { cat <<'USAGE' Upsert ByteLyst GoDaddy A records for the Azure VM cutover. Usage: scripts/godaddy-sync-bytelyst-dns.sh [options] Options: --ip
Target IPv4 address for all managed A records --auto-ip Detect the current public IP with api.ipify.org --domain GoDaddy zone to update (default: bytelyst.com) --ttl TTL for the managed A records (default: 600) --hosts Hostnames to manage (default: api,gitea,admin,tracker,llmlab,ollama) --validate Run dig validation after changes --dry-run Print the API operations without applying them -h, --help Show help Credentials: - Preferred: GODADDY_API_KEY and GODADDY_API_SECRET - Fallback: ~/.config/godaddypy/credentials.yaml Examples: scripts/godaddy-sync-bytelyst-dns.sh --ip 20.81.12.34 --validate scripts/godaddy-sync-bytelyst-dns.sh --auto-ip --dry-run USAGE } while [[ $# -gt 0 ]]; do case "$1" in --ip) TARGET_IP="${2:-}" shift 2 ;; --auto-ip) AUTO_IP=true shift ;; --domain) DOMAIN="${2:-}" shift 2 ;; --ttl) TTL="${2:-}" shift 2 ;; --hosts) IFS=',' read -r -a HOSTS <<<"${2:-}" shift 2 ;; --validate) VALIDATE=true shift ;; --dry-run) DRY_RUN=true shift ;; -h|--help) usage exit 0 ;; *) echo "Unknown arg: $1" >&2 usage exit 2 ;; esac done trim() { local value="$1" value="${value#"${value%%[![:space:]]*}"}" value="${value%"${value##*[![:space:]]}"}" printf '%s' "$value" } load_credential_from_file() { local key_name="$1" if [[ ! -f "$CONFIG_FILE" ]]; then return 1 fi sed -nE "s/^[[:space:]]*${key_name}:[[:space:]]*['\"]?([^'\"]+)['\"]?[[:space:]]*$/\\1/p" "$CONFIG_FILE" | head -n 1 } GODADDY_API_KEY="${GODADDY_API_KEY:-}" GODADDY_API_SECRET="${GODADDY_API_SECRET:-}" if [[ -z "$GODADDY_API_KEY" ]]; then GODADDY_API_KEY="$(load_credential_from_file key || true)" fi if [[ -z "$GODADDY_API_SECRET" ]]; then GODADDY_API_SECRET="$(load_credential_from_file secret || true)" fi if [[ -z "$GODADDY_API_KEY" || -z "$GODADDY_API_SECRET" ]]; then echo "Missing GoDaddy credentials. Set GODADDY_API_KEY and GODADDY_API_SECRET, or configure $CONFIG_FILE." >&2 exit 2 fi if [[ "$AUTO_IP" == true ]]; then TARGET_IP="$(curl -fsS https://api.ipify.org)" fi if [[ -z "$TARGET_IP" ]]; then echo "Missing target IP. Use --ip
or --auto-ip." >&2 exit 2 fi if [[ ! "$TARGET_IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then echo "Target IP does not look like an IPv4 address: $TARGET_IP" >&2 exit 2 fi if [[ ! "$TTL" =~ ^[0-9]+$ ]]; then echo "TTL must be numeric: $TTL" >&2 exit 2 fi if ! command -v curl >/dev/null 2>&1; then echo "curl is required." >&2 exit 1 fi api_request() { local method="$1" local path="$2" local body="${3:-}" local -a curl_args=( -fsS -X "$method" -H "Authorization: sso-key ${GODADDY_API_KEY}:${GODADDY_API_SECRET}" -H "Accept: application/json" "https://api.godaddy.com${path}" ) if [[ -n "$body" ]]; then curl_args+=(-H "Content-Type: application/json" --data "$body") fi curl "${curl_args[@]}" } printf 'Domain: %s\n' "$DOMAIN" printf 'Target IP: %s\n' "$TARGET_IP" printf 'TTL: %s\n' "$TTL" printf 'Hosts: %s\n' "$(IFS=,; printf '%s' "${HOSTS[*]}")" for raw_host in "${HOSTS[@]}"; do host="$(trim "$raw_host")" if [[ -z "$host" ]]; then continue fi path="/v1/domains/${DOMAIN}/records/A/${host}" payload=$(printf '[{"data":"%s","ttl":%s}]' "$TARGET_IP" "$TTL") echo printf 'Record: %s.%s\n' "$host" "$DOMAIN" if [[ "$DRY_RUN" == true ]]; then printf 'Dry run: PUT %s %s\n' "$path" "$payload" continue fi current_records="$(api_request GET "$path" || true)" if [[ -n "$current_records" ]]; then printf 'Existing A records: %s\n' "$current_records" else echo "Existing A records: none" fi api_request PUT "$path" "$payload" >/dev/null echo "Updated A record." done if [[ "$VALIDATE" == true ]]; then echo echo "Validation:" if ! command -v dig >/dev/null 2>&1; then echo "dig not found; skipping DNS validation." >&2 exit 0 fi for raw_host in "${HOSTS[@]}"; do host="$(trim "$raw_host")" if [[ -z "$host" ]]; then continue fi resolved_ip="$(dig +short "${host}.${DOMAIN}" | tail -n 1)" printf '%s.%s -> %s\n' "$host" "$DOMAIN" "${resolved_ip:-}" done fi