阅读 1 分钟

使用 GitHub Actions 自动部署 Astro 项目到 Nginx 服务器

Astro 默认生成静态文件,每次更新文章都在本地构建再上传会比较繁琐。现在本站改为推送到 GitHub 的 main 分支后,由 GitHub Actions 自动完成检查、构建和部署。

部署结构

整个流程分为四步:

  1. 使用 pnpm 安装锁定版本的依赖。
  2. 运行 Astro 检查、静态构建和 Pagefind 索引。
  3. 使用专用 SSH 用户验证服务器目录权限。
  4. 通过 rsync 同步 dist/,并清理已经不存在的旧文件。

服务器上的部署用户没有 sudo 权限,只能写入站点目录。Nginx 继续读取静态文件,因此每次发布文章都不需要重启服务。

GitHub Actions 配置

工作流位于 .github/workflows/deploy.yml

name: Deploy Mistwood

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read

concurrency:
  group: production-deployment
  cancel-in-progress: false

jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    env:
      DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
      DEPLOY_PORT: ${{ secrets.DEPLOY_PORT }}
      DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
      DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
      DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
      DEPLOY_KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v7

      - name: Set up pnpm
        uses: pnpm/action-setup@v6

      - name: Set up Node.js
        uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build site
        run: pnpm build

      - name: Configure SSH
        shell: bash
        run: |
          set -euo pipefail
          install -d -m 700 ~/.ssh
          install -m 600 /dev/null ~/.ssh/id_ed25519
          printf '%s\n' "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519
          printf '%s\n' "$DEPLOY_KNOWN_HOSTS" > ~/.ssh/known_hosts

      - name: Verify deployment target
        shell: bash
        run: |
          set -euo pipefail
          ssh -p "$DEPLOY_PORT" -i ~/.ssh/id_ed25519 \
            -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes \
            "$DEPLOY_USER@$DEPLOY_HOST" \
            "test -d '$DEPLOY_PATH' && test -w '$DEPLOY_PATH'"

      - name: Deploy static files
        shell: bash
        run: |
          set -euo pipefail
          rsync --archive --compress --delay-updates --delete-delay \
            -e "ssh -p $DEPLOY_PORT -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes" \
            dist/ "$DEPLOY_USER@$DEPLOY_HOST:${DEPLOY_PATH%/}/"

--delete-delay 会在新文件传输完成后删除远端旧文件。这个选项很适合静态站点,但必须保证目标目录只存放当前站点的构建产物。

配置 Secrets

在仓库的 Settings > Secrets and variables > Actions 中配置:

  • DEPLOY_HOST:服务器域名或 IP。
  • DEPLOY_PORT:SSH 端口。
  • DEPLOY_USER:无 sudo 权限的部署用户。
  • DEPLOY_PATH:Nginx 的站点目录。
  • DEPLOY_SSH_KEY:专用部署私钥。
  • DEPLOY_KNOWN_HOSTS:经过核对的服务器主机密钥。

不要在工作流中关闭 StrictHostKeyChecking,也不要把私钥直接写进仓库。主机密钥固定后,如果服务器身份意外变化,部署应该明确失败,而不是静默接受。

发布文章

正常提交并推送即可触发部署:

git add .
git commit -m "content: publish new post"
git push origin main

构建失败时不会连接服务器;SSH 目录校验失败时也不会执行 rsync。这样可以避免错误构建或错误路径覆盖线上文件。

输入关键词开始搜索