Skip to content

腾讯云 ​

TencentCloud

宝塔面板

以LuckyPlane为例

本地代码部署到服务器流程 ​

以LuckyPlane为例,基于 rsync + SSH编写的部署脚本deploy.sh ,

1. 本地测试通过后,先预览变更(dry-run) ​

bash
./deploy.sh

默认是预览模式,只列出会上传的文件, 不会改动服务器 。确认文件列表无误后再执行真正部署。

2. 正式部署(推荐完整流程) ​

bash
./deploy.sh --apply --backup --finish

这会依次执行:

  • --backup :在服务器打包备份当前代码到/root/luckyplane-bak-*.tar.gz (排除 vendor/node_modules)
  • --apply :用 rsync 上传本地代码到Tencent:/www/wwwroot/LuckyPlane
  • --finish :服务器收尾——修正文件归属为www:www 、composer install --no-dev 、重建 config/route/view 缓存、storage:link

3. 各参数说明 ​

参数 作用 无参数 预览模式,只显示将要同步的文件 --apply 真正执行上传 --backup 上传前在服务器打包备份 --finish 上传后修正归属、跑 composer、重建缓存 --delete 同步删除服务器上本地没有的文件( 慎用 ,会二次确认)

脚本自动排除的内容 ​

.env ( 硬性排除 ,避免覆盖生产数据库配置)、.git/ 、node_modules/ 、vendor/ 、storage/logs/ 、storage/framework/ 、bootstrap/cache/ 等。

注意事项 ​

  1. .env 不会上传 ——服务器的生产配置(数据库、APP_KEY 等)保持原样,这是刻意设计的
  2. vendor/ 和node_modules/ 不上传,由服务器的--finish 阶段执行composer install
  3. 如果前端有构建产物(如public/build ),脚本也排除了,需要在服务器上单独构建或手动上传
  4. SSH 免密登录已配置好(~/.ssh/config 中Tencent →124.220.44.180:9922 )

deploy.sh脚本内容

bash
#!/usr/bin/env bash
#
# LuckyPlane 部署脚本(本地 → 腾讯云服务器 /www/wwwroot/LuckyPlane)
#
# 用法:
#   ./deploy.sh                         预览:只列出会上传的文件,不动服务器
#   ./deploy.sh --apply                 真正上传
#   ./deploy.sh --apply --backup        上传前先在服务器打包备份到 /root/
#   ./deploy.sh --apply --finish        上传后修正归属、跑 composer、重建缓存(均以 www 身份)
#   ./deploy.sh --apply --delete        同步删除服务器上多余的文件(慎用,会二次确认)
#   ./deploy.sh -h                      查看用法
#
# 选项可组合,例如:./deploy.sh --apply --backup --finish
#
# 重要:.env 永不在同步范围内。本地 .env 与服务器 .env 内容不同,
#       上传会覆盖生产配置(数据库、密钥等),脚本已硬性排除。
#
set -euo pipefail

LOCAL_DIR="/Users/ben/Sites/LuckyPlane/"
SSH_HOST="Tencent"
REMOTE_DIR="/www/wwwroot/LuckyPlane"

APPLY=0
BACKUP=0
FINISH=0
DELETE=0

usage() {
  awk 'NR>2 && /^#/ {sub(/^#[[:space:]]?/, ""); print; next} NR>2 && !/^#/ {exit}' "$0"
}

for arg in "$@"; do
  case "$arg" in
    --apply)  APPLY=1 ;;
    --backup) BACKUP=1 ;;
    --finish) FINISH=1 ;;
    --delete) DELETE=1 ;;
    -h|--help) usage; exit 0 ;;
    *) echo "未知参数: $arg"; usage; exit 1 ;;
  esac
done

GREEN='\033[0;32m'; YELLOW='\033[0;33m'; RED='\033[0;31m'; BOLD='\033[1m'; NC='\033[0m'
info()  { printf "${GREEN}[信息]${NC} %s\n" "$1"; }
warn()  { printf "${YELLOW}[注意]${NC} %s\n" "$1"; }
err()   { printf "${RED}[错误]${NC} %s\n" "$1"; }

# 同步时要排除的内容(顺序无关,均为 rsync --exclude)
EXCLUDES=(
  --exclude='.env'
  --exclude='.git/'
  --exclude='.workbuddy/'
  --exclude='.idea/'
  --exclude='.vscode/'
  --exclude='node_modules/'
  --exclude='vendor/'
  --exclude='storage/framework/'
  --exclude='storage/logs/'
  --exclude='bootstrap/cache/'
  --exclude='public/storage'
  --exclude='public/build'
  --exclude='public/hot'
  --exclude='_ide_helper.php'
  --exclude='.phpunit.result.cache'
  --exclude='.DS_Store'
  --exclude='*.log'
)

printf "${BOLD}LuckyPlane 部署${NC}\n"
echo "  本地:   ${LOCAL_DIR}"
echo "  服务器: ${SSH_HOST}:${REMOTE_DIR}"
echo

