前段时间翻译的 Spring MVC 官方文档完成了第一稿,相关的文章和仓库可以点击以下链接。这篇文章,主要是总结一下这个翻译项目自开始到上线发布,完整的一个生命流程。内容包括 翻译环境搭建 、项目管理 与 自动化构建 三部分。
翻译与写作一样,首要之事均为专注于翻译 /写作本身,而不考虑样式等方面。而章节之间的联系,自然也不想过多操心,这部分与样式一起,都可交由工具去处理。然后版本管理,不用说一定要上,后面也会看到 github 的生态圈使得它与其他工具做到了无缝集成。那么总结起来,我们需要的工具大体是:
Markdown 是一种近乎完美的写作标记语言,其最大的功劳便是将写作从内容中分离出来,这个分离使你只专注于写作内容本身,极大地提高了效率及工作愉悦度。没有 markdown 的话,会是怎样一种情况?想想 HTML 和写论文经常使用的 word 。你在 HTML 中为内容混入各种各样的样式,写论文时最痛苦的莫过于调样式(不过笔者当年写论文时通过 Office Word 的样式窗也是完美地解决了内容和格式的问题)。
比如说,上面这段文字在 markdown 中写出来是这样的:
## 环境搭建
翻译与写作一样,首要之事均为专注于翻译 /写作本身,而不考虑样式等方面。而章节之间的联系,自然也不想过多操心,这部分与样式一起,都可交由工具去处理。然后版本管理,不用说一定要上,后面也会看到 github 的生态圈使得它与其他工具做到了无缝集成。那么总结起来,我们需要的工具大体是:
* markdown
* markdown 编辑器 Atom
* 版本管理 Git
* 代码托管平台 Github
* 写书专用工具 Gitbook
* HTML 转 markdown 工具
说起来 markdown 这个名字也有点意思,一般的标记语言叫 markup language 。这里将 up 改成 down ,寓意着将标记语言中与内容本身无关的标记全部剔除,形成一个精简子集。本篇不是 markdown 用法记,所以更多的语法请自行~~百度~~Google 。我这里可以提供几个链接:
编辑器的选择并无定式,打造一个完全符合自己操作习惯的编辑器也是每个程序员应做的一个工作,这里我不赘述。在 Mac 上,不错的 markdown 编辑器有 Sublime 3 、 Atom 、 MacDown 、 Mark Editor 、 Gitbook Editor 、 Mou 等,可挑选适合自己的,我选择的是 Atom ,是因为有一个刚性的需求它能够满足:
Git 作为版本管理的意义也不赘言。你肯定不想自己的工作区最后变成这样:
同样本小节也不是 Git 入门的命令集,这部分请自行查阅学习。当然我还是可以给出一些链接,虽然没有太多的意义~~我真的不知道为什么我还要给~~。基本的几个命令能熟悉就可以满足日常的需要了。另外, zsh 下的 Git 可以配一下 快捷键(alias) 和 自动补全,具体配置非常简单,可以来这篇文章Mac 优雅的工具集—— oh-my-zsh 篇看一下最终的效果。
Github 是~~世界上最大的同性交友平台~~目前最大~~我并无依据~~的代码托管平台,其生态圈之繁荣与力量令人震惊,几乎你需要的所有工具和资源都可以在上面找到。它与 Git 不是一个层级的概念~~git=github.substring(0, 3)
~~,这部分请自行搜索。在这个平台上,我可以给翻译项目一个README.md
文件,为阅读的人做简单的介绍以及引流,同时它可与 CI (持续集成)、 Gitbook 等工具完美集成,其内置的 issue 、 pull-request 功能还能与 Zenhub 结合,直接当成 trello 来进行项目管理之用。相关的工具下一节会介绍。
上文提到了翻译内容与样式的分离。实际上在这里样式这部分就是由 Gitbook 自带的渲染引擎来负责的。此外, Gitbook 还能帮助你管理整个书的目录结构、章节生成、搜索、词汇表、站点构建与生成等,同时它也有丰富的插件生态。这一切只需要你进行少量的配置(其实,就只是在书的根目录下运行gitbook init
命令,它会生成下图所示的一个目录结构),其低学习成本与高效益,使它成为了写书最易入门的趁手工具。
.
├── README.md
└── SUMMARY.md
项目管理方面的需求,主要是有时会有想法出现,希望增加什么特性,或者想到什么问题不想马上修,那么最好是有一个 TODOLIST 可以随时记录下来,并且能被方便地检索到。这方面的需求,怎么解决呢?
Github 内置的 issue 功能已经好用到爆,再加上专为 Github issue 功能定制的Zenhub简直就是活生生把 issue 当成 trello 来用。一些内置特性如下,稍加体验即可满足所有小型项目管理上的需求:
自动化什么的构建?这还要从本翻译稿的托管平台—— Gitbook ——说起。 gitbook 是一个绝好的写作平台,官方也通过 Webhooks 提供了与 Github 的集成,只要你把代码git push
到远端仓库, Gitbook 就会自动拉取仓库中的内容,依照特定的格式为书本构建站点。过大概 2 到 3 分钟,你就可以在Gitbook上看到自己最新的更新已经到书上了。一切看起来都十分美好,一键提交与部署。但是有一个最大的问题:Gitbook 在国内的速度不行,轻则卡顿,重则整站被墙刷不出页面。
这种情况下,我决定将整个翻译同时迁移到七牛上。七牛的 CDN 在国内速度名声在外,用来托管静态站点再好不过~~只需要拍两张身份证正反双面的照片上传等待审核即可~~。迁移也很简单,因为 gitbook 生成的静态站点其实就是_book
文件夹,只需要把这个文件夹下的全部东西放到七牛空间上去,在使用index.html
作为入口就可以了。但是,问题又来了:
git push
的反馈周期变长,从而使得我更不倾向于频繁提交,影响翻译体验解决方案也很简单:
git push
提交代码即可,站点从构建到最终上线,都有 CI 在负责想法出来了,在实施的时候还是走了一些弯路的。这些折腾过程在此无法深表,只有可能带过~~程序员们都懂的~~。在 CI 的选择上,我选择了 Jenkins 2.0 ,原因比较纯粹,最近项目上在使用 Jenkins 2.0 ,我刚好当是练手。至此文章成笔之时,我仍在考察其他选择,比如 travis-ci ,看起来 UI 和配置都简单许多,这部分可能是后文了。下面会简单介绍~~折腾~~搭建 Jenkins 2.0 作为本翻译项目 CI 的步骤和关键节点。
话说 Jenkins 不愧为 CI/CD 领域的先锋,这个产品每周都会发一个小版本(目前最新是 2.11 )。 Jenkins 2.0 的安装,可以通过直接下载安装包的方式下载,在 Mac 上也可以通过brew install jenkins
来下载安装。安装完成后,运行jenkins
即可在localhost:8080
启动一个本地的 jenkins 。
流程十分简单,免费用户可以有一个空间,有一定的流量限制,不过通常来说对于只有一个空间需求的用户来说,这样的流量应该不会超吧。由于我已经超过了一个空间的需求,同时我又有配置独立域名的需求,所以需要更多的权限。流程也很简单,进行实名认证、填写身份证、上传本人及身份证正反面照片各一张~~本人不需拍反面~~,最后保证账户里有最少 10 元即可。
pipeline 搭建起来了,接下来我们需要捋一捋前面说到的两个步骤:构建站点、上传站点文件到七牛空间。细分下来,主要是有以下的 task 要做:
我们想要构建站点,必然使用 gitbook 的命令行工具;要使用七牛的命令行,也必然引入相应的工具。这里我走过一些弯路,比如尝试将七牛工具以插件形式引入到 Jenkins job 中来,但是 2.0 以后,我们更倾向于使用脚本来描述构建工作,而非将 Jenkins 单纯当作一个转存 /转储的工作区间并为该 workspace 零散地写一些适配脚本。因此,在 pipeline 类型的项目就不存在每个 job 特定的配置空间,若想使用插件,配置起来有些麻烦。于是,最后我决定使用七牛的 npm 包(肯定是有的),并通过 NPM 来管理所有依赖。以下是一个package.json
文件需引入的依赖:
{
...
"devDependencies": {
"gitbook": "^3.1.1",
"gitbook-cli": "^2.3.0",
"qiniu": "^6.1.11"
},
...
}
另外,需要注意的是, Gitbook 的渲染引擎已经升级到 v3.1.1 版本,与 Legacy v2.6.7 版引擎相比,主要的区别是v3 支持多个部分(part)的文章、默认关闭了目录中章节前的数字等。其中 multipart 的部分有些小 bug但还可以忍受,但是默认关闭的目录数字则需要通过以下的配置给设置回来。在book.json
文件中:
{
"gitbook": ">=3.1.0",
"pluginsConfig": {
"theme-default": {
"showLevel": true
}
}
}
简单地运行命令gitbook build
即可生成站点目录。最后的构建脚本Jenkinsfile.groovy如下所示:
node ('main') {
stage 'Pull latest changes from SCM'
git([
url: '[email protected]:linesh-simplicity/translation-spring-mvc-4-documentation.git',
branch: 'master'
])
stage 'Download dependencies: Gitbook/Gitbook-cli/Qiniu'
sh 'npm install'
stage 'Build book serving directory through Gitbook'
sh 'gitbook build --gitbook=3.1.1'
stage 'Upload production _book to Qiniu through their API'
sh './jenkins/sync-book-to-qiniu.sh'
}
使用七牛的命令行工具进行文件上传,需要配置一些东西,主要是要同步的本地目录、七牛的 Access key 和 Secret Key 等,示例代码则可以从七牛 SDK 的官网上参考。我最后完成的这份同步脚本sync-book-to-qiniu.js如下所示,其中完成了 准备本地同步目录 、 排除不同步文件 、 指定覆盖上传策略等工作:
const qiniu = require("qiniu");
const glob = require('glob');
const crypto = require('crypto-js')
const ignoredFiles = [
'Jenkinsfile.groovy',
'sync-book-to-qiniu.js',
'sync-book-to-qiniu.sh',
'mvc-origin.md',
'package.json'
];
// node ./jenkins/sync-book-to-qiniu.js $ACCESS_KEY $SECRET_KEY
let qiniuAccessKey = process.argv.slice(2, 3);
let qiniuSecretKey = process.argv.slice(3);
// Prepare Qiniu configuration options
qiniu.conf.ACCESS_KEY = qiniuAccessKey.toString(crypto.enc.Utf8);
qiniu.conf.SECRET_KEY = qiniuSecretKey.toString(crypto.enc.Utf8);
bucket = 'mvc-linesh-tw';
glob.sync('_book/**/*.*', {}).filter(filename => {
for (let ignored of ignoredFiles) {
if (filename.endsWith(ignored)) return false;
}
return true;
}).forEach(filepath => {
const resource_key_in_qiniu_api = filepath.substring('_book/'.length, filepath.length);
// ':' means allow override upload. For further details refer to offical API docs
const policyToken = new qiniu.rs.PutPolicy(bucket + ":" + resource_key_in_qiniu_api).token();
uploadFile(policyToken, resource_key_in_qiniu_api, filepath)
})
function uploadFile(uptoken, key, localFile) {
let extra = new qiniu.io.PutExtra();
qiniu.io.putFile(uptoken, key, localFile, extra, function(error, response) {
if(!error) {
console.log('[Success] File uploaded: ' + response.key);
} else {
console.log(error);
}
});
}
同行们求 review 代码啊~
AK(Access Key)和 SK(Secret Key)是七牛分配给注册开发者的一对密钥,不能泄露,否则其他人得到了就可以对你的七牛空间进行任意操作。但是,你要把构建工作自动化,就必须这段脚本提交到 github 上,同时 CI 还要能从你的代码中读出正确的 AK 和 SK ,这要如何做到呢?回答是,通过 CI 提供的接口,由 pipeline 将参数注入到你的代码中。这样, AK 和 SK 就被保存在了 pipeline 上,别人无权对其进行访问。在 Jenkins CI 上,这是通过一个EnvInject 插件来做到的,在 travis-ci 中则更加简单,直接设置。
呼呼,最后看到这个图的时候还是很激动的,所有的部署工作都成功了。那么也是时候结束了,本篇文章总结起来,先是提到如何准备和搭建让翻译工作更加专注和高效的环境和工具,如 markdown/atom/gitbook/git/github 等,然后讲到如何使用 github 的 issue 和 zenhub 来辅助管理翻译项目中的待办事项和协作,最后一节讲述了如何使用 CI 工具将整个站点的构建和发布自动化,提高翻译和部署效率。任何有疑问或说错的地方,也请各位给我指出。
—— 2016-07-01