这篇文章接上篇 基于 Git 搭建代码托管服务器,Git 服务器也可以托管电子书。新建一个 git 项目,在根目录下为不同的电子书创建子目录,在子目录下按照 gitbook 模板编写 markdown,然后通过 git push 推送到 git server,触发服务器 hooks 脚本执行,脚本会调用 gitbook 程序生成 html 格式电子书。服务器上启动一个静态 HTTP Server,就可以把电子书发布出去了。如果要更新电子书内容,只需要修改相应的 markdown 内容,然后 push 到服务器,电子书就会自动更新。

有了 Git + Gitbook + Markdown 编辑器后就可以开始写博客了。这种方式和 github pages 是一样的,但部署速度更快,且可以定制 git hooks 脚本,还可以利用服务器渲染动态页面。github pages 虽是免费的,但只能部署静态的内容。

首先创建一个新的仓库,名为 mybook,登录服务器

$ cd /home/git
$ sudo mkdir mybook.git && cd mybook.git
$ sudo git init --bare
Initialized empty Git repository in /home/git/mybook.git/

修改仓库用户归属,确保后面可以 git 用户有权限推送代码

sudo chown -R git:git /home/git/mybook.git

在自己的电脑上克隆仓库

git clone git@huoyijie.cn:/home/git/mybook.git
正克隆到 'mybook'...
warning: 您似乎克隆了一个空仓库。

打开 vscode,打开 mybook 文件夹,目前是空项目。添加框架代码后来看一下项目结构。

$ tree -L 2
.
├── bin
│   └── www
├── mybook.json
├── mygitbook
│   ├── book.json
│   ├── README.md
│   └── SUMMARY.md
├── node_modules
│   ├── http-server
│   ├── shelljs
├── package.json
├── package-lock.json
├── public
│   ├── gitbook
│   └── index.html
└── scripts
    └── deploy.js

从上到下依次介绍下主要目录和文件

  • bin/wwwhttp server 启动脚本,看下内容
#!/bin/bash

DIR="public"
if [ ! -d "$DIR" ]; then
  mkdir $DIR
  echo "Home Page" > $DIR/index.html
fi

./node_modules/http-server/bin/http-server $DIR -a 127.0.0.1 -p 8001

静态文件存储目录为 $DIR=public ,监听 8001 端口。

  • mybook.json 是自定义的关于当前仓库中 book 的配置文件,来看下内容
{
    "deployBook": ["mygitbook"],
    "dropBook": []
}

每个 book 都有唯一的字符串 name,如当前这篇博客文章的名字为 mygitbook。这个配置文件目前还很简单,只定义了 2 个配置项。

  1. deployBook: 需要发布到 http server 的 book name列表
  2. dropBook: 需用从 http server 中删除的 book name列表

也就是说,新增的 book 要在 deployBook 配置一下才会真正发布到 http server,需要下线的 book 要在 dropBook 配置一下。修改完配置推送变更到服务器就会自动部署更新。

  • mygitbook 目录里是当前文章真正的内容,可以通过 gitbook init 初始化。为了更方便的管理,我创建了 gitbookgen.js 脚本,可以快速生成电子书模板。

  • node_modules 为当前项目依赖的 npm 包,主要是 http server 和 shelljs,后者可以方便调用系统 shell 命令。

  • package.json 为 Node.js 项目配置文件

{
  "name": "mybook",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "bin/www"
  },
  "dependencies": {
    "http-server": "^0.12.3",
    "shelljs": "^0.8.4"
  }
}
  • public 目录为 http server 静态内容存储的目录,后面会把电子书部署在这个目录下

  • scripts/deploy.js 目录为部署 book 的脚本文件,会被 git server 上面的 hooks 脚本调用

#!/usr/bin/env node

// run from project root dir with command './scripts/deploy.js'
const shell = require('shelljs')
const fs = require('fs')

console.log(`pwd: ${shell.pwd()}`)

if(shell.exec('git pull').code) {
  console.error('!> deploy error -> git pull failed')
  process.exit(1)
}

const publicDir = 'public'
const mybookJson = JSON.parse(fs.readFileSync('mybook.json'))
console.log(`> load mybook.json: `, mybookJson)

mybookJson.dropBook.forEach(book => {
  console.log(`> drop book: ${book}`)

  var tmp = book.split('|')
  book = tmp[0]
  var bookid = tmp[1] || ''

  if (!!bookid) {
    const bookDir = `${publicDir}/${bookid}`
    shell.rm('-rf', bookDir)
    shell.rm('-rf', book)
  } else {
    console.error(`!> delete book failed ->lack of param bookid.`)
    process.exit(1)
  }
})

