发布于 2026-01-06 0 阅读
0

使用 Docsify、Lefthook 等工具维护开源软件文档

使用 Docsify、Lefthook 等工具维护开源软件文档

“程序的好坏取决于其文档的质量。”——乔·阿姆斯特朗,Erlang编程语言的作者

优秀的开源项目应该具备哪些要素?如果你熟悉 Ruby,可以访问Gem Check(由我本人创建)查看所有最佳实践。但无论使用何种语言或技术栈,对于所有开源项目而言,有一点至关重要:文档。

所有开源项目,无论规模大小,都必须编写文档(即使是现在已被弃用的、臭名昭著的left-pad也需要文档)。在大多数情况下,一份精心编写的 README 文件就足够了,但你还可以更进一步,创建一个“超棒的 README”

然而,随着项目规模的扩大,README 文件支持的文档不再适用。我们可能会说“ 铁路README 文件无法扩展!我们说得没错。

这就引出了一个问题:“还有什么比单个 Markdown 页面更具扩展性呢?”

本文将分享我对这个问题的回答。

起初,一片混沌。

在我们开始讨论docsify之前,让我先向您展示一些我在最终选择 docsify 之前使用过的其他工具

GitHub Wiki

我第一个超出 README 文件容量的项目是AnyCable。我没怎么考虑就把一些文档移到了 GitHub 内置的 wiki 里(旧版本的 gem 文档仍然保留在那里)。既然工具已经存在了——这样做当然没问题,对吧?嗯,也不一定。

事实证明,GitHub wiki 内置是唯一的优点,而缺点却数不胜数:

  • 独立更新代码和文档很可能导致不一致。
  • Web 编辑器还有很多不足之处;例如,您无法轻松上传图像(从技术上讲是可以的,但这需要克隆 wiki 存储库)。
  • 只需更改标题,交叉引用就很容易失效;没有办法设置永久 URL(或者我遗漏了什么)。

docs文件夹

解决 Wiki 所有缺陷(明白我的意思吗?)的一个方法是docs在仓库中创建一个文件夹,并将 Markdown 文件放入其中。GitHub 会.md在浏览器中打开文件时自动显示格式。而且你还可以直接在网页界面编辑内容!既然如此,为什么还要使用 Wiki 功能呢?

所以,我们找到了一种存储文档内容的好方法。但是用户界面/用户体验呢?我们难道不想让文档更易于使用吗(例如,添加搜索功能)?是的,我们想。

让我们把GitHub上的文档转换成Web格式。

Jekyll 和 GitHub Pages

GitHub只需点击几下即可帮助您设置由Jekylldocs支持的文档网站:转到“设置”->“GitHub Pages”,选择网站的来源(我们使用文件夹),然后选择您喜欢的 Jekyll 主题。

GitHub Pages 设置

现在访问设置页面中的 URL,即可找到您的全新文档网站!

Jekyll 网站示例

遗憾的是,GitHub Pages 与 Jekyll 的集成功能有限,尤其是在插件方面。它能实现的功能非常有限。而且,在我看来,如果你想自定义页面外观或添加一些交互功能,Jekyll 就显得过于复杂了。

我们来看看现代工具。

恐龙纪录片

我尝试过的第一个现代文档生成器是Docusaurus。我们用它构建了Clowne的 gem 文档。

克劳恩文档

但我不得不承认,这段经历并不愉快:

  • 鉴于该库是用 React 构建的,我原本期望它能提供更直接的自定义选项。然而,你实际上无法访问其内部机制,而且库的作者也不希望你进行深入的调整。
  • 我第一次使用它的时候,它还不支持实时重新加载,这对于本地开发至关重要(现在看来这个问题已经解决了)。
  • 它需要一个单独的构建步骤(yarn run build)。

所以我开始寻找其他选择,然后发现了docsify

文档化

与 Jekyll 或 Docusaurus 相比,Docsify 使用不同的方法来“生成”网站:它动态渲染 markdown 文件,不需要构建阶段。

它还支持服务器端渲染,甚至支持离线模式

要将您的文档“文档化” docs,您需要执行以下操作:

  • 添加docs/.nojekyll文件以禁用 Jekyll。
  • 添加index.html加载和配置 docsify:
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <meta charset="UTF-8">
  <link rel="stylesheet" href="//unpkg.com/docsify/themes/vue.css">
