预览变更

以 Markdown 格式查看

Fern 提供两种预览文档变更的方式:

  • 本地开发:具有热重载的快速迭代,最适合活跃开发
  • 预览链接:可分享的 URL,用于审查和协作
前置条件

请安装以下内容:

本地开发

运行本地预览服务器以通过热重载即时查看文档变更。首次在线运行后可离线访问。

$ # 启动预览服务器(在包含 fern 文件夹的目录中运行)
$ fern docs dev
$
$ # 或使用自定义端口
$ fern docs dev --port 3002

在 Windows 上,fern docs dev 需要启用长路径支持

要启用长路径支持,请在管理员权限的 PowerShell 提示符中运行以下命令,然后重启终端:

1New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1 -PropertyType DWORD -Force

如果无法启用长路径支持,请使用适用于 Linux 的 Windows 子系统 (WSL)在 Linux 环境中运行 fern docs dev

您的文档将在 http://localhost:3000(默认)或您指定的端口上可用。如果您尝试在已使用的端口上运行 Fern,它将使用下一个可用端口。

本地开发中禁用了一些功能:

  • 搜索
  • SEO(网站图标、自动生成的 meta 标签等)
  • 身份验证

预览链接

生成可分享的预览 URL,在发布前审查和协作文档变更。预览链接不会被搜索引擎索引且不会过期。

默认情况下,每次运行都会生成一个带有唯一 UUID 的新 URL。--id 标志创建稳定的命名预览链接— 使用相同的 --id 重新运行会就地更新现有预览。

$# 生成具有唯一 URL 的预览链接
$fern generate --docs --preview
$
$# 或使用 --id 创建稳定的命名预览链接
$fern generate --docs --preview --id my-feature
Example output
$# 不使用 --id(每次都是唯一 UUID)
$[docs]: Published docs to https://fern-preview-c973a36e-337b-44f5-ab83-aab.docs.buildwithfern.com/learn
$
$# 使用 --id my-feature(稳定 URL)
$[docs]: Published docs to https://fern-preview-my-feature.docs.buildwithfern.com/learn

当具有相同 --id 的预览已存在时,Fern 会提示您确认覆盖。这在 GitHub Actions 中会自动跳过,但对于其他 CI 环境(例如 Azure Pipelines),使用 --force 跳过确认。

$fern generate --docs --preview --id my-feature --force

当不再需要时,您可以删除预览部署

$fern docs preview delete <url>

使用 GitHub Actions 自动化

您可以使用 GitHub Actions 工作流在打开拉取请求时自动生成预览 URL。通过传递带有分支名称的 --id,对同一 PR 的每次推送都会更新相同的预览 URL,而不是创建新的。工作流会在 PR 上发布评论,包含预览链接和指向 PR 中每个更改页面的直接链接,这样审查者可以直接跳转到受影响的页面。

GitHub Actions 机器人在拉取请求上的评论,显示命名预览 URL 和指向已更改文档页面的直接链接

如果您使用引导式 UICLI 快速开始设置网站,此工作流会自动包含在您的仓库中。否则,使用下面的示例手动添加。

这些工作流需要一个 FERN_TOKEN 仓库密钥。如果您使用了引导式工作流,此密钥会自动添加。否则,在终端中运行 fern token 生成令牌,然后在您仓库的 Settings > Secrets and variables > Actions 中以名称 FERN_TOKEN 添加。

您可能需要为配置 FERN_TOKEN 之前打开的任何 PR 重新运行预览构建。

.github/workflows/preview-docs.yml
1name: Preview Docs
2
3on:
4 pull_request:
5 types: [opened, synchronize, ready_for_review]
6 branches:
7 - main
8
9jobs:
10 run:
11 runs-on: ubuntu-latest
12 permissions:
13 pull-requests: write
14 contents: read
15 steps:
16 - name: Checkout repository
17 uses: actions/checkout@v4
18 with:
19 fetch-depth: 0
20
21 - name: Setup Fern CLI
22 uses: fern-api/setup-fern-cli@v1
23
24 - name: Generate preview URL
25 id: generate-docs
26 env:
27 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
28 HEAD_REF: ${{ github.head_ref }}
29 run: |
30 OUTPUT=$(fern generate --docs --preview --id "$HEAD_REF" 2>&1) || true
31 echo "$OUTPUT"
32 URL=$(echo "$OUTPUT" | grep -oP 'Published docs to \K.*(?= \()')
33 echo "preview_url=$URL" >> $GITHUB_OUTPUT
34 echo "Preview URL: $URL"
35
36 - name: Get page links for changed MDX files
37 id: page-links
38 env:
39 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
40 run: |
41 PREVIEW_URL="${{ steps.generate-docs.outputs.preview_url }}"
42 CHANGED_FILES=$(git diff --name-only origin/main...HEAD -- '*.mdx' 2>/dev/null || echo "")
43
44 if [ -z "$CHANGED_FILES" ] || [ -z "$PREVIEW_URL" ]; then
45 echo "page_links=" >> $GITHUB_OUTPUT; exit 0
46 fi
47
48 BASE_URL=$(echo "$PREVIEW_URL" | grep -oP 'https?://[^/]+')
49
50 FILES_PARAM=$(echo "$CHANGED_FILES" | tr '\n' ',' | sed 's/,$//')
51 RESPONSE=$(curl -sf -H "FERN_TOKEN: $FERN_TOKEN" "${PREVIEW_URL}/api/fern-docs/get-slug-for-file?files=${FILES_PARAM}" 2>/dev/null) || {
52 echo "page_links=" >> $GITHUB_OUTPUT; exit 0
53 }
54
55 PAGE_LINKS=$(echo "$RESPONSE" | jq -r --arg url "$BASE_URL" \
56 '.mappings[] | select(.slug != null) | "- [\(.slug)](\($url)/\(.slug))"')
57
58 if [ -n "$PAGE_LINKS" ]; then
59 { echo "page_links<<EOF"; echo "$PAGE_LINKS"; echo "EOF"; } >> $GITHUB_OUTPUT
60 else
61 echo "page_links=" >> $GITHUB_OUTPUT
62 fi
63
64 - name: Create comment content
65 run: |
66 echo ":herb: **预览您的文档:** <${{ steps.generate-docs.outputs.preview_url }}>" > comment.md
67
68 if [ -n "${{ steps.page-links.outputs.page_links }}" ]; then
69 echo "" >> comment.md
70 echo "以下是您已更新的 markdown 页面:" >> comment.md
71 echo "${{ steps.page-links.outputs.page_links }}" >> comment.md
72 fi
73
74 - name: Post PR comment
75 uses: thollander/actions-comment-pull-request@v2.4.3
76 with:
77 filePath: comment.md
78 comment_tag: preview-docs
79 mode: upsert

