手把手教你做个生成静态网页的小工具:podgen

时间:2022-05-06
本文章向大家介绍手把手教你做个生成静态网页的小工具:podgen,主要内容包括产品定义、产品实现、授权、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

程序人生之图穷匕见的podcast发了四期(三期正式的)后,有几个读者说在微信上听效果不好,又没法暂停(停下后再听又从头开始了),能不能在iTunes 播客里面听?程序君觉得这还真是个事,于是便考虑在iTunes里注册,一尝试才发现,iTunes不能帮你host你的episodes,它只是个聚合器,有点像已经死掉的google reader,你提交一个rss,iTunes审核后,就收录你的podcast了。所以,首先我得设置个站点放我的这些episodes。

在查阅了不少现有的static site generator(SSG)后,我发现他们生成的rss都不符合iTunes的格式,而且这些工具主要还是面向博客用户,如果要支持语音播放的话还得修改他们的template,加入一个前端的播放器。所以,使用已有的SSG这条路,我就没走下去。

如果不用SSG,一个可行的方案是使用万能的wordpress。不过wordpress对于我这样一个小需求实在是太重了,整个LAMP栈需要装不说,还得自己搭一台服务器上去,不值当。

想来想去,我觉得还是自己写一个类似于jykell的命令行工具,走SSG的路子。部署嘛,有免费的github pages,对我而言,足够好了。名字嘛,俗一点,就叫podgen(podcast generator)好了。接下来,我谈谈做这么个小工具,需要怎么考虑和如何实现。

产品定义

当开始构想podgen时,有几个需求是我重点考虑的:

  • 尽可能地简单,稍稍有一丁点git使用的经验就能使用
  • 尽可能与模板decouple,这样第三方比较容易提供新的模板
  • 尽可能方便不同平台的安装
  • build的速度要快

第三条和第四条促使我先后放弃了python和clojure。安装python的工具在osx和linux下问题不算特别大,但在windows下很可能是个梦魇;而clojure虽然可以打成一个uberjar,但作为一个命令行工具,每次运行启动jvm那一瞬间的迟滞,还是让人很不爽的,所以最终我选择了不那么精通的golang。

产品的功能则按照一切SSG工具的惯例,切分成几个部分:

  • init:初始化项目文件,一般是将模板文件拷贝到项目里,然后生成一些初始化的配置
  • build:把模版文件编译成html,然后拷贝到build目录下
  • server:在本地运行一个http服务器,方便查看修改
  • push:把所有的修改commit后,push到github上。此时,对gh-pages分支下的文件,github会进行hosting。

我们先看用户使用的整个过程,首先他创建一个空的github repo并clone到本地。接下来就可以直接init了:

如果用户什么都不改就运行 podgen build,应该也能工作 —— 打开build之后产生的index.html,有一个最直观的印象:

这个初始的版本可以立即部署到线上:

而访问线上的 username.github.com/projectname 看到的就是这个样子(这里访问的是 tyrchen.github.com/podgen-test):

这个流程对用户来说是足够简单和可验证的。现在新产品实在太多了,如果五分钟用户搞不清楚一个产品怎么用的话,那么,用户就基本丧失试用下去的兴趣了。

OK,现在一切操作都很顺畅,所见即所得,用户希望能修改添加真实的数据进去,通过运行podgen,用户知道要修改的文件在channel.yml和items.yml里。这里,使用yaml而非json格式,也是考虑到yaml相对而言更好编辑一些。

修改编辑拷贝,最终生成这样的效果:

嗯,这就是podgen产品的一个完整的使用过程。生成好的site满意了之后,可以提交到iTunes里等待审核,通过后,别人就可以在「播客」里搜索到了。

产品实现

这样一个小产品,基本功能都是围绕着易用性走的,所以没什么酷炫的地方。

首先是模板和代码分离,这是个很小的点,但非常重要。jekyll在这一点上做得不好,如果你发现了它模板的问题,还得pull request到整个repo去,麻烦。分离还能把决定权交给用户,由用户选择使用什么样的模板来构建。

