如何使用让Sphnix文档支持多版本构建

By iswbm / Published At 2024-06-16 / In categories Web

python, sphinx, web

若你参照我之前写过的 Sphinx 教程自己搭建过个人博客或者在线文档站点的话,你会发现通过 RTD 托管后,在左下角会有多版本切换的边栏。

但在本地使用 make html 构建生成的 html ,是没有这个的。由于我一直使用 RTD 进行托管,对于这个差异也没有必要去关心了。

可是最近我在搞 SEO 往 Google 上提交站点的 sitemap 时,发现往 Sphinx 项目中加入 sphinx-sitemap 插件后,再使用 RTD 的时候生成的 sitemap 是有问题的,主站的 sitemap 仅有几个版本分支的 url,文章的 url 一个都没有。

但是在本地使用 make html 又是没有问题的,这让我非常的困惑,但又没有办法,毕竟我无法对 RTD 的构建过程进行调试。

于是我只能放弃 RTD 的托管,而改用本地构建,并把构建好的 HTML 放到自己的服务器上,使用 NGINX 进行代理,对外提供访问服务。

但是使用本地构建,我又得放弃 RTD 提供的多版本切换边栏,这也让我不能接受。

原因是在我的 《PyCharm中文指南》在线文档中,有分 windows 版本和 mac 版本的文档,对于不同的人需要看不同版本的。

如果在界面上提供这个入口的话,那么有很多人就不知道怎么切换了。

于是乎,我就在网络上,找了各种 Sphinx 支持本地构建插入 RTD Theme 的 multi-version sidebar。

在网上能找到的插件有两个:

  1. sphinxcontrib-versioning
  2. sphinx-multiversion

其中 sphinxcontrib-versioning 是 N 多年前的项目了,我在本地使用报各种报错,去 github 上一看,发现这个仓库如今已经没人维护了,在 github 一堆 issue 没人解决。

最后只能把希望寄托在 sphinx-multiversion 上面。在验证 sphinx-multiversion 的过程中,实际上也遇到了不少问题,花了不少时间,直到昨天晚上才把流程跑通。

下面来分享下经过整理精简过的教程,如果你刚好有需要,可以参考下。

1. 安装插件

使用 pip 安装 sphinx-multiversion 插件

$ pip3 install sphinx-multiversion

并把该依赖库写入到 requirements.txt 文件中

echo "sphinx-multiversion==0.2.4" >>requirements.txt

并且该插件追加到 Sphinx 的配置文件中的 extensions 中

extensions = ['chinese_search','sphinx.ext.mathjax','sphinx_sitemap', 'sphinx_multiversion']

2. 使用插件

使用 sphinx-multiversion 帮你做的其实就是把你设定好的 html 模板渲染好写入每一个生成的 html 的文件中。

因此 ,在用它之前,你首先得写好你的模板,在这里由于我们要使用 RTD Theme 的样式,所以你得找到这样的现成的样式,在官方文档上刚好有 share,我直接搬过来了。

{%- if current_version %}
<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="versions">
  <span class="rst-current-version" data-toggle="rst-current-version">
    <span class="fa fa-book"> Other Versions</span>
    v: {{ current_version.name }}
    <span class="fa fa-caret-down"></span>
  </span>
  <div class="rst-other-versions">
    {%- if versions.tags %}
    <dl>
      <dt>Tags</dt>
      {%- for item in versions.tags %}
      <dd><a href="{{ item.url }}">{{ item.name }}</a></dd>
      {%- endfor %}
    </dl>
    {%- endif %}
    {%- if versions.branches %}
    <dl>
      <dt>Branches</dt>
      {%- for item in versions.branches %}
      <dd><a href="{{ item.url }}">{{ item.name }}</a></dd>
      {%- endfor %}
    </dl>
    {%- endif %}
  </div>
</div>
{%- endif %}

这个文件可以放在与 conf.py 同级目录下的 _templates 目录,文件名为 versions.html,如果你先前没有该目录,需要你手动创建它。

$ cd source
$ mkdir _templates

接下来,你得在 conf.py 中写几个配置,告诉 Sphinx 去哪里找到这个 versions.html

templates_path = ['_templates']
html_sidebars = {
    '**': [
        'versioning.html',
    ],
}

# 指定哪个分支为 lastest 版本
smv_latest_version = 'master'

接下来就可以使用 sphinx-multiversion 提供的 cli 命令去构建多版本的 html 了。

$ sphinx-multiversion source build/html

其中两个参数的意义是:

  • source:表示 md 或 rst 文档所在的目录,一般是 conf.py 所在目录
  • build/html:表示你要将构建好的 html 放在哪个目录下

执行后,会在 build/html 目录下生成多个目录,每一个目录是一个版本。

 $ ls -l build/html/
drwxr-xr-x 19 root root   4096 Jul  9 01:31 master
drwxr-xr-x 19 root root   4096 Jul  9 00:39 v1.0
drwxr-xr-x 19 root root   4096 Jul  9 00:39 v2.0
drwxr-xr-x 19 root root   4096 Jul  9 00:39 v3.0

最后一步,就配置 nginx 代理,并重启 nginx

server {
        listen 80;
        server_name magic.iswbm.com;
        root /home/wwwroot/magic-python/build/html/;
        }

如此一来,以后访问在线文档的三个版本就得使用如下三个 url:

是不是我还不错呢?

不过这还不够,访问最新版本的时候,不应该还使用 http://magic.iswbm.com/master,而应该还是根目录 http://magic.iswbm.com

那怎么办呢?

我想到的办法是把 master 目录里的文件及文件夹,全部拷贝一份到 与 master 同级目录下(如果你有更好的办法,请一定留言告诉我)。

因此最后的构建命令变成了这样

rm -rf build/ &amp;&amp; sphinx-multiversion source build/html &amp;&amp; cp -rf build/html/master/* build/html/

为了方便,我为这条命令定义了别名 rebuild,这样以后要构建只要执行 rebuild 即可

$ echo "alias rebuild='rm -rf build/ &amp;&amp; sphinx-multiversion source build/html &amp;&amp; cp -rf build/html/master/* build/html/'" >> /root/.bash_profile
$ source /root/.bash_profile
iswbm

Author

iswbm

Cloud Computing & Container & Front-end & Back-end R&D Engineer. I like to explore new technologies, and in my spare time, I also play around with Logseq and other efficiency tools. You can follow me on GitHub to learn more, or add me on WeChat (stromwbm) to communicate with me.