# 在 CI 中检查邮件模板

`bird email templates check` 从任何 CI 系统检查你的邮件模板，并在变更会导致模板损坏时以失败退出，让 pull request 在收件人发现问题之前拦截它。它只读取数据：不会发送任何内容，也不会更改草稿。

## 开始之前

- 使用固定版本安装 [`bird` CLI](/docs/cli)，这样新版本不会在未通知的情况下更改你的流水线。
- 创建一个对 `email_management` 具有读取权限的 API 密钥，并将其作为 `BIRD_API_KEY` 提供给作业。

## 检查模板

```bash
bird email templates check welcome-email order-shipped
```

该命令会渲染每个模板草稿的所有语言版本，因为翻译本身可能出问题，并按模板和语言报告：

- **邮件客户端兼容性**：与[模板预览](/docs/guides/email/templates)显示的兼容性结果相同，例如客户端会移除或忽略的 CSS。
- **大小**：HTML 正文与 Gmail 约 102 KB 的截断点的对比。Gmail 会将超出部分隐藏在一个链接后面。超过 90 KB 时检查会发出警告，因为合并变量可能使消息超限。
- **链接和图片**：链接（`<a>` 和 `<area>`）、图片来源或 `srcset`、`background` 属性，或 `style` 属性或 `<style>` 块中 CSS `url()` 里的每个 `http` 和 `https` URL，均从命令运行的位置进行抓取。`404`、`410` 或 `5xx`，或不存在的主机，视为问题。`401`、`403` 或 `429`，或 DNS 查询超时，视为警告，因为这通常意味着运行器被拒绝或无法到达主机。指向私有地址或回环地址的链接永远不会被抓取。在没有出站网络访问权限的运行器上传入 `--skip-links`。

由模板变量、联系人字段或退订链接构建的链接不会被抓取，因为预览无法用收件人实际收到的值填充它们。报告将它们列在 `unverified_links` 下，逐收件人部分显示为 `{variable}`，`--annotations github` 会将其作为通知打印，以便 pull request 显示哪些内容未被检查。

该命令在 stdout 上打印 JSON 报告：首先是整个运行的 `valid`，然后是每个模板和语言的条目及其 `findings`。每项发现包含一个 `severity`、一个 `area`（`compatibility`、`size` 或 `links`）、一个 `message`，通常还有一个 `fix`。`problem` 会导致检查失败；`warning` 仅被报告，不会导致失败。

## 检查构建渲染的文件

如果你的构建自行渲染模板，例如使用 MJML 或 React Email，可以检查它生成的文件：

```bash
bird email templates check --html dist/welcome.html --template welcome-email --annotations github
```

该文件作为未保存的内容针对你指定的模板进行预览，因此不会更改草稿；任何模板都可以使用。使用 `--annotations github` 时，兼容性结果会标注在文件中对应的行上，大小和链接结果则标注在整个文件上。

## 退出码

退出码是你的 CI 作业据以分支的依据：

| 退出码 | 含义                                                         |
| ------ | ------------------------------------------------------------ |
| `0`    | 无问题。仍可能报告警告。                                     |
| `7`    | 检查已运行并发现至少一个问题。报告会列出每个问题。           |
| `3`    | 你指定的模板在此工作区中不存在。                             |
| `4`    | API 密钥缺失、无效，或缺少对 `email_management` 的读取权限。 |
| `6`    | 被限速或临时服务器错误。在 `retry_after` 延迟后重试。        |

对 `7` 不做更改地重试会以相同方式失败，因此只有 `6` 值得重试。

## 在 GitHub Actions 中运行

使用 `--annotations github` 时，命令会将结果以 GitHub Actions 注解而非 JSON 报告的形式打印，使其显示在 pull request 的检查中。以下工作流会在每个更改模板的 pull request 上检查你的构建所渲染的模板：

```yaml
name: Email templates
on:
  pull_request:
    paths: ["emails/**"]
jobs:
  check:
    # Pull requests from forks get no repository secrets, so the check could only exit 4.
    if: ${{ !github.event.pull_request.head.repo.fork }}
    runs-on: ubuntu-latest
    env:
      BIRD_API_KEY: ${{ secrets.BIRD_API_KEY }}
    steps:
      - uses: actions/checkout@v4
      - name: Install the bird CLI
        run: curl -fsSL https://cli.bird.com/install.sh | sh -s -- --version "${{ vars.BIRD_CLI_VERSION }}"
      - name: Build the templates
        run: npm ci && npm run build:emails
      - name: Check the templates
        run: |
          status=0
          for file in dist/emails/*.html; do
            bird email templates check --html "$file" --template welcome-email --annotations github || status=$?
          done
          exit "$status"
```

将 `BIRD_CLI_VERSION` 设为仓库变量，值为你测试过的发行版，并将 `BIRD_API_KEY` secret 设为具有 `email_management` 读取权限的密钥。GitHub 不会向来自 fork 的 pull request 提供仓库 secret，因此工作流会跳过它们。在合并来自 fork 的变更之前，将相同的变更推送到你仓库的分支上进行检查。在任何其他 CI 系统中，运行相同的命令并在退出码非零时让作业失败。

## 故障排除

- **退出 `3`。** 该模板不在 API 密钥所属的工作区中。使用 `bird email templates list` 列出工作区的模板。
- **退出 `4`。** 密钥缺少对 `email_management` 的读取权限，或 secret 未传递到作业中。
- **所有链接均以警告形式失败。** 运行器没有出站网络访问权限，或被防火墙阻止。在那里使用 `--skip-links` 运行，并从能够访问互联网的运行器检查链接。

## 后续步骤

- [邮件模板](/docs/guides/email/templates)：编辑、预览和发布每种语言的模板
- [`bird email templates check`](/docs/cli/reference/email-templates-check)：命令支持的所有标志
- [`bird` CLI](/docs/cli)：安装固定版本并在 CI 中进行身份验证

## Related resources

- [Getting started with email](/learn/email/getting-started-with-email) (video)
- [Email](/email-api) (product)
- [Build your first integration](/learn/paths/integration) (course)
- [Send your first email](/docs/get-started/send-your-first-email) (docs)

[Get an implementation brief](/learn/workspace?topic=email)
