RunVSAgent/scripts/setup.sh
2025-08-29 12:33:06 +08:00

523 lines
No EOL
16 KiB
Bash
Executable file

#!/bin/bash
# Setup script for RunVSAgent project
# This script initializes the development environment and dependencies
set -euo pipefail
# Source common utilities
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/common.sh"
source "$SCRIPT_DIR/lib/build.sh"
# Script configuration
readonly SCRIPT_NAME="setup.sh"
readonly SCRIPT_VERSION="1.0.0"
readonly PATCH_FILE="deps/patches/vscode/feature-cline-ai.patch"
# Setup options
FORCE_REINSTALL=false
SKIP_SUBMODULES=false
SKIP_DEPENDENCIES=false
APPLY_PATCHES=true
# Show help for this script
show_help() {
cat << EOF
$SCRIPT_NAME - Setup development environment for RunVSAgent
USAGE:
$SCRIPT_NAME [OPTIONS]
DESCRIPTION:
DESCRIPTION:
This script initializes the development environment by:
- Validating system requirements (including Git LFS)
- Setting up Git LFS for large file handling
- Initializing git submodules
- Installing project dependencies
- Applying necessary patches
- Setting up build environment
OPTIONS:
-f, --force Force reinstall of dependencies
-s, --skip-submodules Skip git submodule initialization
-d, --skip-deps Skip dependency installation
-p, --no-patches Skip applying patches
-v, --verbose Enable verbose output
-n, --dry-run Show what would be done without executing
-h, --help Show this help message
EXAMPLES:
$SCRIPT_NAME # Full setup
$SCRIPT_NAME --force # Force reinstall everything
$SCRIPT_NAME --skip-deps # Skip dependency installation
$SCRIPT_NAME --verbose # Verbose output
ENVIRONMENT:
NODE_VERSION_MIN Minimum Node.js version (default: 16.0.0)
SKIP_VALIDATION Skip environment validation if set to 'true'
REQUIREMENTS:
- Git LFS must be installed for handling large files
- Run 'git lfs install' manually if setup fails
EXIT CODES:
0 Success
1 General error
2 Environment validation failed
3 Dependency installation failed
4 Patch application failed
EOF
}
# Parse command line arguments
parse_setup_args() {
while [[ $# -gt 0 ]]; do
case $1 in
-f|--force)
FORCE_REINSTALL=true
shift
;;
-s|--skip-submodules)
SKIP_SUBMODULES=true
shift
;;
-d|--skip-deps)
SKIP_DEPENDENCIES=true
shift
;;
-p|--no-patches)
APPLY_PATCHES=false
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
-n|--dry-run)
DRY_RUN=true
shift
;;
-h|--help)
show_help
exit 0
;;
*)
log_error "Unknown option: $1"
log_info "Use --help for usage information"
exit 1
;;
esac
done
}
# Validate system requirements
validate_system_requirements() {
log_step "Validating system requirements..."
# Skip validation if requested
if [[ "${SKIP_VALIDATION:-false}" == "true" ]]; then
log_warn "Skipping environment validation (SKIP_VALIDATION=true)"
return 0
fi
# Validate basic environment
validate_environment
# Check for additional development tools
local dev_tools=("git" "unzip" "curl")
for tool in "${dev_tools[@]}"; do
if ! command_exists "$tool"; then
die "Required development tool not found: $tool" 2
fi
log_debug "Found development tool: $tool"
done
# Check for Git LFS
if ! command_exists "git-lfs"; then
die "Git LFS not found. Please install Git LFS: https://git-lfs.github.io/" 2
fi
log_debug "Found Git LFS"
# Check Git configuration
if ! git config user.name >/dev/null 2>&1; then
log_warn "Git user.name not configured. Run: git config --global user.name 'Your Name'"
fi
if ! git config user.email >/dev/null 2>&1; then
log_warn "Git user.email not configured. Run: git config --global user.email 'your.email@example.com'"
fi
# Check available disk space (at least 2GB)
local available_space
if is_macos; then
available_space=$(df -g "$PROJECT_ROOT" | awk 'NR==2 {print $4}')
else
available_space=$(df -BG "$PROJECT_ROOT" | awk 'NR==2 {print $4}' | sed 's/G//')
fi
if [[ "$available_space" -lt 2 ]]; then
log_warn "Low disk space: ${available_space}GB available. At least 2GB recommended."
fi
log_success "System requirements validated"
}
# Initialize Git LFS
setup_git_lfs() {
log_step "Setting up Git LFS..."
cd "$PROJECT_ROOT"
# Check if Git LFS is already initialized
if git lfs env >/dev/null 2>&1; then
log_debug "Git LFS environment already configured"
else
log_info "Initializing Git LFS..."
execute_cmd "git lfs install" "Git LFS installation"
fi
# Check if .gitattributes exists and has LFS entries
if [[ -f ".gitattributes" ]]; then
local lfs_files
lfs_files=$(grep -c "filter=lfs" .gitattributes 2>/dev/null || echo "0")
if [[ "$lfs_files" -gt 0 ]]; then
log_info "Found $lfs_files LFS file pattern(s) in .gitattributes"
# Pull LFS files
log_info "Pulling LFS files..."
execute_cmd "git lfs pull" "Git LFS pull"
# Verify LFS files
log_info "Verifying LFS files..."
if git lfs ls-files >/dev/null 2>&1; then
local lfs_file_count
lfs_file_count=$(git lfs ls-files | wc -l | tr -d ' ')
if [[ "$lfs_file_count" -gt 0 ]]; then
log_success "Successfully pulled $lfs_file_count LFS file(s)"
git lfs ls-files | while read -r line; do
log_debug "LFS file: $line"
done
else
log_warn "No LFS files found in repository"
fi
fi
else
log_info "No LFS file patterns found in .gitattributes"
fi
else
log_warn "No .gitattributes file found, skipping LFS file pull"
fi
log_success "Git LFS setup completed"
}
# Initialize git submodules
setup_submodules() {
if [[ "$SKIP_SUBMODULES" == "true" ]]; then
log_info "Skipping git submodule initialization"
return 0
fi
log_step "Setting up git submodules..."
cd "$PROJECT_ROOT"
# Check if .gitmodules exists
if [[ ! -f ".gitmodules" ]]; then
log_warn "No .gitmodules file found, skipping submodule setup"
return 0
fi
# Initialize and update submodules
if [[ "$FORCE_REINSTALL" == "true" ]]; then
log_info "Force reinstalling submodules..."
execute_cmd "git submodule deinit --all -f" "submodule deinit"
fi
execute_cmd "git submodule init" "submodule init"
execute_cmd "git submodule update --recursive" "submodule update"
# Switch to development branch if specified
local vscode_dir="$PROJECT_ROOT/$VSCODE_SUBMODULE_PATH"
if [[ -d "$vscode_dir" ]]; then
cd "$vscode_dir"
if git show-ref --verify --quiet "refs/heads/$VSCODE_BRANCH"; then
execute_cmd "git checkout $VSCODE_BRANCH" "checkout $VSCODE_BRANCH"
elif git show-ref --verify --quiet "refs/remotes/origin/$VSCODE_BRANCH"; then
execute_cmd "git checkout -b $VSCODE_BRANCH origin/$VSCODE_BRANCH" "checkout $VSCODE_BRANCH"
else
log_warn "Branch $VSCODE_BRANCH not found, staying on current branch"
fi
fi
log_success "Git submodules set up"
}
# Install project dependencies
install_dependencies() {
if [[ "$SKIP_DEPENDENCIES" == "true" ]]; then
log_info "Skipping dependency installation"
return 0
fi
log_step "Installing project dependencies..."
# Install extension host dependencies
if [[ -d "$PROJECT_ROOT/$EXTENSION_HOST_DIR" && -f "$PROJECT_ROOT/$EXTENSION_HOST_DIR/package.json" ]]; then
log_info "Installing extension host dependencies..."
cd "$PROJECT_ROOT/$EXTENSION_HOST_DIR"
if [[ "$FORCE_REINSTALL" == "true" ]]; then
remove_dir "node_modules"
[[ -f "package-lock.json" ]] && rm -f "package-lock.json"
fi
execute_cmd "npm install" "extension host dependencies installation"
fi
# Install VSCode extension dependencies
local vscode_dir="$PROJECT_ROOT/$PLUGIN_SUBMODULE_PATH"
if [[ -d "$vscode_dir" && -f "$vscode_dir/package.json" ]]; then
log_info "Installing VSCode extension dependencies..."
cd "$vscode_dir"
if [[ "$FORCE_REINSTALL" == "true" ]]; then
remove_dir "node_modules"
[[ -f "package-lock.json" ]] && rm -f "package-lock.json"
[[ -f "pnpm-lock.yaml" ]] && rm -f "pnpm-lock.yaml"
fi
# Use pnpm if available and lock file exists
local pkg_manager="npm"
if command_exists "pnpm" && [[ -f "pnpm-lock.yaml" ]]; then
pkg_manager="pnpm"
fi
execute_cmd "$pkg_manager install" "VSCode extension dependencies installation"
fi
log_success "Dependencies installed"
}
# Apply project patches and copy VSCode files
apply_patches() {
if [[ "$APPLY_PATCHES" != "true" ]]; then
log_info "Skipping patch application"
return 0
fi
log_step "Applying project patches..."
local patch_file="$PROJECT_ROOT/$PATCH_FILE"
if [[ ! -f "$patch_file" ]]; then
log_warn "Patch file not found: $patch_file"
return 0
fi
# Use deps/vscode as the source directory (like init.sh)
local vscode_source_dir="$PROJECT_ROOT/deps/vscode"
local vscode_target_dir="$PROJECT_ROOT/$EXTENSION_HOST_DIR/vscode"
if [[ ! -d "$vscode_source_dir" ]]; then
log_warn "VSCode source directory not found: $vscode_source_dir"
return 0
fi
cd "$vscode_source_dir"
execute_cmd "git clean -dfx && git reset --hard" "reset VSCode source"
# Check if patch can be applied
if git apply --check "$patch_file" 2>/dev/null; then
log_info "Applying patch to VSCode source..."
execute_cmd "git apply '$patch_file'" "patch application"
# Copy src/* to target directory (exactly like init.sh)
log_info "Copying src/* to $vscode_target_dir..."
# Use the same logic as init.sh: mkdir -p $TARGET_DIR || rm -rf "$TARGET_DIR"/*
if [[ ! -d "$vscode_target_dir" ]]; then
ensure_dir "$vscode_target_dir"
else
rm -rf "$vscode_target_dir"/*
fi
if [[ -d "$vscode_source_dir/src" ]]; then
execute_cmd "cp -r src/* '$vscode_target_dir/'" "VSCode files copy"
else
log_error "VSCode src directory not found in $vscode_source_dir"
exit 4
fi
# Reset the source repository (like init.sh)
log_info "Resetting VSCode source repository..."
execute_cmd "git reset --hard" "git reset"
execute_cmd "git clean -fd" "git clean"
log_success "Patch applied and VSCode files copied successfully"
else
# Check if patch is already applied
if git apply --reverse --check "$patch_file" 2>/dev/null; then
log_info "Patch appears to already be applied, copying files..."
# Still copy the files even if patch is already applied
# Use the same logic as init.sh: mkdir -p $TARGET_DIR || rm -rf "$TARGET_DIR"/*
if [[ ! -d "$vscode_target_dir" ]]; then
ensure_dir "$vscode_target_dir"
else
rm -rf "$vscode_target_dir"/*
fi
if [[ -d "$vscode_source_dir/src" ]]; then
execute_cmd "cp -r src/* '$vscode_target_dir/'" "VSCode files copy"
log_success "VSCode files copied successfully"
else
log_error "VSCode src directory not found in $vscode_source_dir"
exit 4
fi
else
log_error "Patch cannot be applied (conflicts may exist)"
log_info "You may need to resolve conflicts manually"
exit 4
fi
fi
}
# Setup development environment
setup_dev_environment() {
log_step "Setting up development environment..."
# Create necessary directories
local dirs_to_create=(
"$PROJECT_ROOT/logs"
"$PROJECT_ROOT/tmp"
"$PROJECT_ROOT/build"
)
for dir in "${dirs_to_create[@]}"; do
ensure_dir "$dir"
done
# Set up Git hooks if they exist
local hooks_dir="$PROJECT_ROOT/.githooks"
if [[ -d "$hooks_dir" ]]; then
log_info "Setting up Git hooks..."
execute_cmd "git config core.hooksPath .githooks" "Git hooks setup"
# Make hooks executable
find "$hooks_dir" -type f -exec chmod +x {} \;
fi
# Create environment file template if it doesn't exist
local env_file="$PROJECT_ROOT/.env.local"
if [[ ! -f "$env_file" ]]; then
log_info "Creating environment file template..."
cat > "$env_file" << 'EOF'
# Local environment configuration
# Copy this file to .env.local and customize as needed
# Build configuration
# BUILD_MODE=release
# VERBOSE=false
# Development settings
# SKIP_VALIDATION=false
# USE_DEBUG_BUILD=false
# Paths (usually auto-detected)
# PROJECT_ROOT=
# VSCODE_DIR=
EOF
log_info "Created $env_file - customize as needed"
fi
log_success "Development environment set up"
}
# Verify setup
verify_setup() {
log_step "Verifying setup..."
local errors=0
# Check critical directories
local critical_dirs=(
"$PROJECT_ROOT/$EXTENSION_HOST_DIR"
"$PROJECT_ROOT/$IDEA_DIR"
)
for dir in "${critical_dirs[@]}"; do
if [[ ! -d "$dir" ]]; then
log_error "Critical directory missing: $dir"
((errors++))
fi
done
# Check for VSCode submodule
local vscode_dir="$PROJECT_ROOT/$VSCODE_SUBMODULE_PATH"
if [[ ! -d "$vscode_dir" ]] || [[ ! "$(ls -A "$vscode_dir" 2>/dev/null)" ]]; then
log_error "VSCode submodule not properly initialized: $vscode_dir"
((errors++))
fi
# Check for package.json files
local package_files=(
"$PROJECT_ROOT/$EXTENSION_HOST_DIR/package.json"
)
for file in "${package_files[@]}"; do
if [[ ! -f "$file" ]]; then
log_error "Package file missing: $file"
((errors++))
fi
done
# Check for build tools
if [[ -d "$PROJECT_ROOT/$IDEA_DIR" ]]; then
if [[ ! -f "$PROJECT_ROOT/$IDEA_DIR/build.gradle" && ! -f "$PROJECT_ROOT/$IDEA_DIR/build.gradle.kts" ]]; then
log_warn "No Gradle build file found in IDEA directory"
fi
fi
if [[ $errors -gt 0 ]]; then
log_error "Setup verification failed with $errors errors"
exit 3
fi
log_success "Setup verification passed"
}
# Main setup function
main() {
log_info "Starting RunVSAgent development environment setup..."
log_info "Script: $SCRIPT_NAME v$SCRIPT_VERSION"
log_info "Platform: $(get_platform)"
log_info "Project root: $PROJECT_ROOT"
if [[ "$DRY_RUN" == "true" ]]; then
log_warn "DRY RUN MODE - No changes will be made"
fi
# Parse arguments
parse_setup_args "$@"
# Run setup steps
validate_system_requirements
setup_git_lfs
setup_submodules
install_dependencies
apply_patches
setup_dev_environment
verify_setup
log_success "Setup completed successfully!"
log_info ""
log_info "Next steps:"
log_info " 1. Run './scripts/build.sh' to build the project"
log_info " 2. Run './scripts/build.sh --help' for build options"
log_info " 3. Check the generated files in the idea/ directory"
log_info ""
log_info "For more information, see the project documentation."
}
# Run main function with all arguments
main "$@"