#!/bin/bash # Universal yq wrapper - handles both Python and Go versions # Provides consistent interface regardless of which yq is installed set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2 } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" >&2 } log_debug() { if [[ "${YQ_DEBUG:-}" == "1" ]]; then echo -e "${GREEN}[DEBUG]${NC} $1" >&2 fi } detect_yq_type() { if ! command -v yq &> /dev/null; then log_error "yq not found. Please install yq first." log_error " Go version: https://github.com/mikefarah/yq" log_error " Or run: requirements/install.sh --yq-only" return 2 fi # Check version string local version_output version_output=$(yq --version 2>&1) if echo "$version_output" | grep -q "mikefarah"; then echo "go" return 0 elif echo "$version_output" | grep -qi "kislyuk\|jq"; then echo "python" return 0 else # Feature detection fallback if echo 'test: value' | yq eval '.test = "new"' - &>/dev/null 2>&1; then echo "go" return 0 elif echo 'test: value' | yq -y '.test = "new"' - &>/dev/null 2>&1; then echo "python" return 0 else log_error "Unknown yq version. Cannot determine type." return 1 fi fi } yq_set() { local file="$1" local path="$2" local value="$3" if [[ -z "$file" ]] || [[ -z "$path" ]] || [[ -z "$value" ]]; then log_error "Usage: yq_set " return 1 fi if [[ ! -f "$file" ]]; then log_error "File not found: $file" return 1 fi local yq_type yq_type=$(detect_yq_type) || return $? log_debug "Detected yq type: $yq_type" log_debug "Setting $path = \"$value\" in $file" case "$yq_type" in go) yq eval -i "${path} = \"${value}\"" "$file" ;; python) local temp_file="${file}.tmp.$$" yq -y "${path} = \"${value}\"" "$file" > "$temp_file" mv "$temp_file" "$file" ;; *) log_error "Unknown yq type: $yq_type" return 1 ;; esac } yq_get() { local file="$1" local path="$2" if [[ -z "$file" ]] || [[ -z "$path" ]]; then log_error "Usage: yq_get " return 1 fi if [[ ! -f "$file" ]]; then log_error "File not found: $file" return 1 fi local yq_type yq_type=$(detect_yq_type) || return $? log_debug "Detected yq type: $yq_type" log_debug "Getting $path from $file" case "$yq_type" in go) yq eval "$path" "$file" ;; python) yq -r "$path" "$file" ;; *) log_error "Unknown yq type: $yq_type" return 1 ;; esac } yq_delete() { local file="$1" local path="$2" if [[ -z "$file" ]] || [[ -z "$path" ]]; then log_error "Usage: yq_delete " return 1 fi if [[ ! -f "$file" ]]; then log_error "File not found: $file" return 1 fi local yq_type yq_type=$(detect_yq_type) || return $? log_debug "Detected yq type: $yq_type" log_debug "Deleting $path from $file" case "$yq_type" in go) yq eval -i "del(${path})" "$file" ;; python) local temp_file="${file}.tmp.$$" yq -y "del(${path})" "$file" > "$temp_file" mv "$temp_file" "$file" ;; *) log_error "Unknown yq type: $yq_type" return 1 ;; esac } yq_merge() { local file1="$1" local file2="$2" local output="$3" if [[ -z "$file1" ]] || [[ -z "$file2" ]]; then log_error "Usage: yq_merge [output_file]" return 1 fi if [[ ! -f "$file1" ]] || [[ ! -f "$file2" ]]; then log_error "Input files not found" return 1 fi local yq_type yq_type=$(detect_yq_type) || return $? log_debug "Detected yq type: $yq_type" log_debug "Merging $file1 and $file2" case "$yq_type" in go) if [[ -n "$output" ]]; then yq eval-all '. as $item ireduce ({}; . * $item)' "$file1" "$file2" > "$output" else yq eval-all '. as $item ireduce ({}; . * $item)' "$file1" "$file2" fi ;; python) log_warn "Merge operation not fully supported with Python yq" log_warn "Consider installing Go-based yq: requirements/install.sh --yq-only" if [[ -n "$output" ]]; then yq -y -s '.[0] * .[1]' "$file1" "$file2" > "$output" else yq -y -s '.[0] * .[1]' "$file1" "$file2" fi ;; *) log_error "Unknown yq type: $yq_type" return 1 ;; esac } show_usage() { cat << EOF Usage: $0 Universal yq wrapper - works with both Python and Go versions of yq COMMANDS: set Set a value in YAML file get Get a value from YAML file delete Delete a key from YAML file merge [out] Merge two YAML files detect Detect which yq version is installed EXAMPLES: $0 set config.yaml '.citation.style' 'unsrtnat' $0 get config.yaml '.citation.style' $0 delete config.yaml '.old_key' $0 merge base.yaml override.yaml output.yaml $0 detect ENVIRONMENT: YQ_DEBUG=1 Enable debug output EOF } main() { case "${1:-}" in set) shift yq_set "$@" ;; get) shift yq_get "$@" ;; delete|del) shift yq_delete "$@" ;; merge) shift yq_merge "$@" ;; detect) local yq_type yq_type=$(detect_yq_type) || exit $? echo "Detected yq type: $yq_type" yq --version ;; -h|--help|help) show_usage exit 0 ;; "") log_error "No command specified" show_usage exit 1 ;; *) log_error "Unknown command: $1" show_usage exit 1 ;; esac } main "$@"