如果您的仓库接受来自分支的贡献,请使用 pull_request_target 而不是 pull_request,以允许工作流访问您的 FERN_TOKEN 密钥:

.github/workflows/preview-docs.yml
1name: Preview Docs
2
3on:
4 pull_request_target:
5 types: [opened, synchronize, ready_for_review]
6 branches:
7 - main
8
9jobs:
10 run:
11 runs-on: ubuntu-latest
12 permissions:
13 pull-requests: write
14 contents: read
15 steps:
16 - name: Checkout repository
17 uses: actions/checkout@v4
18 with:
19 fetch-depth: 0
20
21 - name: Checkout PR
22 run: |
23 git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-${{ github.event.pull_request.number }}
24 git checkout pr-${{ github.event.pull_request.number }}
25
26 - name: Setup Fern CLI
27 uses: fern-api/setup-fern-cli@v1
28
29 - name: Generate preview URL
30 id: generate-docs
31 env:
32 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
33 HEAD_REF: ${{ github.head_ref }}
34 run: |
35 OUTPUT=$(fern generate --docs --preview --id "$HEAD_REF" 2>&1) || true
36 echo "$OUTPUT"
37 URL=$(echo "$OUTPUT" | grep -oP 'Published docs to \K.*(?= \()')
38 echo "preview_url=$URL" >> $GITHUB_OUTPUT
39 echo "Preview URL: $URL"
40
41 - name: Get page links for changed MDX files
42 id: page-links
43 env:
44 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
45 run: |
46 PREVIEW_URL="${{ steps.generate-docs.outputs.preview_url }}"
47 CHANGED_FILES=$(git diff --name-only origin/main...HEAD -- '*.mdx' 2>/dev/null || echo "")
48
49 if [ -z "$CHANGED_FILES" ] || [ -z "$PREVIEW_URL" ]; then
50 echo "page_links=" >> $GITHUB_OUTPUT; exit 0
51 fi
52
53 BASE_URL=$(echo "$PREVIEW_URL" | grep -oP 'https?://[^/]+')
54
55 FILES_PARAM=$(echo "$CHANGED_FILES" | tr '\n' ',' | sed 's/,$//')
56 RESPONSE=$(curl -sf -H "FERN_TOKEN: $FERN_TOKEN" "${PREVIEW_URL}/api/fern-docs/get-slug-for-file?files=${FILES_PARAM}" 2>/dev/null) || {
57 echo "page_links=" >> $GITHUB_OUTPUT; exit 0
58 }
59
60 PAGE_LINKS=$(echo "$RESPONSE" | jq -r --arg url "$BASE_URL" \
61 '.mappings[] | select(.slug != null) | "- [\(.slug)](\($url)/\(.slug))"')
62
63 if [ -n "$PAGE_LINKS" ]; then
64 { echo "page_links<<EOF"; echo "$PAGE_LINKS"; echo "EOF"; } >> $GITHUB_OUTPUT
65 else
66 echo "page_links=" >> $GITHUB_OUTPUT
67 fi
68
69 - name: Create comment content
70 run: |
71 echo ":herb: **预览您的文档:** <${{ steps.generate-docs.outputs.preview_url }}>" > comment.md
72
73 if [ -n "${{ steps.page-links.outputs.page_links }}" ]; then
74 echo "" >> comment.md
75 echo "以下是您已更新的 markdown 页面:" >> comment.md
76 echo "${{ steps.page-links.outputs.page_links }}" >> comment.md
77 fi
78
79 - name: Post PR comment
80 uses: thollander/actions-comment-pull-request@v2.4.3
81 with:
82 filePath: comment.md
83 comment_tag: preview-docs
84 mode: upsert

在 PR 合并时清理预览链接

要在 PR 合并后自动清理预览链接,请在上述工作流旁边添加此工作流。它使用 PR 的分支名称作为 --id 调用 fern docs preview delete,与生成预览时使用的标识符匹配。

.github/workflows/cleanup-preview.yml
1name: Clean up preview links
2
3on:
4 pull_request:
5 types: [closed]
6
7jobs:
8 cleanup:
9 if: github.event.pull_request.merged == true
10 runs-on: ubuntu-latest
11 steps:
12 - name: Checkout repository
13 uses: actions/checkout@v4
14
15 - name: Setup Fern CLI
16 uses: fern-api/setup-fern-cli@v1
17
18 - name: Delete preview deployment
19 env:
20 FERN_TOKEN: ${{ secrets.FERN_TOKEN }}
21 run: |
22 echo "Deleting preview for branch: ${{ github.head_ref }}"
23 fern docs preview delete --id "${{ github.head_ref }}" || echo "Preview deletion returned non-zero — it may already be gone"