跳到正文

仅构建变更切片

用脚本把拉取请求的变更路径映射到切片,并由 GitHub Actions 构建这些切片。

在拆分为部分构建的大型站点上,改动一个切片的拉取请求不需要重建其余部分。一个脚本把变更路径映射到切片,再由工作流构建每个切片。

前置条件

  • 已拆分为切片、部署在同一主机名下的站点:见部分构建
  • GitHub Actions

将变更路径映射到切片

把切片映射表集中在一处:

scripts/pick-slice.mjsjs
// 从 stdin 读取变更路径(git diff --name-only --no-renames,因此
// 在集合间移动的页面会同时列出新旧路径),并以 JSON 输出要构建的
// 切片。集合内容文件夹之外的路径(组件、配置、片段、API 规范、
// public/)会触及所有切片;`--all` 选择所有切片。
import { readFileSync } from "node:fs";

// `preview` 和 `deploy` 是主机对应的命令;这里是 Cloudflare 的命令。
const SLICES = [
  {
    name: "docs-site",
    only: "docs,api",
    preview: "npx wrangler versions upload --config wrangler.jsonc",
    deploy: "npx wrangler deploy --config wrangler.jsonc",
  },
  {
    name: "docs-site-es",
    only: "sdk",
    preview: "npx wrangler versions upload --config wrangler.sdk.jsonc",
    deploy: "npx wrangler deploy --config wrangler.sdk.jsonc",
  },
];

const paths = process.argv.includes("--all")
  ? ["astro.config.ts"]
  : readFileSync(0, "utf8").split("\n").filter(Boolean);
const picked = new Set();
for (const path of paths) {
  const collection = /^src\/content\/([^/]+)\//.exec(path)?.[1];
  const slice =
    collection && collection !== "partials"
      ? SLICES.find((candidate) => candidate.only.split(",").includes(collection))
      : undefined;
  if (slice) picked.add(slice);
  else for (const candidate of SLICES) picked.add(candidate);
}
console.log(JSON.stringify([...picked]));
.github/workflows/slices.ymlyaml
name: Slices

on:
  pull_request:
  push:
    branches: [main]

jobs:
  pick:
    runs-on: ubuntu-latest
    outputs:
      slices: ${{ steps.pick.outputs.slices }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - id: pick
        run: |
          if [ "${{ github.event_name }}" = "pull_request" ]; then
            git diff --name-only --no-renames "origin/${{ github.base_ref }}...HEAD" | node scripts/pick-slice.mjs
          else
            node scripts/pick-slice.mjs --all
          fi | sed 's/^/slices=/' >> "$GITHUB_OUTPUT"

  build:
    needs: pick
    if: needs.pick.outputs.slices != '[]'
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        slice: ${{ fromJSON(needs.pick.outputs.slices) }}
    name: ${{ matrix.slice.name }}
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: pnpm
      - run: pnpm install --frozen-lockfile

      # 上一次全站构建的路由,用于指向其他切片的链接。
      - uses: actions/cache/restore@v4
        with:
          path: .nimbus/routes.json
          key: routes-${{ github.event.pull_request.base.sha || github.sha }}
          restore-keys: routes-

      - run: pnpm build
        env:
          NIMBUS_BUILD_ONLY: ${{ matrix.slice.only }}
      - run: pnpm nimbus-docs lint

      - if: github.event_name == 'pull_request'
        run: ${{ matrix.slice.preview }}
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
      - if: github.event_name == 'push'
        run: ${{ matrix.slice.deploy }}
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

  # 整个站点,每次推送运行一次:所有切片构建借用这份路由基准。
  routes:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm build
      - uses: actions/cache/save@v4
        with:
          path: .nimbus/routes.json
          key: routes-${{ github.sha }}

仅触及 src/content/sdk/ 的拉取请求会构建并预览 sdk 切片;触及组件的拉取请求会构建所有切片。推送到 main 会部署所有切片,并保存整个站点的路由供下一次拉取请求使用。

后续步骤

输入以搜索…

↑↓ 导航↵ 选择Esc 关闭