RunVSAgent/scripts/lib/common.sh
2025-08-01 20:21:08 +08:00

284 lines
No EOL
6.5 KiB
Bash

#!/bin/bash
# Common utility functions for build scripts
# This file provides cross-platform compatible utility functions
# Prevent multiple sourcing
if [[ -n "${_COMMON_SH_LOADED:-}" ]]; then
return 0
fi
_COMMON_SH_LOADED=1
# Color codes for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[0;33m'
readonly BLUE='\033[0;34m'
readonly PURPLE='\033[0;35m'
readonly CYAN='\033[0;36m'
readonly NC='\033[0m' # No Color
# Global variables
SCRIPT_DIR=""
PROJECT_ROOT=""
VERBOSE=false
DRY_RUN=false
# Project structure constants
export VSCODE_SUBMODULE_PATH="deps/vscode"
export PLUGIN_SUBMODULE_PATH="deps/roo-code"
export EXTENSION_HOST_DIR="extension_host"
# Logging functions
log_info() {
echo -e "${GREEN}[INFO]${NC} $1" >&2
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1" >&2
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
log_debug() {
if [[ "$VERBOSE" == "true" ]]; then
echo -e "${BLUE}[DEBUG]${NC} $1" >&2
fi
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1" >&2
}
log_step() {
echo -e "${CYAN}[STEP]${NC} $1" >&2
}
# Error handling
die() {
log_error "$1"
exit "${2:-1}"
}
# Check if command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Check command result
check_result() {
local exit_code=$?
local error_msg="$1"
local success_msg="$2"
if [[ $exit_code -ne 0 ]]; then
die "$error_msg" $exit_code
elif [[ -n "$success_msg" ]]; then
log_success "$success_msg"
fi
}
# Ensure directory exists
ensure_dir() {
local dir="$1"
if [[ ! -d "$dir" ]]; then
if [[ "$DRY_RUN" == "true" ]]; then
log_debug "Would create directory: $dir"
else
mkdir -p "$dir"
check_result "Failed to create directory: $dir" "Created directory: $dir"
fi
fi
}
# Remove directory safely
remove_dir() {
local dir="$1"
if [[ -d "$dir" ]]; then
if [[ "$DRY_RUN" == "true" ]]; then
log_debug "Would remove directory: $dir"
else
rm -rf "$dir"
check_result "Failed to remove directory: $dir" "Removed directory: $dir"
fi
fi
}
# Copy files/directories
copy_files() {
local src="$1"
local dest="$2"
local description="${3:-files}"
if [[ "$DRY_RUN" == "true" ]]; then
log_debug "Would copy $description from $src to $dest"
else
cp -r "$src" "$dest"
check_result "Failed to copy $description from $src to $dest" "Copied $description successfully"
fi
}
# Execute command with logging
execute_cmd() {
local cmd="$1"
local description="${2:-command}"
log_debug "Executing: $cmd"
if [[ "$DRY_RUN" == "true" ]]; then
log_debug "Would execute: $cmd"
return 0
fi
if [[ "$VERBOSE" == "true" ]]; then
eval "$cmd"
else
eval "$cmd" >/dev/null
fi
local exit_code=$?
if [[ $exit_code -ne 0 ]]; then
log_error "Command execution failed with exit code $exit_code: $cmd"
die "Failed to execute $description" $exit_code
else
log_success "Successfully executed $description"
fi
}
# Find files with pattern
find_files() {
local dir="$1"
local pattern="$2"
local max_depth="${3:-1}"
find "$dir" -maxdepth "$max_depth" -name "$pattern" -type f 2>/dev/null
}
# Get latest file matching pattern
get_latest_file() {
local dir="$1"
local pattern="$2"
find_files "$dir" "$pattern" | sort -r | head -n 1
}
# Platform detection
get_platform() {
case "$(uname -s)" in
Darwin*) echo "macos" ;;
Linux*) echo "linux" ;;
CYGWIN*|MINGW*|MSYS*) echo "windows" ;;
*) echo "unknown" ;;
esac
}
# Check if running on specific platform
is_macos() {
[[ "$(get_platform)" == "macos" ]]
}
is_linux() {
[[ "$(get_platform)" == "linux" ]]
}
is_windows() {
[[ "$(get_platform)" == "windows" ]]
}
# Validate environment
validate_environment() {
log_step "Validating environment..."
# Check required commands
local required_commands=("git" "node" "npm")
for cmd in "${required_commands[@]}"; do
if ! command_exists "$cmd"; then
die "Required command not found: $cmd"
fi
log_debug "Found command: $cmd"
done
# Check Node.js version
local node_version
node_version=$(node --version | sed 's/v//')
local required_node_version="16.0.0"
if ! version_gte "$node_version" "$required_node_version"; then
die "Node.js version $node_version is too old. Required: $required_node_version+"
fi
log_success "Environment validation passed"
}
# Version comparison
version_gte() {
local version1="$1"
local version2="$2"
# Simple version comparison (works for most cases)
printf '%s\n%s\n' "$version2" "$version1" | sort -V -C
}
# Parse command line arguments
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
-v|--verbose)
VERBOSE=true
shift
;;
-n|--dry-run)
DRY_RUN=true
shift
;;
-h|--help)
show_help
exit 0
;;
*)
# Unknown option
log_warn "Unknown option: $1"
shift
;;
esac
done
}
# Show help (to be overridden by individual scripts)
show_help() {
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " -v, --verbose Enable verbose output"
echo " -n, --dry-run Show what would be done without executing"
echo " -h, --help Show this help message"
}
# Cleanup function (to be called on script exit)
cleanup() {
local exit_code=$?
if [[ $exit_code -ne 0 ]]; then
log_error "Script failed with exit code $exit_code"
fi
return $exit_code
}
# Set up trap for cleanup
trap cleanup EXIT
# Initialize common variables when sourced (only if not already initialized)
if [[ -z "${_INIT_COMMON_CALLED:-}" ]]; then
# Get the directory of the script that sourced this file
if [[ -z "${SCRIPT_DIR:-}" ]]; then
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[1]}")" && pwd)"
export SCRIPT_DIR
fi
# PROJECT_ROOT should be set by the main script, don't override it
if [[ -z "${PROJECT_ROOT:-}" ]]; then
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
export PROJECT_ROOT
fi
_INIT_COMMON_CALLED=1
fi