</head>
<body>
  <div id="app"></div>
  <script>
    window.$docsify = {
      loadSidebar: true,
      subMaxLevel: 2,
      repo: 'palkan/docs-example',
      basePath: '/docs-example/',
      auto2top: true,
      homepage: 'https://raw.githubusercontent.com/palkan/docs-example/master/README.md'
    }
  </script>
  <script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
  <script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
  <script src="//unpkg.com/prismjs/components/prism-ruby.min.js"></script>
</body>
</html>

就这样!现在你得到类似这样的结果:

简单的 Docsify 示例

请注意,我添加了一些特定的配置选项:

  • basePath: '/docs-example/定义您网站的根路径(对于 GitHub Pages 上的个人项目,它就是仓库名称);
  • homepage: '...'设置为仓库的 README 文件(默认情况下 docsify 使用该docs/README.md文件);这使我们能够保持两个主页(GitHub 和 web)同步。

而这仅仅是个开始!Docsify 的主要优势之一就是可以通过插件轻松添加实用功能。

我们来添加搜索功能。

我们只需要添加这两行代码:

 window.$docsify = {
   loadSidebar: true,
   subMaxLevel: 2,
+  search: 'auto',
   repo: 'palkan/docs-example',
   basePath: '/docs-example/',
   auto2top: true,
   homepage: 'https://raw.githubusercontent.com/palkan/docs-example/master/README.md'
   }
 </script>
 <script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
 <script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
 <script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
 <script src="//unpkg.com/prismjs/components/prism-ruby.min.js"></script>

!我们可以搜索文档了!搜索功能在客户端实现,并由保存在 . 中的索引提供支持localStorage

Docsify 搜索演示

我喜欢 docsify 的另一个原因是它易于自定义样式:该库使用CSS 属性,因此无需编写自己的 CSS 即可更改颜色和布局!

您可以直接在以下位置更改颜色和字体大小index.html

<style>
  :root {
    --theme-color: #ff5e5e;
    --theme-color-light: #fd7373;
    --theme-color-dark: #f64242;
    --theme-color-secondary: #ff5e5e;
    --theme-color-secondary-dark: #f64242;
    --theme-color-secondary-light: #fd7373;
    --text-color-base: #363636;
    --text-color-secondary: #646473;
  }
</style>
<body>
  ...
</body>

添加红色款式

是不是很棒

上述示例的代码可以在 GitHub 上找到:palkan/docs-example

请访问AnyCable 文档网站和相应的代码库,查看更高级的示例!

AnyCable 文档

额外福利*:您可以在此 gist中找到“在 GitHub 上编辑”功能的浮动操作按钮的实现

使用代码检查器保持文档健康

在本教程的第二部分,我想分享一下我维护文档良好状态的方法。所谓“良好状态”,指的是:

  • 源文件(Markdown)和代码示例(Ruby)的风格一致性。
  • 拼写正确。
  • 有效的代码示例(从语法角度来看)。
  • 有效链接(任何链接都不应指向 4xx/5xx)。

上述所有情况都有相应的开源工具(有时甚至有很多),这并不奇怪。

Markdownlint帮助我强制执行 Markdown 文件样式(它还有NodeJS 版本VS Code 插件)。

我通常还会禁用几条规则:

  • 行长度(MD013)—现代编辑器(如 VS Code)可以通过换行来处理长行。
  • HTML 片段——有时 Markdown 还不够用。

为此,我.mdlrc在项目根目录下放置了一个文件,内容如下:

rules "~MD013", "~MD033"

为了处理 Ruby 语法,我使用RuboCop以及我专门为此编写的rubocop-md插件。作为默认样式配置,我最近开始使用standard .

要使此设置生效,您需要:

  • 安装standardgems rubocop-mdgem install standardgem install rubocop-md
  • 添加一个.rubocop.yml包含以下内容的列表:
require:
  - standard/cop/semantic_blocks
  - rubocop-md

inherit_gem:
  standard: config/base.yml

Standard/SemanticBlocks:
  Enabled: false
  • 快跑,鲁博科普!

对于拼写检查,还有另一个 Ruby 工具——Forspell。它是对著名的Hunspell包的封装。

