Files
chatroom/deploy_update.sh
T
2026-05-04 18:23:54 +08:00

251 lines
10 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
PROJECT_ROOT="/www/wwwroot/chat.ay.lc" # <--- 确认这里的路径是否正确
export COMPOSER_ALLOW_SUPERUSER=1
SUPERVISORCTL_BIN=""
PHP_BIN=""
for candidate in /usr/bin/supervisorctl /usr/local/bin/supervisorctl /www/server/panel/pyenv/bin/supervisorctl; do
if [ -x "$candidate" ]; then
SUPERVISORCTL_BIN="$candidate"
break
fi
done
for candidate in /www/server/php/84/bin/php /usr/bin/php /usr/local/bin/php; do
if [ -x "$candidate" ]; then
PHP_BIN="$candidate"
break
fi
done
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE} 🚀 Laravel 稳健更新脚本 (带严格检查) ${NC}"
echo -e "${BLUE}========================================${NC}"
cd "$PROJECT_ROOT" || { echo -e "${RED}❌ 无法进入项目目录:$PROJECT_ROOT${NC}"; exit 1; }
if [ -z "$PHP_BIN" ]; then
echo -e "${RED}❌ 未找到可用 PHP 可执行文件,请确认服务器已安装 PHP 8.4。${NC}"
exit 1
fi
PHP_VERSION=$("$PHP_BIN" -r 'echo PHP_VERSION;' 2>/dev/null)
echo -e "${BLUE}使用 PHP$PHP_BIN (版本:${PHP_VERSION:-unknown})${NC}"
frontend_build_required() {
local file="$1"
case "$file" in
resources/js/*|resources/css/*|resources/views/*.blade.php|resources/views/*/*.blade.php|resources/views/*/*/*.blade.php|resources/views/*/*/*/*.blade.php)
return 0
;;
package.json|package-lock.json|vite.config.*|tailwind.config.*|postcss.config.*)
return 0
;;
esac
return 1
}
# 1. Git Pull(先重置 lock 文件,避免服务器环境差异导致冲突)
echo -e "${YELLOW}[1/8] 拉取代码...${NC}"
BEFORE_REV=$(git rev-parse HEAD 2>/dev/null || echo "")
git checkout -- composer.lock package-lock.json 2>/dev/null || true
git fetch origin && git pull origin master
if [ $? -ne 0 ]; then echo -e "${RED}❌ Git 失败${NC}"; exit 1; fi
AFTER_REV=$(git rev-parse HEAD 2>/dev/null || echo "")
FRONTEND_BUILD_NEEDED=0
FRONTEND_CHANGED_FILES=""
MIGRATION_NEEDED=0
NEW_MIGRATION_FILES=""
if [ -z "$BEFORE_REV" ] || [ -z "$AFTER_REV" ]; then
FRONTEND_BUILD_NEEDED=1
FRONTEND_CHANGED_FILES="无法识别更新前后版本,保险起见执行构建"
MIGRATION_NEEDED=1
NEW_MIGRATION_FILES="无法识别更新前后版本,保险起见执行迁移检查"
elif [ "$BEFORE_REV" != "$AFTER_REV" ]; then
while IFS= read -r changed_file; do
if frontend_build_required "$changed_file"; then
FRONTEND_BUILD_NEEDED=1
FRONTEND_CHANGED_FILES="${FRONTEND_CHANGED_FILES}${changed_file}"$'\n'
fi
done < <(git diff --name-only "$BEFORE_REV" "$AFTER_REV")
while IFS=$'\t' read -r change_status changed_file extra_path; do
if [ "$change_status" = "A" ]; then
case "$changed_file" in
database/migrations/*.php)
MIGRATION_NEEDED=1
NEW_MIGRATION_FILES="${NEW_MIGRATION_FILES}${changed_file}"$'\n'
;;
esac
fi
done < <(git diff --name-status "$BEFORE_REV" "$AFTER_REV")
fi
# 2. Composer Install (关键检查点)
echo -e "${YELLOW}[2/8] 安装依赖 (Composer)...${NC}"
composer install --no-dev --optimize-autoloader --classmap-authoritative --no-interaction
COMPOSER_EXIT_CODE=$?
if [ $COMPOSER_EXIT_CODE -ne 0 ]; then
echo -e "${RED}========================================${NC}"
echo -e "${RED} ❌ 致命错误:Composer 安装失败! ${NC}"
echo -e "${RED} vendor/autoload.php 未生成,网站将不可用。 ${NC}"
echo -e "${RED} 请检查上方的错误日志 (网络?内存?PHP版本?) ${NC}"
echo -e "${RED}========================================${NC}"
exit 1
fi
# 检查文件是否真的存在
if [ ! -f "vendor/autoload.php" ]; then
echo -e "${RED}❌ 奇怪:Composer 显示成功,但 vendor/autoload.php 不存在!${NC}"
exit 1
fi
echo -e "${GREEN}✅ 依赖安装成功且文件存在。${NC}"
# 关键 Composer polyfill 文件检查:若 vendor 目录不完整,Laravel 接口会返回 PHP Warning HTML,前端会误判为 JSON 解析失败
if [ ! -f "vendor/symfony/polyfill-mbstring/bootstrap.php" ]; then
echo -e "${RED}========================================${NC}"
echo -e "${RED} ❌ 致命错误:Composer vendor 不完整! ${NC}"
echo -e "${RED} 缺少 vendor/symfony/polyfill-mbstring/bootstrap.php。 ${NC}"
echo -e "${RED} 请删除线上 vendor 后重新执行 composer install。 ${NC}"
echo -e "${RED}========================================${NC}"
exit 1
fi
# 重新生成生产环境 autoload,避免 autoload_files.php 指向已缺失的旧文件
composer dump-autoload --no-dev --optimize --classmap-authoritative --no-interaction
if [ $? -ne 0 ]; then echo -e "${RED}❌ Composer autoload 重建失败${NC}"; exit 1; fi
# 用当前 PHP 直接加载 Laravel autoload,提前暴露 vendor 缺文件 / 权限 / autoload 缓存问题
"$PHP_BIN" -d opcache.enable_cli=0 -r "require __DIR__.'/vendor/autoload.php'; echo 'Composer autoload OK'.PHP_EOL;"
if [ $? -ne 0 ]; then
echo -e "${RED}========================================${NC}"
echo -e "${RED} ❌ 致命错误:PHP 无法加载 vendor/autoload.php ${NC}"
echo -e "${RED} 请检查 vendor 是否完整、文件权限是否正确、Composer 是否使用了正确 PHP 版本。 ${NC}"
echo -e "${RED}========================================${NC}"
exit 1
fi
# 3. 前端构建
if [ "$FRONTEND_BUILD_NEEDED" -eq 1 ]; then
echo -e "${YELLOW}[3/8] 检测到前端构建输入变更,开始前端构建 (npm run build)...${NC}"
if [ -n "$FRONTEND_CHANGED_FILES" ]; then
echo -e "${BLUE}触发构建的文件:${NC}"
printf '%s\n' "$FRONTEND_CHANGED_FILES"
fi
npm run build
if [ $? -ne 0 ]; then echo -e "${RED}❌ npm run build 失败${NC}"; exit 1; fi
echo -e "${GREEN}✅ 前端资源构建完成。${NC}"
else
echo -e "${GREEN}[3/8] 未检测到前端构建输入变更,跳过 npm run build。${NC}"
fi
# 4. 数据库迁移
if [ "$MIGRATION_NEEDED" -eq 1 ]; then
echo -e "${YELLOW}[4/8] 检测到新增迁移文件,执行数据库迁移...${NC}"
if [ -n "$NEW_MIGRATION_FILES" ]; then
echo -e "${BLUE}新增迁移文件:${NC}"
printf '%s\n' "$NEW_MIGRATION_FILES"
fi
"$PHP_BIN" artisan migrate --force
if [ $? -ne 0 ]; then echo -e "${RED}❌ 数据库迁移失败${NC}"; exit 1; fi
else
echo -e "${GREEN}[4/8] 未检测到新增迁移文件,跳过数据库迁移。${NC}"
fi
# 5. 优化
echo -e "${YELLOW}[5/8] 生产环境优化...${NC}"
# 注意:optimize 命令内部已经包含了 config:cache, route:cache, event:cache,此处无须多余处理
"$PHP_BIN" artisan optimize:clear && "$PHP_BIN" artisan optimize && "$PHP_BIN" artisan view:cache
# 6. 重启 Horizon / 队列进程
echo -e "${YELLOW}[6/8] 重启 Horizon...${NC}"
"$PHP_BIN" artisan horizon:terminate >/dev/null 2>&1 || true
if [ -n "$SUPERVISORCTL_BIN" ]; then
"$SUPERVISORCTL_BIN" restart horizon >/dev/null 2>&1 || "$SUPERVISORCTL_BIN" restart horizon:* >/dev/null 2>&1 || true
fi
# 7. 重启 Reverb,确保长驻 WebSocket 进程加载最新代码和配置
echo -e "${YELLOW}[7/8] 重启 Reverb...${NC}"
REVERB_RESTARTED=0
REVERB_CAN_AUTO_RESTART=0
SUPERVISOR_REVERB_TARGET=""
# 先探测是否存在可自动拉起 Reverb 的进程管理器,避免在纯手工启动场景下执行 reverb:restart 后把聊天室停掉。
if [ -n "$SUPERVISORCTL_BIN" ]; then
if "$SUPERVISORCTL_BIN" status reverb >/dev/null 2>&1; then
SUPERVISOR_REVERB_TARGET="reverb"
REVERB_CAN_AUTO_RESTART=1
elif "$SUPERVISORCTL_BIN" status reverb:* >/dev/null 2>&1; then
SUPERVISOR_REVERB_TARGET="reverb:*"
REVERB_CAN_AUTO_RESTART=1
fi
fi
# Laravel 官方文档说明:reverb:restart 适合在 Supervisor 等进程管理器托管下使用。
if [ "$REVERB_CAN_AUTO_RESTART" -eq 1 ]; then
"$PHP_BIN" artisan reverb:restart >/dev/null 2>&1
if [ $? -eq 0 ]; then
REVERB_RESTARTED=1
echo -e "${GREEN}✅ 已执行 php artisan reverb:restart。${NC}"
fi
# 若线上通过 Supervisor 托管 reverb:start,再补一次显式 restart,尽量确保进程被拉起。
if [ -n "$SUPERVISOR_REVERB_TARGET" ]; then
"$SUPERVISORCTL_BIN" restart "$SUPERVISOR_REVERB_TARGET" >/dev/null 2>&1 || true
REVERB_RESTARTED=1
echo -e "${GREEN}✅ 已尝试重启 Supervisor 托管的 Reverb 进程。${NC}"
fi
else
echo -e "${YELLOW}⚠️ 未检测到 Reverb 进程管理器,已跳过 reverb:restart,避免把手工启动的聊天室长连接服务停掉。${NC}"
echo -e "${YELLOW}⚠️ 若当前服务器是手工执行 php artisan reverb:start,请在部署完成后手动重启该进程。${NC}"
fi
# 8. 重载 PHP-FPM / opcache,避免 Web 进程继续使用旧的 autoload 缓存
echo -e "${YELLOW}[8/8] 重载 PHP-FPM...${NC}"
PHP_FPM_RELOADED=0
if command -v systemctl >/dev/null 2>&1; then
for svc in php-fpm php-fpm-84 php-fpm-83 php-fpm-82 php84-php-fpm php83-php-fpm php82-php-fpm; do
if systemctl list-unit-files "$svc.service" >/dev/null 2>&1; then
systemctl reload "$svc" >/dev/null 2>&1 || systemctl restart "$svc" >/dev/null 2>&1 || true
PHP_FPM_RELOADED=1
fi
done
fi
for init_script in /etc/init.d/php-fpm /etc/init.d/php-fpm-*; do
if [ -x "$init_script" ]; then
"$init_script" reload >/dev/null 2>&1 || "$init_script" restart >/dev/null 2>&1 || true
PHP_FPM_RELOADED=1
fi
done
if [ "$PHP_FPM_RELOADED" -eq 1 ]; then
echo -e "${GREEN}✅ PHP-FPM 已尝试重载。${NC}"
else
echo -e "${YELLOW}⚠️ 未识别到 PHP-FPM 服务,请在面板中手动重启当前站点使用的 PHP。${NC}"
fi
# 权限修正(针对宝塔或 Nginx + FPM 环境)
echo -e "${YELLOW}[后处理] 修复权限...${NC}"
# 将所有文件所属权变更为 Web 运行用户(如 www),防止 root 权限导致框架日志或缓存写入失败
chown -R www:www .
# 默认读写执行权限
chmod -R 755 .
# 针对 Laravel 必须具备写入权限的核心目录给予 775 权限
chmod -R 775 storage bootstrap/cache
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} 🎉 更新成功!网站已恢复。 ${NC}"
echo -e "${GREEN}========================================${NC}"