Matrix 首页推荐
Matrix 是少数派的写作社区,我们主张分享真实的产品体验,有实用价值的经验与思考。我们会不定期挑选 Matrix 最优质的文章,展示来自用户的最真实的体验和观点。
文章代表作者个人观点,少数派仅对标题和排版略作修改。
本文速通:
- 「Static Website Generator」近些年的发展历程,简单聊聊代表性产品。
- 我的第一套自动化写作部署方案「Obsidian + Hexo」,只在 Obsidian 中实现「写作」+「发布」。
- 我的第二套自动化写作部署方案「mdbook + Gitlab CI」,尝试线上写作。
第一套方案用于日常学习笔记和个人博客的发布,第二套方案用于自己最近写书。两套方案都比较小众,前者需要有一定的 Obsidian 使用经验,且针对 MaxOS/Linux 用户,没有对 Windows 用户进行适配;后者需要有一定的 git 使用基础。下面先为大家展示效果,感兴趣的读者可以直接跳转到对应章节。
只用在 obsidian 中通过一个快捷键即可发布到自己的个人博客/Github Page/Medium等平台
只用 git push
就可以完成网页的自动发布:
运维不应该是写作的负担
相信很多少数派的作者们都有过自建博客的经历。在读本科的时候,我自己也有一次建站经历,不过后面变得不了了之。流量统计、全栈搜索、MathJax 配置、代码复制等一系列看起来似乎非常基础的功能,都需要自己一项一项去处理。而且当时自己贪心不足,还试图维护了评论区等功能,导致本来就不够稳定的博客系统雪上加霜。这一年里,自己花费在运维上的成本远远高于写作本身。于是,在第二年年末,服务器需要续费的时候,自己终止了这一计划。
在之后的复盘分析中,我认为自己将过度的经历放在了「运维」和「管理」上,而忽视了最底层的「生产」。好比企业的发展,没有底层的生产力,优秀的管理系统和运营策略也很难成功。之后的几年里,我的写作风格也变得更「实用主义」,日用的笔记主要是为了方便自己进行阅读和检索,为此我选择了 Obsidian。丰富的插件社区可以让我能便捷的跨平台阅读自己的笔记。
最近,在少数派的鼓励下,自己的分享欲和创作欲再次被激发。慢慢继续写起这些知识共享的文章。随着自己的积累和沉淀,自己的生产效率和写作速度都得到了一定的提升。尽管现在的语言水平还有提升的空间,但是「码字」这件事情本身已经逐渐变得更加得心应手了。在经历了一段时间的写作和整理之后,自己也对写作工作流程有了一定的改良和提升。在保证了一定的文字积累后,适当筛选,进行整理和输出,从而保证了自己的「产量」。回过头来,就可以清晰的看到自己创作、发布和部署上的痛点和盲点。而不是和自己以前那样,盲目维护一个自己用不上的庞杂系统,最后事倍功半。
千里之行,始于足下。所以在搭建自己的写作系统之前,最重要的还是要多写。正如我之前在 Robert Birming 的 Blog 读到的一句话所言:「I don't wanna tell you what to do, but I know it works, so I say it as plainly as I can: Just write, keep on writing.」1
个人建站的选择
在解决了内容和产量的问题之后,对于建站的事情,也确实是一个见仁见智的选择。不过对于以内容为主的方案,我相信主流的写作方案应该都是基于 Markdown 进行排版和写作的。然而 .md
格式并不是一个在传统网站架构体系内的一环。「HTML」+「CSS」+「JS」中,对内容的组织和引用,并不会直接将 Markdown 文件变成网页布局。最直观的想法是使用 pandoc 或其他导出方案,将 Markdown 转到 HTML。但这样对样式可能产生一定的破坏,而且生成的 HTML 文件并不适合直接作为网站内容,因为该文件只包含了文章内容。考虑到美观性,网站的头尾部分可能需要添加标题,可能会在侧方添加章节目录。所以,为了解决这一痛点,「Static Website Generator」就此诞生,将「基于 Markdown 的网站生成」简化到最简。大家常见的 Hexo 和 Hugo 都是代表产品。
Jamstack - generators 收纳了许多 generator,着实有一种「乱花渐欲迷人眼」的感觉。大家选择当然也是自由的,不过除了「易用」和「美观」之外。在挑选的过程中,一些其他的维度大家应该作为考虑:
- 能否导出 PDF,作为写书的用户可能会有需求
- 轻量的站内搜索功能
- tag 和 category 等分类管理系统,博客用户必备
- 能否执行代码,创作编程教程类用户可能需要
对于 PDF 导出,mdbook 和 mkdocs 是我使用体验比较好的产品,可以方便的进行 PDF 格式导出(mkdocs 需要通过插件实现)。站内搜索有通过 Algolia 实现的方案,但是对于静态页面,Hexo、Docsify 都有默认的搜索功能,使用体验也尚可。如果觉得这些工具使用有难度,也可以试试 Gitbook。
所以对于个人用户而言,「易于维护」和「一定程度美观」作为自己的优先考虑要素比较合适。我工作使用最多的是 mkdocs,插件比较丰富,作为 wiki 比较简单好用,且维护成本非常低。个人生活则维护了两套其他方案,在下面的章节中分别进行介绍。
用「Obsidian + Hexo」实现自动化发布
本文的重点不是 Obsidian 或者 Hexo 本身,Obsidian 和 Hexo 的相关笔记在互联网上也有非常丰富的资料,对它们的使用这里也不做赘述。Obsidian 凭借丰富的社区插件,实现了惊人的拓展能力。所以也吸引了我使用 Obsidian 作为自己的笔记系统。经过多年的发展和进步,Obsidian 的能力也在不断变强。而这个时候,我也对自己的笔记发布过程进行了一些反思。
Wikilink:又恨又爱的语法
从流程上回顾,我是这么进行笔记发布的:
- 进行整理和筛选
- 将文章导出成 Standard Markdown,替换 Obsidian 中的 Wikilink
- 将引用的图片附件上传到图床
- 复制 Markdown 到 Hexo 对应目录,最后使用 Hexo deploy 命令进行发布和部署
这里不得不诟病 Obsidian 的 Wikilink 的兼容性。写作的时候确实非常好用,而且可以非常便捷的对图片的大小进行微调。例如这里,我只需要在图片文件名之后用 |[width]
进行指定说明,就可以对图片的宽度进行限制。虽然在 Markdown 中也可以使用 HTML 语法,使用 <img ...>
对图片进行限制。但是 Wikilink 的语法确实给写作带来了流畅感。对尺寸的修改是「增量式」的,而不是对图片的引用进行整行的替换,当我专注于写作的时候,确实宛如神兵利器。例如下面的这个例子,我直接使用 |600
就将一张过小的图片进行了宽度调整,导出 PDF 的时候也更加美观。因此,这也让我不舍得关闭 wikilinks 的编辑功能。
可是如果要导出一个标准的 Markdown 到其他的静态页面生成器的时候确实也非常麻烦了,因为在 Hexo 等静态网页生成器中,他们只能识别最基本的图片引用,而不能很好的对 Wikilink 语法进行支持。同理,不仅仅是图片,如果 Markdown 引用了其他的 Markdown 文件,这里也非常难进行支持。这个时候我想起了我本科参加 Hackthon 的一个作品,「Make Slides Automatically From Markdown」,初衷想法是借助「reveal.js」将 Markdown 文档自动变成可展示的 PPT。可惜的是,当时的作品没有很好的运维下去,后续的很多替代品,例如 vscode 插件,和 Marp 的出现,都比我最初版本做的更好。不过当初作品中保留下来了一些非常有价值的思想:自动翻译。
如果要具体的实现 Markdown 内容的全部转换工作,需要借助 AST(Abstract Syntax Tree) 的思想。这个概念来自编译原理,是一种用于表示编程语言中的代码抽象语法结构的树形数据结构。通常由编译器或解释器在解析源代码时生成。可用于进行语法分析、代码转换、代码优化和错误检测等操作。下面2通过 mdast 展示了一个例子。
所以通过将每一种语法类型分别处理,再重新进行组合拼装,就可以达到我们需要的效果。例如我们在将 Markdown 进行分割处理(专业一点的说法叫 parse)之后,对我们需要处理的节点进行转换,例如 Wikilink,替换成我们需要的格式就好了。
最终简化版本的程序在:obs2hexo - github(代码比较简陋,希望大家见谅)
主要功能为:替换引用图片的 wikilink,分析 tag,发布到指定的分类上。支持 picgo 上传图床。安装方法也非常简单,直接使用
pip install o2h
即可完成安装。
不过,项目中的缺点也非常明显。在这里进行自我批评和反省:
首先, wikilink 是可以引用非图片格式的文件的,例如其他笔记和 pdf 类型,但是项目中都没有进行处理。其次,wikilink 需要单独一行写出,这里也是由于程序简化导致的,为了方便检测,我进行匹配的方案比较简单粗暴,但是实际引用的时候,可能会有更加复杂的引用情况,例如放在列表中引用。此外,目前为止应该没有支持 Windows,我日用系统是 Linux 和 MacOS,所以还没有在 Windows 下进行测试。这部分我会尽快处理,争取在两周内完成新版本的整理更新。
总之,基于这个小代码项目,就完成了对 Obsidian 文件中 wikilink 和一些细节语法的自动处理。但是距离自动发布还有一些距离,这个时候,另一个 Obsidian 插件,帮我完成了自动化的最后一块拼图。
Obsidian Shell Commands
在我起初使用 Obsidian 的很长一段时间里,我一直不理解这个功能的作用。因为作为 Shell Command Executor,这个功能又太过于鸡肋。如果我为了执行一个脚本,我为什么不通过 iTerm 等终端直接执行呢?
不过后来,在我有了自动化的需求之后,我也确实发现了他的实用性。我通过自己的 obs2hexo 翻译器确实完成了 Markdown 文档的转换,变成了可以直接放到 Hexo 目录下使用的 Markdown 格式,并且对图片文件进行了管理。但是这个过程也是比较僵硬的,我需要:
- 打开终端
- 运行 obs2hexo
- 复制文件到对应目录
- 执行
hexo g
和hexo d
进行发布
如此反复,也确实不爽。所以最终,在 Shell Command 的帮助之下,完成这部分脚本命令的自动执行。下面我用自己的自动发布作为例子介绍使用方法。
首先是插件安装,在社区插件中找到 Shell commands:
之后点击安装即可,此时在设置栏中,会出现 Shell commands 的设置界面。
新建 shell commands 即可将自己的脚本写入,这里我的脚本内容如下:
export PATH="$HOME/.pyenv/shims:$HOME/.yarn/bin:$HOME/.nvm/versions/node/v16.16.0/bin:$PATH";
obs2hexo -c {{_category}} -p {{file_path:absolute}};
mkdir -p $HOME/Documents/Projects/Blog/source/_posts/{{date:YYYY}};
cd $HOME/Documents/Projects/Blog/source/_posts/{{date:YYYY}};
mv /tmp/o2houtput/* .;
hexo g;
hexo d;
逐行进行说明:
- 设置环境变量,我的 python 环境是通过 pyenv 进行管理的,而 hexo、picgo 则分别通过 nvm 和 yarn 进行安装管理,所以将他们的对应路径写入环境变量中。(使用其他环境管理工具也只需要将对应的目录写入 PATH 中即可)
- 实现翻译,
{{_category}}
是我手工添加的环境变量,在 Preaction 中进行设置,稍候会展示。{{file_path:absolute}}
则是 Shell Commands 插件提供的宏,使用时会替换成当前文件的绝对路径。 - 检查 Hexo 存放 Markdown 文件的路径,我多设置了一个按年份分类的管理。
- 前往对应目录
- 完成文件复制
- Hexo 完成静态网页生成
- Hexo 完成部署
上面的 Preaction 是我为了方便「发布时进行博客分类」,单独设置了一个页面:
这样就会在我使用这个 Shell Command 脚本时,弹出窗口让我填写类别了,效果如下:
完成配置之后,发布就可以只在 Obsidian 中完成,而不需要切到终端输入命令。
其他
基于类似的方案,我还额外进行了 Medium 平台的自动发布和 WordPress 的自动发布。不过这些发布其实摧毁了 Obsidian 的核心双链功能。wikilink 引用了其他笔记的时候,这里的发布方案并不能进行智能的处理。和官方的 Obsidian Publish 比起来还是有一些距离的。不过毕竟是小作坊的小打小闹,也是抛砖引玉为大伙提供一个自动发布的思路。也欢迎大家对我的方案进行改进建议和批评。
懒人写书「mdbook + Gitlab CI」
第二个故事是我独立在 Obsidian 之外进行的一系列创作。项目的出发点是来自 Quivr - Github。少数派的「玉树芝兰」老师前两天也发了一篇非常不错的教程:如何用 ChatGPT 和你的卡片笔记对话?开源应用 Quivr 尝试,对我非常有启发。
项目部署之后,可以通过整理好的笔记,完成自己知识系统的描述,借助 LLM 辅助自己对已经掌握的知识进行索引和查询。另一个出发点来自 Knowledge - Github,作者将自己的已有知识进行了梳理和总结,保留了自己的学习历程。因此,在我自己博士生活正式开始之前,我也希望通过这个过程整理自己已经掌握的知识。为了部署的便捷性和运维的简洁,我最终选择了 mdbook - Github 作为自己的写作框架。
mdbook 开始的基础框架
mdbook 是一个基于 Rust 开发的静态网页生成工具,可以用于生成一个轻量的静态网站,默认提供了检索、pdf 生成、分享等功能。安装基于 cargo 进行(这里不赘述 cargo 的安装,参考相关链接 Rust installation)3:
cargo install mdbook
更多安装信息参考: mdbook - installation
常用指令如下:
# Create a new mdbook project
mdbook init
# Serve on local and open a browser
mdbook serve --open
# Generate website
mdbook build
mdbook 拓展
在基础的 mdbook 框架上,还有一些提升拓展,也可以通过 cargo 进行安装:
- mermaid 流程图功能:mdbook-mermaid - Github
- toc 标题生成:mdbook-toc - Gituhb
- 特殊样式引用:mdbook-admonish - Github
都可以使用 cargo install
直接进行安装。
Gitlab CI/CD 自动管理
这个项目也是我个人第一次尝试使用 Gitlab CI/CD 进行项目管理。自己的 Github 账号由于没有续上教育认证,暂时没有 Pro 的额度,所以将一部分业务转移到了 Gitlab 上。先谈谈 CI/CD,持续集成交付,包含了:
- Continuous Integration (CI)
- Continuous Delivery (CD)
- Continuous Deployment (CD)
几个层面的含意。具体的介绍可以参考 Gitlab CI/CD 上详细的介绍。
概括的说,持续集成(CI),通过将开发人员的代码频繁地集成到共享存储库中,然后自动构建和测试该代码,以确保新功能和修改不会导致主干代码的破坏。
而持续交付(CD)是在持续集成的基础上构建的方法,它旨在自动化软件交付过程。通过持续交付,开发团队能够自动构建、测试和部署应用程序,使新功能和更改能够快速、可靠地交付给最终用户。
CI/CD 通过自动化和自动化工具的使用,可以加快开发团队的软件交付速度,减少错误并增加整体质量。
上述的概念比较生硬,在这里我们写作的场景下,可以理解成:
作者只需关注作品,上传到 Gitlab 之后,部署和发布可以由平台自动完成
回归内容,具体的操作可以参考如下流程。
首先在 Gitlab 中创建一个新的 Public 项目用于放置内容:
完成创建之后,我们需要上传内容。在使用 git 上传 mdbook 项目之前,还需要写入 gitlab CI 的配置文件:
pages:
stage: deploy
image: "rust:latest"
variables:
CARGO_HOME: "$CI_PROJECT_DIR/cargo"
before_script:
- export PATH="$PATH:$CARGO_HOME/bin"
- mdbook --version || cargo install mdbook
- mdbook-toc --version || cargo install mdbook-toc
- mdbook-admonish --version || cargo install mdbook-admonish
- mdbook-mermaid --version || cargo install mdbook-mermaid
script:
- mdbook build
artifacts:
paths:
- public
only:
- main
cache:
paths:
- $CARGO_HOME/bin
这里提供我都版本供大家参考,思路是在一个 rust 容器中,检查我们需要的包即可。
为了使用上这些额外的插件,mdbook 的配置文件也要进行一些微调,book.toml
文件内容的模板如下:
[book]
authors = ["John Doe"]
language = "en"
multilingual = false
src = "src"
title = "xxxx"
[build]
build-dir = "public"
create-missing = true
[preprocessor.toc]
command = "mdbook-toc"
renderer = ["html"]
marker = "[TOC]"
[preprocessor.admonish]
command = "mdbook-admonish"
assets_version = "2.0.0" # do not edit: managed by `mdbook-admonish install`
[preprocessor.mermaid]
command = "mdbook-mermaid"
[output.html]
mathjax-support = true
additional-css = ["./theme/catppuccin.css", "./theme/catppuccin-highlight.css", "./theme/mdbook-admonish.css"] # additional theme settings, this line is optional
git-repository-url = "https://gitlab.com/Chivier/chivipedia/"
git-repository-icon = "fa-gitlab"
additional-js = ["mermaid.min.js", "mermaid-init.js"
[output.html.playground]
editable = false
copyable = true
copy-js = true
line-numbers = false
runnable = false
[output.html.fold]
enable = true
level = 0
之后和一般的 git 使用方法一样,上传自己已经写好的内容。接着可以看到 Gitlab pipeline 中有对应的任务:
由于这里基于容器,并且安装相关包使用的是 cargo,效率有一定程度的影响,可能会有 5~20 分钟不等的延迟才能生效。所以为了验证效果,建议在本地配置 mdbook 并使用 mdbook serve
命令进行验证。
可选操作:之后还可以在 Settings > Pages 里面更换网页的域名。
总结
写作是一个长期的过程,也是我的一个日常爱好。所谓「千里之行,始于足下」,我并非一个成熟的作者,但是我也确实有一些微小的表达欲和分享欲,每天也随性写几行自己的感受和心得。过去一年,也确实有了十几万字的笔记和作品积累,也不希望敝帚自珍,所以找了个时间,简化了自己分享发布的流程。希望上面的两种自动化写作方案能帮到需要的朋友,让发布和分享不会成为大家的负担。