Hugo使用
hugo是一个静态网站生成器, 类似hexo
本文假设读者已经了解静态页面的含义, 也接触过Jekyll, octopress或hexo.
目录
简述
与hexo类似, hugo提供了命令行工具
hugo  # 编译生成静态文件
hugo help # 获取帮助信息
hugo new site # 初始化网站
hugo new post/*** # 创建文章
hugo server # 启动服务器
在万事俱备后, 添加新博文的组合拳基本上是
hugo new post/blog_name.md  # 新建博文到content/post/目录下
hugo server -D              # 启动服务器预览
hugo                        # 编译
然后将更新后的public目录上传到pages或服务器上
第一次配置
安装
在windows下, 可以借助choco
choco install hugo -confirm
在linux下,
go get -v github.com/gohugoio/hugo
初始化网站
假设博客目录是/data/blog
cd /data/
hugo new site blog
cd blog
以下操作默认都在/data/blog目录下
安装主题
装好hugo后, 必须安装主题, 才能开始编译
下方任选一个即可
git clone https://github.com/kakawait/hugo-tranquilpeak-theme.git themes/tranquilpeak
git clone https://github.com/vjeantet/hugo-theme-docdock.git themes/docdock
配置主题
以tranquilpeak为例, 拷贝参考配置文件到博客根目录下
cp themes/tranquilpeak/exampleSite/config.toml .
然后编辑该配置文件, 将博客名称, 作者等信息改成自己
如果配置文件里有themeDir配置, 记得删除
另外, 配置文件中theme这项要改为主题名称, 比如theme = "tranquilpeak" (其实是主题所在目录的名称, 如果你是在themes目录下直接执行git clone而没有指定存放目录的话, 就应该是hugo-tranquikpeak-theme)
测试配置效果
hugo new post/first.md
echo "## Content for Test" >> content/post/first.md
hugo server -D
打开浏览器localhost:1313 查看效果
编辑博文
hugo new post/第二篇博文啦.md
默认此命令生成的文件, 头部有3个metadata
- title
 - date
 - draft
 
对应的就是文章标题, 生成日期和是否草稿啦. 草稿文章只有hugo server -D指令才会编译和显示
可以编辑archetypes/default.md修改新文章模板
比如添加
categories: [],
tags: []
这样就更容易加标签和分类了
部署
编译生成的静态文件存放在public目录下, 有两种选择将其部署到网上
- 利用代码托管网站的pages服务
 - 将其放入web服务器资源文件目录下
 
利用pages服务对读者来说应该是轻车熟路了, 下面讲自行维护web服务的情况. 当然前提是你有一台自己的vps或服务器
计划是这样的
- 本地机器不参与编译环节, 也不维护public目录, 只维护content目录和一些配置文件, 比如config.toml, archtypes/default.md等
 - 本地编辑源文件后上传到服务器
 - 服务器自动编译, 将public拷贝到web服务器对应目录下, 或者hugo指定静态文件存放目录到web服务器对应目录下
 
上面提到的是常规做法, 实际上hugo集成了web服务器, 因此可以无需借助nginx或apache.
这里直接贴上守护程序的配置, 应该看得出做啥的吧
[program:hugo]
command=hugo server --baseURL=http://blogDomain --port=80 --bind=0.0.0.0  --disableLiveReload
directory=/data/blog
autostart=true
autorestart=true
user=root
log_stderr=true
redirect_stderr=true
stdout_logfile=/var/log/hugo.log
[program:hugo_draft]
command=hugo server -wD dev --baseURL=http://blogDomain_draft --port=8080 --bind=0.0.0.0
directory=/data/blog
autostart=true
autorestart=true
user=root
log_stderr=true
redirect_stderr=true
stdout_logfile=/var/log/hugo_draft.log
draft域名无需公网解析, 本地hosts即可
配合编辑器的sftp插件, 简直美滋滋
其实既然是本地编辑上传, 就无需维持一个draft状态和对应的服务器了. 只要上传动作是手动的, 那么默认上传的内容都是可以发表的即可.
使用nginx
当然, 开启一个hugo占用了80端口, 如果服务器还部署了其他服务, 就不能采用这种方式, 而是直接使用nginx代理public目录
比如nginx配置
server {
    server_name blog.grunmin.tech;
    listen 443 http2 ssl;
    ssl on; 
    ssl_certificate /path/to/cer;
    ssl_certificate_key /path/to/key;
    root /data/blog/public; 
}
那么, 要实现上传文件时实时更新public目录怎么办?
可以借助这个工具Watch来实现文件变更时自动编译
go get github.com/eaburns/watch
cd /data/blog
${GOPATH}/bin/watch -x public/ -t ${GOPATH}/bin/hugo
然后再将这条命令加入守护即可
从hexo中迁移
从hexo迁移到hugo有几点需要变更
- hexo的md文件使用yaml格式, hugo则是toml
 - hexo中date字段不带时区, hugo带时区
 - hugo中的隔断符严格要求是
<!--more-->, hexo中的隔段符则可能是<!-- more --> 
whatever, 下面附上迁移脚本, 改编自从 Hexo 迁移到 Hugo
// yaml_to_toml.js
const readline = require('readline');
const fs = require('fs');
const os = require('os');
const moment = require('moment-timezone');  // 需要通过 npm install moment-timezone 来安装
const timezone = 'Asia/Shanghai';  // 时区
const src = 'y';  // hexo .md 文件源目录
const target = 't';  // 目标文件目录
// 开始转换
readDir();
// 遍历目录
function readDir() {
    // read all files in src
    fs.readdir(src, function(err, files) {
        files.map((filename) => {
            // get the file extension
            const extension = filename.substr(filename.lastIndexOf('.', filename.length));
            if (extension === '.md') {
              readFile(`${filename}`);
            }
        });
    });
}
function readFile(filename) {
  fs.readFile(`${src}/${filename}`, { encoding: 'utf8' }, function(err, data) {
      if (err) {
          return console.log('err: ', err);
      }
      const content = data.split('---');
      let headContent = null;
      let bodyContent = null;
      if(content[0].length){
        headContent = content[0]
        bodyContent = content.slice(2).join('---')
      } else {
        headContent = content[1]
        bodyContent = content.slice(2).join('---')
      }
      const head = headContent.split('\n');
      // console.log('head: ', head);
      let newHead = head.map((item, index) => {
        // console.log('slpitHead: ', slpitHead(item, index, head));
        return slpitHead(item, index, head);
      });
      newHead = newHead.filter((item) => {return item;});
      const newBody = bodyContent.split('\n').map(item => replaceSummaryTag(item)).join(os.EOL)
      // console.log('newHead: ', newHead);
      const newContent = `---${os.EOL}${newHead.join(os.EOL)}${os.EOL}${os.EOL}---${os.EOL}${newBody}`;
      const newFileName = filename.split('-').slice(3).join('-')
      fs.writeFile(`${target}/${newFileName}`, newContent, {
          encoding: 'utf8'
      }, function(err) {
          if (err) {
            throw err;
          }
          console.log(`${newFileName}  生成成功!`);
      });
  });
}
function replaceSummaryTag(item){
  if (item.indexOf('<!-- more -->') === 0){
    return '<!--more-->'
  }
  return item
}
function slpitHead(item, index, head) {
  // title
  if (item.indexOf('title:') !== -1) {
    if(item.split('"').length === 3){
      return `title: ${item.split('title:')[1].trim()}`;
    }
    return `title: "${item.split('title:')[1].trim()}"`;
  }
  // date
  if (item.indexOf('date:') !== -1) {
    return `date: ${(moment.tz(item.split('date:')[1], timezone)).format()}`;
  }
  // tags
  if (item.indexOf('tags:') !== -1) {
    // console.log('tags...');
    const tags = [];
    if(item.length >5){
      const c = item.split(':')[1].split('').filter(o=>o !== ' ').join('');
      if(c){
        tags.push(item.split(':')[1].split('').filter(o=>o !== ' ').join(''))
      }
    }
    for (let i=index+1; i<head.length; i++) {
      if (head[i].indexOf('-') !== -1) {
        // console.log('head[i].split('-')[1]: ', head[i].split('-')[1]);
        tags.push(head[i].split('-')[1].trim());
      } else {
        break;
      }
    }
    // console.log('tags: ', tags);
    return `tags: ${JSON.stringify(tags)}`;
  }
  // categories
  if (item.indexOf('categories:') !== -1) {
    const categories = [];
    if(item.length >11){
      const c = item.split(':')[1].split('').filter(o=>o !== ' ').join('');
      if(c){
        categories.push(item.split(':')[1].split('').filter(o=>o !== ' ').join(''))
      }
    }
    for (let i=index+1; i<head.length; i++) {
      if (head[i].indexOf('-') !== -1) {
        categories.push(head[i].split('-')[1].trim());
      } else {
        break;
      }
    }
    // console.log('categories: ', categories);
    return `categories: ${JSON.stringify(categories)}`;
  }
  return false;
}
其他问题
备份
如果选择了pages托管, 那么只需记得将源码也上传一份即可, 如果是放在自己服务器, 可以跑一个定时任务, 如果当天有变更, 则通过git push到某个仓库进行备份
比如在博客目录下添加一个备份脚本backup.sh, 内容是
git add .
git commit -m "update in `date +'%Y-%m-%d %H:%M:%S'`"
git push origin master
然后添加一个计划任务
0 0 * * * cd /data/blog && sh backup.sh
不过, 如果是通过git的方式备份, 添加的主题仓库必须作为submodule, 如果不是则要先删除, 等初始化后重新下载
git submodule add https://github.com/kakawait/hugo-tranquilpeak-theme.git themes/tranquilpeak
语法高亮
语法高亮与主题的关系大一些, 以tranquilpeak主题为例. 该主题默认使用highlight.js做语法高亮. highlight.js脚本只包含一部分语法高亮的规则, 某些语言被独立出去了, 比如go, 就必须额外导入go.min.js文件, 完整文件路径像这样
https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/languages/go.min.js
有几种方式可以导入该文件, 第一是修改该主题下目录下的layout/partials/script.html文件, 第二是拷贝一份修改版到博客根目录下的layout目录. 不过tranquilpeak提供了一个配置参数, 可以很方便的导入该文件.
方法就是修改config.toml文件, 找到[[params.customJS]]标签, 在下方添加如下一行即可
customJS = ["https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.8.0/languages/go.min.js"]