#!/bin/bash -e echo "Starting deployment process..." # Enable jemalloc for reduced memory usage and latency if [ -z "${LD_PRELOAD+x}" ]; then LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit) export LD_PRELOAD fi # Ensure all required directories exist with proper permissions echo "Setting up directories..." echo "Setting up directories......" mkdir -p /rails/code /rails/storage /rails/public/uploads /rails/public/assets /rails/public/vite /rails/node_modules /rails/log /rails/tmp/pids /rails/tmp/cache /rails/tmp/sockets echo "after Setting up directories..." # Set very permissive permissions for all directories # chmod -R 777 /rails/storage /rails/public/uploads /rails/public/assets /rails/public/vite /rails/node_modules /rails/log /rails/tmp # chown -R rails:rails /rails/storage /rails/public/uploads /rails/public/assets /rails/public/vite /rails/node_modules /rails/log /rails/tmp # Verify the storage directory exists and has proper permissions # echo "Verifying storage directory permissions:" # ls -la /rails | grep storage # # Test if storage directory is writable # echo "Testing storage directory write access..." # if touch /rails/storage/test_write_access; then # echo "✅ Storage directory is writable" # rm /rails/storage/test_write_access # else # echo "❌ ERROR: Cannot write to storage directory. Check volume mount and permissions." # # Continue anyway, but log the error # fi # Wait for external volumes to be properly mounted sleep 5 # 设置SSH配置以解决Host key verification failed问题 echo "Setting up SSH configuration..." # 打印当前用户和环境 echo "Current user: $(whoami)" echo "Home directory: $HOME" # 检查SSH密钥目录 echo "Checking SSH keys directory..." ls -la /root/.ssh/ || echo "No SSH keys directory found" # 检查密钥文件权限 echo "Checking SSH key permissions..." find /root/.ssh -type f -name "id_*" -exec ls -la {} \; || echo "No SSH keys found" # 确保密钥有正确的权限 echo "Setting correct permissions on SSH keys..." find /root/.ssh -type f -name "id_*" -exec chmod 600 {} \; || echo "No SSH keys to chmod" # 创建一个更详细的SSH脚本,显示调试信息 mkdir -p /tmp/git-ssh cat > /tmp/git-ssh/ssh.sh << 'EOL' #!/bin/bash echo "[SSH Debug] Command: ssh $@" >&2 ssh -v -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "$@" EOL # 设置执行权限 chmod +x /tmp/git-ssh/ssh.sh # 设置GIT_SSH环境变量 export GIT_SSH="/tmp/git-ssh/ssh.sh" echo "GIT_SSH set to: $GIT_SSH" # 测试SSH连接 echo "Testing SSH connection to Git repository host..." # 从 Git 仓库地址中提取完整的用户和主机信息 GIT_USER_HOST=$(echo "${GIT_REPOSITORY}" | sed -E 's/(.*):.*/\1/') if [ "${GIT_USER_HOST}" != "${GIT_REPOSITORY}" ]; then echo "Detected Git user and host: ${GIT_USER_HOST}" # 显示当前的SSH密钥 echo "Available SSH keys:" ls -la /root/.ssh/ # 测试SSH连接,使用完整的用户@主机格式 ssh -T -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${GIT_USER_HOST} 2>&1 || { echo "SSH connection test failed with error code: $?" echo "This may cause Git operations to fail, but we'll continue anyway" } fi # Check if code directory is empty or if we need to clone/pull the repository if [ -z "${GIT_REPOSITORY}" ]; then echo "⚠️ GIT_REPOSITORY environment variable not set. Using mounted code directory." else # 检查代码目录是否为空或只包含隐藏文件 if [ "$(find /rails/code -type f -not -path "*/\.*" | wc -l)" -gt 0 ]; then echo "✅ Code directory contains non-hidden files. Checking Git repository status." # 检查是否是一个有效的Git仓库 if [ -d "/rails/code/.git" ]; then echo "Git repository exists, attempting to pull latest changes..." cd /rails/code git config --global --add safe.directory /rails/code git fetch || echo "Git fetch failed, but continuing anyway" # Check if we need to checkout a specific branch or tag if [ -n "${GIT_BRANCH}" ]; then echo "Checking out branch/tag: ${GIT_BRANCH}" git checkout ${GIT_BRANCH} || echo "Git checkout failed, but continuing anyway" git pull origin ${GIT_BRANCH} || echo "Git pull failed, but continuing anyway" else echo "No branch specified, attempting to pull latest from current branch" git pull || echo "Git pull failed, but continuing anyway" fi else echo "⚠️ Code directory is not a Git repository. Attempting to clone repository..." # 备份现有文件 echo "Backing up existing files..." mkdir -p /tmp/code_backup cp -r /rails/code/* /tmp/code_backup/ 2>/dev/null || true # 清空目录并克隆仓库 echo "Cleaning directory and cloning repository..." rm -rf /rails/code/* /rails/code/.[!.]* 2>/dev/null || true # 尝试克隆仓库 if [ -n "${GIT_BRANCH}" ]; then git clone --branch ${GIT_BRANCH} ${GIT_REPOSITORY} /rails/code || echo "Git clone failed, restoring backup" else git clone ${GIT_REPOSITORY} /rails/code || echo "Git clone failed, restoring backup" fi # 如果克隆失败,恢复备份 if [ ! -d "/rails/code/.git" ]; then echo "Git clone failed, restoring backup files..." cp -r /tmp/code_backup/* /rails/code/ 2>/dev/null || true fi # 清理备份 rm -rf /tmp/code_backup fi else echo "🔄 Code directory is empty or contains only hidden files. Setting up from Git repository: ${GIT_REPOSITORY}" # 添加 Git 安全目录配置,解决仓库所有权问题 echo "Configuring Git safe directory..." git config --global --add safe.directory /rails/code # 清空目录以确保没有隐藏文件干扰 rm -rf /rails/code/* /rails/code/.[!.]* 2>/dev/null || { echo "Error removing files: $?"; } # 检查是否安装了 git 命令 if ! command -v git &> /dev/null; then echo "⚠️ Git command not found. Skipping Git operations." else # 检查是否能访问 Git 仓库 if ! git ls-remote --quiet ${GIT_REPOSITORY} &> /tmp/git_error; then echo "⚠️ Cannot access Git repository. Error details:" cat /tmp/git_error echo "Skipping Git operations." else # 尝试克隆仓库 echo "Cloning repository..." if [ -n "${GIT_BRANCH}" ]; then if ! git clone --branch ${GIT_BRANCH} ${GIT_REPOSITORY} /rails/code 2> /tmp/git_clone_error; then echo "Git clone failed with error:" cat /tmp/git_clone_error echo "Continuing anyway, but deployment may fail." fi else if ! git clone ${GIT_REPOSITORY} /rails/code 2> /tmp/git_clone_error; then echo "Git clone failed with error:" cat /tmp/git_clone_error echo "Continuing anyway, but deployment may fail." fi fi fi fi fi echo "✅ Using code from Git repository or mounted directory" fi # Create symbolic links from code directory to Rails app directory echo "Setting up symbolic links..." cd /rails/code # Link important directories and files to maintain Rails structure for dir in app bin config db lib public vendor; do if [ -d "/rails/code/$dir" ]; then ln -sfn /rails/code/$dir /rails/$dir fi done # Link important files for file in Gemfile Gemfile.lock Rakefile config.ru package.json package-lock.json; do if [ -f "/rails/code/$file" ]; then ln -sf /rails/code/$file /rails/$file echo "Linked $file" else echo "Warning: $file not found in /rails/code" fi done # Link node_modules if it exists in the mounted volume if [ -d "/rails/node_modules" ]; then ln -sfn /rails/node_modules /rails/code/node_modules fi # If running the rails server then install dependencies and prepare the database if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then cd /rails # Install Ruby dependencies if needed echo "Installing Ruby dependencies..." if [ ! -d "${BUNDLE_PATH}/ruby" ] || [ "$FORCE_BUNDLE_INSTALL" = "true" ]; then bundle install echo "✅ Ruby dependencies installed" else echo "✅ Ruby dependencies already installed" fi # Install Node.js dependencies if needed echo "Checking for package.json..." if [ -f "/rails/package.json" ]; then echo "Found package.json, installing Node.js dependencies..." if [ ! -d "/rails/node_modules/node_modules" ] || [ "$FORCE_NPM_INSTALL" = "true" ]; then npm install echo "✅ Node.js dependencies installed" else echo "✅ Node.js dependencies already installed" fi else echo "No package.json found in /rails, skipping npm install" fi # Prepare database files with proper permissions echo "Ensuring database files exist with proper permissions..." # Create database directory with proper permissions mkdir -p /rails/storage # 注释掉可能导致权限错误的命令 # chmod -R 777 /rails/storage # chown -R rails:rails /rails/storage echo "Creating database files if they don't exist..." # Create database files if they don't exist for db_file in production.sqlite3 production_cache.sqlite3 production_queue.sqlite3 production_cable.sqlite3; do if [ ! -f "/rails/storage/$db_file" ]; then echo "Creating /rails/storage/$db_file" touch "/rails/storage/$db_file" || echo "Cannot create $db_file, but continuing anyway" # 注释掉可能导致权限错误的命令 # chmod 666 "/rails/storage/$db_file" # chown rails:rails "/rails/storage/$db_file" else echo "Database file /rails/storage/$db_file already exists" # 注释掉可能导致权限错误的命令 # chmod 666 "/rails/storage/$db_file" # chown rails:rails "/rails/storage/$db_file" fi done # Double-check permissions and ownership echo "Database directory permissions:" ls -la /rails/storage # Test if we can write to the database file echo "Testing database file write access..." if sqlite3 /rails/storage/production.sqlite3 "PRAGMA user_version;" > /dev/null 2>&1; then echo "✅ Database file is writable" else echo "❌ ERROR: Cannot write to database file. Check volume mount and permissions." # 注释掉可能导致权限错误的命令 # echo "Attempting more aggressive permission fix..." # chown -R rails:rails /rails/storage # chmod -R 777 /rails/storage # chmod 666 /rails/storage/*.sqlite3 # Try to create an empty database structure echo "Attempting to initialize empty database..." sqlite3 /rails/storage/production.sqlite3 "CREATE TABLE IF NOT EXISTS schema_migrations (version varchar(255) NOT NULL); CREATE UNIQUE INDEX unique_schema_migrations ON schema_migrations (version);" || true fi # Prepare the database with retry logic echo "Preparing database..." # First try to manually create schema_migrations table to avoid common issues echo "Attempting to create schema_migrations table manually..." for db_file in /rails/storage/production.sqlite3 /rails/storage/production_cache.sqlite3 /rails/storage/production_queue.sqlite3 /rails/storage/production_cable.sqlite3; do echo "Initializing $db_file..." sqlite3 "$db_file" "CREATE TABLE IF NOT EXISTS schema_migrations (version varchar(255) NOT NULL); CREATE UNIQUE INDEX IF NOT EXISTS unique_schema_migrations ON schema_migrations (version);" || true done # Create empty schema.rb file to help Rails recognize the database state mkdir -p /rails/db if [ ! -f "/rails/db/schema.rb" ]; then echo "Creating empty schema.rb file..." cat > /rails/db/schema.rb << 'EOL' # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # # This file is the source Rails uses to define your schema when running `bin/rails # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to # be faster and is potentially less error prone than running all of your # migrations from scratch. Old migrations may fail to apply correctly if those # migrations use external dependencies or application code. # # It's strongly recommended that you check this file into your version control system. ActiveRecord::Schema[7.1].define(version: 0) do # These are extensions that must be enabled in order to support this database end EOL fi # Check if we should skip database preparation if [ "${SKIP_DB_PREPARATION}" = "true" ]; then echo "⚠️ SKIP_DB_PREPARATION is set to true, skipping database preparation" echo "✅ Using existing database files without migrations" SUCCESS=true else # Now try to run db:prepare with multiple attempts for i in {1..3}; do echo "Database preparation attempt $i..." # Print current environment for debugging echo "Current environment:" echo "- Working directory: $(pwd)" echo "- User: $(whoami)" echo "- Storage directory contents:" ls -la /rails/storage echo "- Database config:" cat /rails/config/database.yml | grep -A 10 production # Try different database commands with increasing aggressiveness if [ $i -eq 1 ]; then echo "Trying db:prepare..." RAILS_ENV=production ./bin/rails db:prepare --trace && SUCCESS=true && break elif [ $i -eq 2 ]; then echo "Trying db:migrate:status..." RAILS_ENV=production ./bin/rails db:migrate:status --trace || true echo "Trying db:schema:load..." RAILS_ENV=production ./bin/rails db:schema:load --trace && SUCCESS=true && break else echo "Trying db:setup..." RAILS_ENV=production ./bin/rails db:setup --trace && SUCCESS=true && break fi echo "⚠️ Database preparation attempt $i failed, retrying..." # 注释掉可能导致权限错误的命令 # echo "Fixing permissions again..." # chmod -R 777 /rails/storage # chmod 666 /rails/storage/*.sqlite3 # chown -R rails:rails /rails/storage sleep 5 done fi if [ "$SUCCESS" = "true" ]; then echo "✅ Database prepared successfully" else echo "⚠️ WARNING: Database preparation failed after 3 attempts, but continuing startup" echo "The application may not function correctly until database issues are resolved" fi # Build Vite assets if needed if [ ! -d "/rails/public/vite" ] || [ -z "$(ls -A /rails/public/vite 2>/dev/null || echo empty)" ] || [ "$FORCE_VITE_BUILD" = "true" ]; then echo "Building Vite assets..." bundle exec vite build echo "✅ Vite assets built" else echo "✅ Vite assets already exist" fi # Create health check file touch /rails/tmp/healthy echo "✅ Application ready to start" fi echo "🚀 Starting application..." exec "${@}"