2026-03-05 11:35:50 +08:00
|
|
|
|
#!/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" # <--- 确认这里的路径是否正确
|
2026-04-24 23:46:53 +08:00
|
|
|
|
export COMPOSER_ALLOW_SUPERUSER=1
|
2026-04-29 10:59:35 +08:00
|
|
|
|
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
|
2026-03-05 11:35:50 +08:00
|
|
|
|
|
|
|
|
|
|
echo -e "${BLUE}========================================${NC}"
|
|
|
|
|
|
echo -e "${BLUE} 🚀 Laravel 稳健更新脚本 (带严格检查) ${NC}"
|
|
|
|
|
|
echo -e "${BLUE}========================================${NC}"
|
|
|
|
|
|
|
|
|
|
|
|
cd "$PROJECT_ROOT" || { echo -e "${RED}❌ 无法进入项目目录:$PROJECT_ROOT${NC}"; exit 1; }
|
|
|
|
|
|
|
2026-04-29 10:59:35 +08:00
|
|
|
|
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}"
|
|
|
|
|
|
|
2026-03-05 11:49:45 +08:00
|
|
|
|
# 1. Git Pull(先重置 lock 文件,避免服务器环境差异导致冲突)
|
2026-04-29 10:39:07 +08:00
|
|
|
|
echo -e "${YELLOW}[1/8] 拉取代码...${NC}"
|
2026-03-05 11:49:45 +08:00
|
|
|
|
git checkout -- composer.lock package-lock.json 2>/dev/null || true
|
2026-03-05 11:35:50 +08:00
|
|
|
|
git fetch origin && git pull origin master
|
|
|
|
|
|
if [ $? -ne 0 ]; then echo -e "${RED}❌ Git 失败${NC}"; exit 1; fi
|
|
|
|
|
|
|
|
|
|
|
|
# 2. Composer Install (关键检查点)
|
2026-04-29 10:39:07 +08:00
|
|
|
|
echo -e "${YELLOW}[2/8] 安装依赖 (Composer)...${NC}"
|
2026-03-05 11:35:50 +08:00
|
|
|
|
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}"
|
|
|
|
|
|
|
2026-04-24 23:42:52 +08:00
|
|
|
|
# 关键 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
|
|
|
|
|
|
|
2026-04-24 23:46:53 +08:00
|
|
|
|
# 用当前 PHP 直接加载 Laravel autoload,提前暴露 vendor 缺文件 / 权限 / autoload 缓存问题
|
2026-04-29 10:59:35 +08:00
|
|
|
|
"$PHP_BIN" -d opcache.enable_cli=0 -r "require __DIR__.'/vendor/autoload.php'; echo 'Composer autoload OK'.PHP_EOL;"
|
2026-04-24 23:46:53 +08:00
|
|
|
|
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
|
|
|
|
|
|
|
2026-03-05 11:48:07 +08:00
|
|
|
|
# 3. 前端构建
|
2026-04-29 10:39:07 +08:00
|
|
|
|
echo -e "${YELLOW}[3/8] 前端构建 (npm run build)...${NC}"
|
2026-03-05 11:48:07 +08:00
|
|
|
|
npm run build
|
|
|
|
|
|
if [ $? -ne 0 ]; then echo -e "${RED}❌ npm run build 失败${NC}"; exit 1; fi
|
|
|
|
|
|
echo -e "${GREEN}✅ 前端资源构建完成。${NC}"
|
|
|
|
|
|
|
2026-03-05 11:35:50 +08:00
|
|
|
|
|
|
|
|
|
|
# 4. 数据库迁移
|
2026-04-29 10:39:07 +08:00
|
|
|
|
echo -e "${YELLOW}[4/8] 数据库迁移...${NC}"
|
2026-04-29 10:59:35 +08:00
|
|
|
|
"$PHP_BIN" artisan migrate --force
|
2026-03-05 11:35:50 +08:00
|
|
|
|
|
|
|
|
|
|
# 5. 优化
|
2026-04-02 15:02:12 +08:00
|
|
|
|
echo -e "${YELLOW}[5/8] 生产环境优化...${NC}"
|
2026-04-02 13:34:02 +08:00
|
|
|
|
# 注意:optimize 命令内部已经包含了 config:cache, route:cache, event:cache,此处无须多余处理
|
2026-04-29 10:59:35 +08:00
|
|
|
|
"$PHP_BIN" artisan optimize:clear && "$PHP_BIN" artisan optimize && "$PHP_BIN" artisan view:cache
|
2026-03-05 11:35:50 +08:00
|
|
|
|
|
2026-04-02 15:02:12 +08:00
|
|
|
|
# 6. 重启 Horizon / 队列进程
|
|
|
|
|
|
echo -e "${YELLOW}[6/8] 重启 Horizon...${NC}"
|
2026-04-29 10:59:35 +08:00
|
|
|
|
"$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
|
2026-04-02 15:02:12 +08:00
|
|
|
|
|
2026-04-29 10:39:07 +08:00
|
|
|
|
# 7. 重启 Reverb,确保长驻 WebSocket 进程加载最新代码和配置
|
|
|
|
|
|
echo -e "${YELLOW}[7/8] 重启 Reverb...${NC}"
|
|
|
|
|
|
REVERB_RESTARTED=0
|
2026-04-29 10:52:44 +08:00
|
|
|
|
REVERB_CAN_AUTO_RESTART=0
|
2026-04-29 10:59:35 +08:00
|
|
|
|
SUPERVISOR_REVERB_TARGET=""
|
2026-04-29 10:39:07 +08:00
|
|
|
|
|
2026-04-29 10:52:44 +08:00
|
|
|
|
# 先探测是否存在可自动拉起 Reverb 的进程管理器,避免在纯手工启动场景下执行 reverb:restart 后把聊天室停掉。
|
2026-04-29 10:59:35 +08:00
|
|
|
|
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:*"
|
2026-04-29 10:52:44 +08:00
|
|
|
|
REVERB_CAN_AUTO_RESTART=1
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# Laravel 官方文档说明:reverb:restart 适合在 Supervisor 等进程管理器托管下使用。
|
|
|
|
|
|
if [ "$REVERB_CAN_AUTO_RESTART" -eq 1 ]; then
|
2026-04-29 10:59:35 +08:00
|
|
|
|
"$PHP_BIN" artisan reverb:restart >/dev/null 2>&1
|
2026-04-29 10:52:44 +08:00
|
|
|
|
if [ $? -eq 0 ]; then
|
|
|
|
|
|
REVERB_RESTARTED=1
|
|
|
|
|
|
echo -e "${GREEN}✅ 已执行 php artisan reverb:restart。${NC}"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# 若线上通过 Supervisor 托管 reverb:start,再补一次显式 restart,尽量确保进程被拉起。
|
2026-04-29 10:59:35 +08:00
|
|
|
|
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}"
|
2026-04-29 10:39:07 +08:00
|
|
|
|
fi
|
2026-04-29 10:52:44 +08:00
|
|
|
|
else
|
|
|
|
|
|
echo -e "${YELLOW}⚠️ 未检测到 Reverb 进程管理器,已跳过 reverb:restart,避免把手工启动的聊天室长连接服务停掉。${NC}"
|
|
|
|
|
|
echo -e "${YELLOW}⚠️ 若当前服务器是手工执行 php artisan reverb:start,请在部署完成后手动重启该进程。${NC}"
|
2026-04-29 10:39:07 +08:00
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# 8. 重载 PHP-FPM / opcache,避免 Web 进程继续使用旧的 autoload 缓存
|
|
|
|
|
|
echo -e "${YELLOW}[8/8] 重载 PHP-FPM...${NC}"
|
2026-04-24 23:46:53 +08:00
|
|
|
|
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
|
|
|
|
|
|
|
2026-04-29 10:39:07 +08:00
|
|
|
|
# 权限修正(针对宝塔或 Nginx + FPM 环境)
|
|
|
|
|
|
echo -e "${YELLOW}[后处理] 修复权限...${NC}"
|
2026-04-02 13:34:02 +08:00
|
|
|
|
# 将所有文件所属权变更为 Web 运行用户(如 www),防止 root 权限导致框架日志或缓存写入失败
|
|
|
|
|
|
chown -R www:www .
|
|
|
|
|
|
# 默认读写执行权限
|
|
|
|
|
|
chmod -R 755 .
|
|
|
|
|
|
# 针对 Laravel 必须具备写入权限的核心目录给予 775 权限
|
|
|
|
|
|
chmod -R 775 storage bootstrap/cache
|
2026-03-05 11:35:50 +08:00
|
|
|
|
|
|
|
|
|
|
echo -e "${GREEN}========================================${NC}"
|
|
|
|
|
|
echo -e "${GREEN} 🎉 更新成功!网站已恢复。 ${NC}"
|
2026-04-24 23:42:52 +08:00
|
|
|
|
echo -e "${GREEN}========================================${NC}"
|