mybookJson.deployBook.forEach(book => {
  console.log(`> deploy book: ${book}`)

  if (!fs.existsSync(book)) {
    console.error(`!> deploy error with ${book} -> not exist`)
    process.exit(1)
  } else {

    shell.cd(book)

    const bookJson = JSON.parse(fs.readFileSync(`book.json`))
    console.log(`> load book.json:`, bookJson)
    const bookDir = `${publicDir}/${bookJson.id}`

    if(!shell.exec('gitbook build').code) {
      shell.cd('-')

      if (!fs.existsSync(bookDir)) {
        shell.mkdir(bookDir)
      } else {
        shell.rm('-rf', `${bookDir}/*`)
      }

      shell.cp('-r', `${book}/_book/*`, bookDir)
      console.info(`> deploy succeed with ${book}`)
    } else {
      console.error(`!> deploy error with ${book}`)
      process.exit(1)
    }

  }

})

process.exit(0)

这个脚本主要做的事情就是等变更推送到服务器后,把配置在 deployBook 下的 book 发布出去,期间调用了 gitbook build 把 markdown 内容转为 html。把配置在 dropBook 下的 book 下线掉。这个脚本是被 git server 上的 post-receive hooks 调用的。post-receive 是在 git server 接受 push 后自动触发调用的。所以这个脚本是完成推送变更,自动部署更新 book 的关键。

下面来看一下存放在 git server 上的 post-receive hooks。文件存放路径为

$ ls -l /home/git/mybook.git/hooks/post-receive 
-rwxr-xr-- 1 git git 103 Mar 23 15:59 /home/git/mybook.git/hooks/post-receive

这个脚本的内容非常简单

#!/bin/sh

cd /home/git/vswork/mybook

unset GIT_DIR

./scripts/deploy.js

exec git-update-server-info

主要逻辑就是调用了 ./scripts/deploy.js 部署脚本,其中 /home/git/vswork/mybook 是通过下面的命令克隆的,也是服务器上对应的博客工作目录。

$ cd /home/git/vswork
$ sudo git clone /home/git/mybook.git
$ cd mybook
$ npm install

安装好依赖后,修改下用户归属

sudo chown -R git:git mybook

到此为止,项目基本已经部署配置完成。接下来就是打开 vscode,编辑 mygitbook 电子书。编辑完成后 push 到服务器上完成文章自动部署更新。下面是 push 代码后的输出,可以看到 scripts/deploy.js 脚本在服务器上执行后的输出,有在自动部署 mygitbook。

$ git push
对象计数中: 4, 完成.
Delta compression using up to 8 threads.
压缩对象中: 100% (4/4), 完成.
写入对象中: 100% (4/4), 1.94 KiB | 1.94 MiB/s, 完成.
Total 4 (delta 2), reused 0 (delta 0)
remote: pwd: /home/git/vswork/mybook
remote: From /home/git/mybook
remote:    4fe0a5e..6e62e3d  master     -> origin/master
remote: Updating 4fe0a5e..6e62e3d
remote: Fast-forward
remote:  mygitbook/README.md | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++--
remote:  1 file changed, 104 insertions(+), 4 deletions(-)
remote: > load mybook.json:  {
remote:   '// deployBook': "['firstbook','secondbook']",
remote:   deployBook: [ 'mygitbook' ],
remote:   '// dropBook': "['firstbook|f44b5ae08bb611ebbf5b4f5ee88ae545','secondbook|f44b5ae08bb611ebbf5b4f5ee88ae545']",
remote:   dropBook: []
remote: }
remote: > deploy book: mygitbook
remote: > load book.json: {
remote:   id: 'c0a4a7508bc011eb952e418fdf675f27',
remote:   name: 'mygitbook',
remote:   title: '使用 Git 与 Gitbook 创建管理电子书',
remote:   author: 'huoyijie',
remote:   description: '使用 Git 与 Gitbook 创建管理电子书',
remote:   introduce: '这里是内容介绍...',
remote:   coverImg: 'https://cdn.huoyijie.cn/ab/c0a4a7508bc011eb952e418fdf675f27/mygitbook-cover.jpg',
remote:   language: 'zh-hans',
remote:   plugins: [ '-lunr', '-search', '-sharing', '-fontsettings' ],
remote:   links: { sidebar: { '首页': 'https://huoyijie.cn/?from=article_mygitbook' } }
remote: }
remote: info: 7 plugins are installed 
remote: info: 2 explicitly listed 
remote: info: loading plugin "highlight"... OK 
remote: info: loading plugin "theme-default"... OK 
remote: info: found 1 pages 
remote: info: found 0 asset files 
remote: info: >> generation finished with success in 0.4s ! 
remote: > deploy succeed with mygitbook
To huoyijie.cn:/home/git/mybook.git
   4fe0a5e..6e62e3d  master -> master

OK,关于如何使用 Git 与 Gitbook 创建管理电子书的主要内容介绍完了。