所以,podgen是两个repo组成的:

  • github.com/tyrchen/podgen:核心代码
  • github.com/tyrchen/podgen-basic:基础模板

现在基于bootstrap的免费模板很多,我选择了startboostrap-landing-page这个模板(repo里有来源)。稍作修改即可。基于bootstrap的模板的好处是desktop和mobile展现起来都不错。

mp3播放器是前端主要的障碍,选一个license合理又使用简单,得花一些心思。比较之下我选择了soundmanager2,基于BSD。

前端搞定之后,这个项目就基本完成60%。

后端首要的难点是选择一个好的CLI library。如果是其他语言,我都有非常熟悉的CLI library,但go我还是第一次认认真真写复杂的CLI(简单参数,没有子命令的不算)。go自带的flag不够强大,所以得找第三方的库。首先我注意到的是consul。由于consul是用golang写的,其命令行又简洁强大,所以我就先从他身上上偷师。可惜hashicorp(consul背后的公司)没有把其强大的命令行工具抽象出来,我只好把consul的command模块抄过来,然后裁剪了一下。不难,很快就把CLI弄好了。

后来在做其他东西的过程中,偶尔发现了 spf13/cobra 这个库。spf13是github上的一个大牛,我曾经vim的配置就是抄他的。这哥们写的cobra库和consul的command模块有异曲同工之妙(也许天下代码一大抄?),我就把自己修改的从consul抄来的CLI全部废掉,换用了cobra,很简单,很好用。

golang毕竟年轻,旁门左道的库少一些。要做一个能让iTunes接受的podcast,必须要有符合其xml规范的rss,而这种偏门的东西,在golang很难找到成熟的。权衡再三,我使用了没有人用过的 andjosh/gopod,抱着不好用就自己写的态度,发现这货还可以。

解析yaml和处理template不是什么事,golang自己的库就能搞定。但静态语言在这里的缺陷就被无限放大了:作为一个产品,我希望不需要改动代码可以随意在yaml文件里增加新的域,同时在template里通过模板语言使用这些新的域。这在python/clojure里不用修改代码就能秒杀的需求,在golang里面根本找不到优雅的解决方案:毕竟,一个yaml解构需要一个golang的struct去解析,可惜这是固定死的,改yaml结构意味着该代码重新编译;如果不这样做,使用类型断言的话,代码又会奇丑无比。

golang的template也挺弱(也许是我的道行的问题),一个paginator,处理当前页高亮,如果当前页是第一页不允许上翻,如果当前页是最后一页不允许下翻等等这样大众的UI需求在golang的template里丑陋地让人吐血,也许写几个helper放在FuncMap里会好一些,但最后我受不了就干脆写了个函数生成这个Html片段。

最后一个不算难点的难点是调用系统命令。比如调用git的各种功能,以及系统的cpmv等帮助用户简化整个使用的流程。如果直接走shell命令,代码很简单,跨平台很难。如果使用libgit及封装好的系统库,跨平台不难,但代码要额外花时间写。考虑这个project我想在今天就能发布一个可用的版本,就走了前面一条路,使用了 github.com/codeskyblue/go-sh 这个库。

开发就说这么多,核心代码不到500行,很容易读。

授权

最后谈谈授权,做一个开源软件,最好指定好软件的授权,方便其他人使用。一般而言,你对衍生的代码的归属以及是否开源不在意的话,可以使用MIT,BSD,Apache。

podgen这个工具花了我一天半的功夫完成了80%的功能,现在已经可用。本着eat your own dogfood的态度,我的podcast使用它生成并已经上线,感兴趣的话可以访问:podcast.tchen.me;如果要订阅的话,osx下的iTunes里已经可以试用,打开File菜单,选择"Subscribe to Podcast",然后输入rss的地址:podcast.tchen.me/rss.xml 即可。如果要在手机上订阅,还需等一等,apple还在审核。

如果你也想做自己的podcast网站,不妨试试这个工具(github.com/tyrchen/podgen),osx下可以直接下载使用,以后会增加 brew install podgen 的安装方式。