# 1) 检查 SSH 连通性(密钥登录,不交互)
if ! ssh -o BatchMode=yes -o ConnectTimeout=10 "$SSH_HOST" "true" 2>/dev/null; then
  err "无法免密连接 ${SSH_HOST}。请检查 ~/.ssh/config 与密钥是否有效。"
  exit 1
fi
info "SSH 连接正常"

# 2) --delete 需要二次确认(会删服务器文件,风险最高)
if [[ "$DELETE" -eq 1 && "$APPLY" -eq 1 ]]; then
  warn "--delete 会删除服务器上本地没有的文件(含线上用户上传内容)"
  printf "确认继续?请输入 yes: "
  read -r ans
  if [[ "$ans" != "yes" ]]; then
    info "已取消"
    exit 0
  fi
fi

# 3) 组装 rsync 参数
RSYNC_ARGS=(-avz -e ssh "${EXCLUDES[@]}")
# 不把本地的 uid/gid(Mac 上是 501:games)带到服务器,归属统一由 --finish 阶段的 chown 收编
RSYNC_ARGS+=(--no-owner --no-group)
if [[ "$APPLY" -eq 0 ]]; then
  RSYNC_ARGS+=(--dry-run)
fi
if [[ "$DELETE" -eq 1 ]]; then
  RSYNC_ARGS+=(--delete)
fi

# 4) 服务器侧备份(可选)
if [[ "$BACKUP" -eq 1 ]]; then
  info "在服务器打包备份(排除 vendor / node_modules)..."
  ssh "$SSH_HOST" "bash -s" <<REMOTE_BACKUP
set -e
tar czf /root/luckyplane-bak-\$(date +%m%d-%H%M).tar.gz \
  --exclude='vendor' --exclude='node_modules' --exclude='storage/framework/*' \
  -C /www/wwwroot LuckyPlane
ls -lh /root/luckyplane-bak-*.tar.gz | tail -3
REMOTE_BACKUP
  info "备份完成"
fi

# 5) 同步
if [[ "$APPLY" -eq 1 ]]; then
  info "开始上传..."
else
  warn "预览模式(未加 --apply),服务器不会被改动"
fi
echo
rsync "${RSYNC_ARGS[@]}" "$LOCAL_DIR" "${SSH_HOST}:${REMOTE_DIR}/" || {
  err "rsync 执行失败,请查看上方输出"
  exit 1
}
echo

# 6) 服务器收尾(可选)
if [[ "$FINISH" -eq 1 ]]; then
  if [[ "$APPLY" -eq 0 ]]; then
    warn "预览模式下不执行收尾命令"
  else
    info "在服务器执行收尾:归属修正 / 依赖安装 / 缓存重建(composer 与 artisan 均以 www 身份运行)..."
    ssh "$SSH_HOST" "bash -s" <<'REMOTE_FINISH'
set -e
PROJECT=/www/wwwroot/LuckyPlane
cd "$PROJECT"

# 1) 统一归属,消除历史遗留的 501:games(Mac 本地 uid)
chown -R www:www "$PROJECT"
if [ -f .env ]; then chmod 600 .env; fi

# 2) composer / artisan 一律以 www 身份执行
#    root 下 composer 会禁用插件并告警;artisan 也需用 www,
#    否则生成的缓存文件归 root,www 下次无法重写
#    注意:www 的 shell 是 /sbin/nologin,sudo -u / su 会被 PAM 拒绝,只能用 runuser
runuser -u www -- env COMPOSER_HOME=/home/www/.composer bash -c \
  "cd $PROJECT && composer install --no-dev --optimize-autoloader --no-interaction"

artisan() { runuser -u www -- php artisan "$@"; }
artisan config:clear
artisan config:cache
artisan route:cache
artisan view:cache
artisan storage:link || echo "(storage:link 已存在,跳过)"

chmod -R 775 storage bootstrap/cache
echo "收尾完成"
REMOTE_FINISH
    info "收尾完成"
  fi
fi

if [[ "$APPLY" -eq 1 ]]; then
  info "部署结束"
  echo "  提示:.env 未被上传,服务器上的生产配置保持原样"
else
  info "预览结束。确认无误后加 --apply 真正上传"
fi

运行前置条件 ​

确保脚本有执行权限:

bash
chmod +x /Users/ben/Sites/LuckyPlane/deploy.sh

运行方式 ​

方式一:在项目目录下运行(推荐) ​

bash
cd /Users/ben/Sites/LuckyPlane
./deploy.sh

方式二:用绝对路径运行 ​

bash
/Users/ben/Sites/LuckyPlane/deploy.sh

方式三:用 bash 显式调用(最稳妥,无需执行权限) ​

bash
bash /Users/ben/Sites/LuckyPlane/deploy.sh

配置更改后需要刷新缓存配置

bash
php artisan config:clear
php artisan config:cache

验证 (改完后在你本机执行)

bash
curl -i -X OPTIONS "https://luckyplane.com/api/user/current" \
  -H "Origin: http://yadonghx.vicp.net" \
  -H "Access-Control-Request-Method: GET"

幸运之翼在线文档