由于涉及大量技术术语,您在第一次运行 Forspell 时可能会看到很多警告:

$ forspell docs/

docs/development/lefthook.md:5: lefthook (suggestions: left hook, left-hook, leftmost)
docs/development/lefthook.md:9: lefthook (suggestions: left hook, left-hook, leftmost)
docs/development/lefthook.md:11: Hombrew (suggestions: Hombre, Hombres, Hombre w)
docs/development/lefthook.md:17: Golang (suggestions: Golan, Golan g, Angolan)

这个问题很容易解决,只需运行带有相应--gen-dictionary标志的 Forspell 命令即可:它会生成一个forspell.dict包含所有未知单词的文件。别忘了仔细检查这个文件,并删除实际的拼写错误。

最后,为了确保我们的文档没有任何死链接,我使用了liche——一个用 Go 语言编写的 Markdown 和 HTML 链接检查器:

$ liche -r docs/

 ERROR    https://githb.com/palkan/anyway_config
                Dialing to the given TCP address timed out

Liche缺少一些我希望它具备的功能:例如,它不会对返回404错误的URL发出警告。尽管如此,我发现它比其他现有工具略好一些。

为了管理所有这些代码检查工具,我使用Lefthook进行本地开发,使用 CircleCI 进行拉取请求。

以下是我的配置文件的内容lefthook.yml,该文件存储了 Lefthook 的配置

pre-commit:
  commands:
    mdl:
      glob: "**/*.md"
      run: mdl {staged_files}
    liche:
      glob: "**/*.md"
      run: liche -r docs
    forspell:
      glob: "**/*.md"
      run: forspell {staged_files}
    rubocop:
      glob: "**/*.md"
      run: rubocop {staged_files}

CirclCI 的配置语句稍微冗长一些,但功能基本相同:

version: 2.1

workflows:
  version: 2
  build_and_test:
    jobs:
      - checkout
      - md_lint:
          requires:
            - checkout
      - links_lint:
          requires:
            - checkout
      - spelling:
          requires:
            - checkout
      - rubocop:
          requires:
            - checkout

executors:
  golang:
    docker:
      - image: circleci/golang:1.12.4-stretch
  ruby:
    docker:
      - image: circleci/ruby:2.5-stretch

jobs:
  checkout:
    executor: ruby
    steps:
      - restore_cache:
          keys:
            - project-source-v1-{{ .Branch }}-{{ .Revision }}
            - project-source-v1-{{ .Branch }}
            - project-source-v1
      - checkout
      - save_cache:
          key: project-source-v1-{{ .Branch }}-{{ .Revision }}
          paths:
            - .git
      - persist_to_workspace:
          root: .
          paths: .
  md_lint:
    executor: ruby
    steps:
      - attach_workspace:
          at: .
      - run:
          name: Install mdl
          command: gem install mdl
      - run:
          name: Markdown lint
          command: mdl docs
  links_lint:
    executor: golang
    steps:
      - attach_workspace:
          at: .
      - run:
          name: Install liche
          command: go get -u github.com/raviqqe/liche
      - run:
          name: Check links
          command: liche -r docs
  spelling:
    executor: ruby
    steps:
      - attach_workspace:
          at: .
      - run:
          name: Install hunspell
          command: sudo apt-get install hunspell
      - run:
          name: Install forspell
          command: gem install forspell
      - run:
          name: Check spelling
          command: forspell docs/
  rubocop:
    executor: ruby
    steps:
      - attach_workspace:
          at: .
      - run:
          name: Install standard
          command: gem install standard
      - run:
          name: Install rubocop-md
          command: gem install rubocop-md
      - run:
          name: Check Ruby style
          command: rubocop

完整的示例可以在docs.anycable.io仓库中找到(参见 PR #14#15)。


我花了很长时间(真的,好几年)才搭建出这套方案。现在我几分钟就能启动一个新的文档网站。希望这篇文章对您有所帮助,下次您需要为项目创建基于 Web 的文档时,可以考虑采用类似的方法。

文档万岁!


请访问https://evilmartians.com/chronicles阅读更多开发者文章

文章来源:https://dev.to/evilmartians/keeping-oss-documentation-with-docsify-lefthook-and-friends-11e5