「推送即部署」是很多个人站长和中小团队最想要的部署体验:本地 git push,线上自动更新,不需要登录服务器敲命令。Git 生态里实现这件事有三条常用路线——GitHub Actions、Git Hooks 和 Webhook——下面逐个拆解,最后给出选择建议。

方案一:GitHub Actions 自动部署

把「推送即部署」做成标准动作,最省事的方式是用 GitHub Actions。每次 push 到 main,自动在云端跑构建和 rsync 到服务器。整体工作流长这样:

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Deploy via rsync
        uses: easingthemes/ssh-deploy@main
        with:
          SSH_PRIVATE_KEY: \${{ secrets.SSH_PRIVATE_KEY }}
          SOURCE: "dist/"
          REMOTE_HOST: \${{ secrets.DEPLOY_HOST }}
          REMOTE_USER: \${{ secrets.DEPLOY_USER }}
          TARGET: /var/www/example.com/

GitHub Secrets 配置

上面用到的变量都来自仓库的 Secrets,不在代码里出现。在 GitHub 仓库 Settings → Secrets and variables → Actions 中添加:

Secret 名称 说明
SSH_PRIVATE_KEY 服务器的 SSH 私钥
DEPLOY_HOST 服务器 IP 或域名
DEPLOY_USER SSH 登录用户名

思路是「构建和产物上传都交给云端,服务器只做接收」。私钥建议用专门的部署用户生成,别拿 root 的密钥到处用。

把三个 Secrets 配好之后,还需要在服务器上准备好两样东西:SSH 私钥对应的公钥要写进部署用户的 authorized_keys,服务器的 /var/www/example.com 目录要归部署用户所有。第一次跑之前,可以在本地先用 ssh -i ~/.ssh/deploy_key deploy@<host> 手动连一次,确认免密登录没问题,再让 Actions 去跑。

方案二:Git Hooks 自动部署

不想引入 CI 平台,也可以在服务器上配一个裸仓库,本地 push 时通过 post-receive hook 自动拉取并部署。先看服务器端的 hook 脚本:

#!/bin/bash
# /var/repo/site.git/hooks/post-receive

TARGET=/var/www/example.com
GIT_DIR=/var/repo/site.git

while read oldrev newrev ref
do
    if [[ $ref =~ main$ ]]; then
        echo "Deploying to production..."
        git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f
        cd $TARGET
        npm ci --production
        npm run build
        sudo systemctl reload nginx
        echo "Deployment complete!"
    fi
done

git --work-tree=... --git-dir=... checkout -f 会把最新代码直接检出到目标目录,这是 Git Hooks 方案的骨干。

服务器端配置步骤

# 1. 在服务器上创建裸仓库
sudo mkdir -p /var/repo/site.git
cd /var/repo/site.git
sudo git init --bare

# 2. 创建 post-receive hook
sudo nano hooks/post-receive
# (粘贴上面的脚本内容)
sudo chmod +x hooks/post-receive

# 3. 设置目标目录权限
sudo mkdir -p /var/www/example.com
sudo chown -R $USER:$USER /var/www/example.com

本地配置

# 添加服务器为 remote
git remote add production ssh://user@your-server/var/repo/site.git

# 推送部署
git push production main

之后每次 git push production main,服务器就会自动执行部署脚本,全程不需要 SSH 上去敲命令。

本地配置这块有两点容易踩坑:一是服务器的目录必须是裸仓库(git init --bare),普通仓库收不到 push;二是 hook 文件必须有执行权限,否则 push 成功了脚本也不会跑。可以先用 git push production main 推一次,再在服务器上 ls -l hooks/post-receive 确认权限和内容都正确。

方案三:Webhook 触发部署

如果服务器没有开通 GitHub Actions,或想用别的平台(如 Gitee、GitLab)触发,可以在服务器上跑一个轻量 Webhook 监听:

#!/bin/bash
# deploy-webhook.sh - 在服务器上运行的 Webhook 监听脚本

API_PORT=9000
SECRET="your-webhook-secret"

while true; do
    request=$(nc -l -p $API_PORT)
    # 验证签名并拉取最新代码
    cd /var/www/example.com
    git pull origin main
    npm ci --production
    npm run build
    sudo systemctl reload nginx
done

注意:真实环境里务必验证 Webhook 请求的签名(平台会带签名头),否则任何人都能触发你的部署脚本。生产环境更推荐用方案一或方案二,这类自写监听更适合内网或个人项目。如果确实需要自己写 Webhook,建议用现成的工具(如开源的 webhook 项目或 CI 平台自带的 Webhook 支持),把签名校验、并发保护这些脏活交给成熟实现,自己只写部署那一步。

三种方案怎么选

方案 适合场景 优点 缺点
GitHub Actions 代码托管在 GitHub 云端构建、日志可查、生态成熟 需要公网可达或有 runner
Git Hooks 自建 Git 服务器 简单直接、无额外依赖 脚本在服务器上维护,出问题难排查
Webhook 多平台触发、自定义流程 灵活、可对接任意平台 安全要自己把关,稳定性要自己保障

选择时先问自己三个问题:代码托管在哪里、服务器能不能公网访问、团队愿不愿意维护额外组件。大多数托管在 GitHub 的中小项目,方案一就是性价比最高的起点。

安全建议

  1. 使用专门的部署用户,最小权限原则——不要用 root 跑部署。
  2. SSH 密钥添加密码短语(passphrase),并限制用途。
  3. 部署后清理 .env 等敏感文件,确保不被公开。
  4. 保留上一版本作为回滚备份,出问题秒级回退。
  5. 添加部署通知(Slack/邮件),部署失败第一时间知道。
  6. 定期轮换部署密钥和密码,离职成员的权限及时回收。
  7. 用 .gitignore 把 .env、密钥文件等排除在仓库之外,防止误提交。

常见问题

  • 推送后没有触发部署? 检查 hook 是否可执行、分支名是否匹配(main),以及服务器上是不是裸仓库。
  • 部署脚本没有权限写目标目录? 确认运行部署的用户对 /var/www/example.com 有写权限,用 chown 或把用户加进对应组。
  • 怎么回滚? 在目标目录保留上一版本(比如软链切换),或者给仓库打 tag,出问题 checkout 到上一个 tag 再跑一次部署。
  • 部署很快但页面没变化? 检查部署目标目录和 Web 服务器根目录是否一致,以及是否配置了 CDN/缓存。
  • push 超时? 大仓库用 git push --mirror 拆分,或把产物构建挪到 CI 上,只传输成品。

参考:GitHub Actions 官方文档 https://docs.github.com/actions ;Git Hooks 文档 https://git-scm.com/docs/githooks