使用Hugo+Github+Github Action构建个人技术博客,搭建技术博客的新方式!

前期准备

Hugo入门

本小结利用hugo快速创建一个可以在本地运行的博客工作区 ​

  1. 创建站点
1
$ hugo new site workspace
  1. 添加主题
1
2
$ cd workspace
$ git clone https://github.com/olOwOlo/hugo-theme-even themes/even
  1. 拷贝配置
1
2
# 当前目录为workspace
$ cp themes/even/exampleSite/config.toml .
  1. 添加文章
1
2
# 当前目录为workspace
$ hugo new post/my-first-post.md
  1. 修改文章
1
2
# 当前目录为workspace
$ vim content/post/my-first-post.md
1
2
3
4
5
6
---
title: "My First Post"
date: 2019-03-26T08:47:11+01:00
draft: true
---
This is my first post!
  1. 生成文件
1
2
# 当前目录为workspace
$ hugo server -D
  1. 查看站点
1
http://localhost:1313/

创建Github远程库

我们需要两个Github库,一个私有库及一个公有库。

远程工作库

如下图所示,创建一个私有Github库用于存放博客的所有资源(源文件、图片等)

创建远程工作库

注意

图中的库名并不重要,下文使用远程工作库指代该私有Github库。

远程博客库

其本质就是Github Pages库,库名格式必须符合<username>.github.io要求且必须为公有库

创建远程博客库

注意

库名很重要,务必把username替换为你的Github用户名。下文使用远程博客库指代该Github库。

整体流程

整体流程图

  1. 使用Git托管Hugo工作区,使其成为本地工作库
  2. 借助Git将本地工作库远程工作库关联。
  3. 利用Github Action实现博客自动化发布到远程博客库
    • 本地工作库推送到远程工作库触发Github Action工作流。
    • 在Github Action工作流中生成博客文章并部署到远程博客库

托管本地工作区

  1. 使用git将本地工作区变为本地工作库
1
2
$ cd workspace
$ git init
  1. 将主题重新以git子模块的方式添加
1
2
$ rm -rf themes/even
$ git submodule add https://github.com/olOwOlo/hugo-theme-even themes/even
  1. 创建本地工作库的main分支
1
2
$ git add .
$ git commit -m "init git workspace"

关联远程工作库

  1. 关联本地工作库与远程工作库
1
2
3
4
5
6
7
8
# 关联本地与远程工作库
$ git remote add origin https://github.com/longyue0521/workspace

# 设置本地库main分支追踪远程库main分支
$ git branch --set-upstream-to=origin/main main

# 查看远程工作库
$ git remote -v
  1. 同步本地工作库与远程工作库
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 将远程工作区内容拉到本地
$ git pull --rebase origin main

# 来自 https://github.com/longyue0521/workspace
# * branch            main       -> FETCH_HEAD
# 成功变基并更新 refs/heads/main

# 将本地工作库内容推到远程
$ git push

# 或者使用下面的命令推送并关联
$ git push --set-upstream origin main

自动化部署博客

Github Action

本地工作库添加Github Action工作流的方法很简单,只需要在本地工作库.github/workflows目录下添加格式为YAML的文件即可。

创建目录并添加配置文件

1
2
3
$ cd workspace
$ mkdir -p .github/workflows
$ vim .github/workflows/gh-pages.yml

gh-pages.yml添加如下内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# Workflow to build and deploy site to Github Pages using Hugo

# Name of Workflow
name: Github Pages Deploy

# Controls when the action will run. Triggers the workflow on main request
# events but only for the master branch
on:
  push:
    branches:
      - main

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "deploy"
  deploy:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:

    # Step 1 - Checks-out your repository under $GITHUB_WORKSPACE
    - name: Checkout
      uses: actions/checkout@v2
      with:
          submodules: true  # Fetch Hugo themes
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

    # Step 2 - Sets up the latest version of Hugo
    - name: Setup Hugo
      uses: peaceiris/actions-hugo@v2
      with:
          hugo-version: 'latest'
          extended: true

    # Step 3 - Clean and don't fail
    - name: Clean public directory
      run: rm -rf public

    # Step 4 - Builds the site using the latest version of Hugo
    - name: Builds site
      run: hugo --minify

    # Step 5 - Create README file
    - name: Create README file
      run: cp gojustfor.fun.md public/README.md

    # Step 6 - Push our generated site to Github Pages Repo
    - name: Deploy it!
      uses: peaceiris/actions-gh-pages@v3
      with:
        deploy_key: ${{ secrets.GP_ACTIONS_DEPLOY_KEY }}
        external_repository: longyue0521/longyue0521.github.io
        publish_branch: main
        publish_dir: ./public
        #cname: gojustfor.fun
  • 第4行,定义了工作流名。
  • 第8——11行,将本地工作库main分支推送到远程工作库时触发第14行定义的工作。
  • 第16行,定义了一个名为deploy的工作。
    • 该工作运行在ubuntu-latest
    • 第一步,检出远程工作库,即上面gh-pages.yml所在私有Github库。
    • 第二步,安装Hugo
    • 第三步,清理博客文件
    • 第四步,生成博客文件
    • 第五步,拷贝README,可选
    • 第六步,将博客文件部署到远程博客库

跨库部署博客

跨库部署的关键步骤定义在gh-pages.yml中,详解如下:

1
2
3
4
5
6
7
8
# Step 6 - Push our generated site to Github Pages Repo
- name: Deploy it!
      uses: peaceiris/actions-gh-pages@v3
      with:
        deploy_key: ${{ secrets.GP_ACTIONS_DEPLOY_KEY }}
        external_repository: longyue0521/longyue0521.github.io
        publish_branch: main
        publish_dir: ./public
  • 这是一个名为Deploy it!的步骤
  • 使用另一个Github Action工作流peaceiris/actions-gh-pages@v3
  • deploy_key当前库publish_dir目录下的内容部署到external_repository库的publish_branch分支。

由此可见deploy_key是跨库部署的关键,这与用git通过SSH Key推送代码到Github库相类似。

  1. 本地生成密钥对
1
2
3
4
$ ssh-keygen -t rsa -b 4096 -C "$(git config user.email)" -f gh-pages -N ""
# You will get 2 files:
#   gh-pages.pub (public key)
#   gh-pages     (private key)

得到公钥gh-pages.pub和私钥gh-pages。 ​

  1. 配置远程工作库

如下图所示,将gh-pages私钥内容添加到远程工作库,添加路径为Settings > Secrets > New secret

用私钥配置工作库

注意

密钥名应为GP_ACTIONS_DEPLOY_KEY与上文的${{ secrets.GP_ACTIONS_DEPLOY_KEY }}对应

  1. 配置远程博客库

如下图所示,将gh-pages.pub公钥内容添加到远程博客库,添加路径为Settings > Deploy keys > Add deploy key

用公钥配置博客库

注意

注意添加路径,并勾选 Allow write access

配置博客

整体配置

下面以Even主题为例

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
baseURL = "https://longyue0521.github.io"
languageCode = "zh-cn"
defaultContentLanguage = "zh-cn"                             # en / zh-cn / ... (This field determines which i18n file to use)
title = "Talent Is Enduring Patience"
preserveTaxonomyNames = true
enableRobotsTXT = true
enableEmoji = true
theme = "even"
enableGitInfo = false # use git commit log to generate lastmod record # 可根据 Git 中的提交生成最近更新记录。

# Syntax highlighting by Chroma. NOTE: Don't enable `highlightInClient` and `chroma` at the same time!
pygmentsOptions = "linenos=table"
pygmentsCodefences = true
pygmentsUseClasses = true
pygmentsCodefencesGuessSyntax = true

hasCJKLanguage = true     # has chinese/japanese/korean ? # 自动检测是否包含 中文\日文\韩文
paginate = 5                                              # 首页每页显示的文章数
disqusShortname = "" # "outofmemory-me"      # disqus_shortname
googleAnalytics = ""      # UA-XXXXXXXX-X
copyright = ""            # default: author.name ↓        # 默认为下面配置的author.name ↓

[author]                  # essential                     # 必需
  name = "龍躍"

[sitemap]                 # essential                     # 必需
  changefreq = "weekly"
  priority = 0.5
  filename = "sitemap.xml"

[[menu.main]]             # config your menu              # 配置目录
  name = "首页" #"Home"
  weight = 10
  identifier = "home"
  url = "/"
[[menu.main]]
  name = "归档" #"Archives"
  weight = 40
  identifier = "archives"
  url = "/post/"
[[menu.main]]
  name = "标签" #"Tags"
  weight = 30
  identifier = "tags"
  url = "/tags/"
[[menu.main]]
  name = "分类" #"Categories"
  weight = 20
  identifier = "categories"
  url = "/categories/"

[params]
  version = "4.x"           # Used to give a friendly message when you have an incompatible update
  debug = false             # If true, load `eruda.min.js`. See https://github.com/liriliri/eruda

  since = "2017"            # Site creation time          # 站点建立时间
  # use public git repo url to link lastmod git commit, enableGitInfo should be true.
  # 指定 git 仓库地址,可以生成指向最近更新的 git commit 的链接,需要将 enableGitInfo 设置成 true.
  gitRepo = ""

  # site info (optional)                                  # 站点信息(可选,不需要的可以直接注释掉)
  logoTitle = "Go! Just for fun!"        # default: the title value    # 默认值: 上面设置的title值
  keywords = ["Go", "K8s","Docker"]
  description = "Talent Is Enduring Patience | Just For Fun"

  # paginate of archives, tags and categories             # 归档、标签、分类每页显示的文章数目,建议修改为一个较大的值
  archivePaginate = 50

  # show 'xx Posts In Total' in archive page ?            # 是否在归档页显示文章的总数
  showArchiveCount = true

  # The date format to use; for a list of valid formats, see https://gohugo.io/functions/format/
  dateFormatToUse = "2006-01-02"

  # show word count and read time ?                       # 是否显示字数统计与阅读时间
  moreMeta = true

  # Syntax highlighting by highlight.js
  highlightInClient = false

  # 一些全局开关,你也可以在每一篇内容的 front matter 中针对单篇内容关闭或开启某些功能,在 archetypes/default.md 查看更多信息。
  # Some global options, you can also close or open something in front matter for a single post, see more information from `archetypes/default.md`.
  toc = true                                                                            # 是否开启目录
  autoCollapseToc = false   # Auto expand and collapse toc                              # 目录自动展开/折叠
  fancybox = true           # see https://github.com/fancyapps/fancybox                 # 是否启用fancybox(图片可点击)

  # mathjax
  mathjax = false           # see https://www.mathjax.org/                              # 是否使用mathjax(数学公式)
  mathjaxEnableSingleDollar = false                                                     # 是否使用 $...$ 即可進行inline latex渲染
  mathjaxEnableAutoNumber = false                                                       # 是否使用公式自动编号
  mathjaxUseLocalFiles = false  # You should install mathjax in `your-site/static/lib/mathjax`

  postMetaInFooter = true   # contain author, lastMod, markdown link, license           # 包含作者,上次修改时间,markdown链接,许可信息
  linkToMarkDown = true    # Only effective when hugo will output .md files.           # 链接到markdown原始文件(仅当允许hugo生成markdown文件时有效)
  contentCopyright = '<a rel="license noopener" title="国际许可(CC BY-NC-SA 4.0)"href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">"署名-非商业性使用-相同方式共享 4.0 国际"</a> 转载请保留作者及原文链接'     # e.g. '<a rel="license noopener" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank">CC BY-NC-ND 4.0</a>'

  changyanAppid = ""        # Changyan app id             # 畅言
  changyanAppkey = ""       # Changyan app key

  livereUID = ""            # LiveRe UID                  # 来必力

  baiduPush = false        # baidu push                  # 百度
  baiduAnalytics = ""      # Baidu Analytics
  baiduVerification = ""   # Baidu Verification
  googleVerification = ""  # Google Verification         # 谷歌

  # Link custom CSS and JS assets
  #   (relative to /static/css and /static/js respectively)
  customCSS = []
  customJS = []

  uglyURLs = false          # please keep same with uglyurls setting

  [params.publicCDN]        # load these files from public cdn                          # 启用公共CDN,需自行定义
    enable = true
    jquery = '<script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>'
    slideout = '<script src="https://cdn.jsdelivr.net/npm/slideout@1.0.1/dist/slideout.min.js" integrity="sha256-t+zJ/g8/KXIJMjSVQdnibt4dlaDxc9zXr/9oNPeWqdg=" crossorigin="anonymous"></script>'
    fancyboxJS = '<script src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.js" integrity="sha256-XVLffZaxoWfGUEbdzuLi7pwaUJv1cecsQJQqGLe7axY=" crossorigin="anonymous"></script>'
    fancyboxCSS = '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.1.20/dist/jquery.fancybox.min.css" integrity="sha256-7TyXnr2YU040zfSP+rEcz29ggW4j56/ujTPwjMzyqFY=" crossorigin="anonymous">'
    timeagoJS = '<script src="https://cdn.jsdelivr.net/npm/timeago.js@3.0.2/dist/timeago.min.js" integrity="sha256-jwCP0NAdCBloaIWTWHmW4i3snUNMHUNO+jr9rYd2iOI=" crossorigin="anonymous"></script>'
    timeagoLocalesJS = '<script src="https://cdn.jsdelivr.net/npm/timeago.js@3.0.2/dist/timeago.locales.min.js" integrity="sha256-ZwofwC1Lf/faQCzN7nZtfijVV6hSwxjQMwXL4gn9qU8=" crossorigin="anonymous"></script>'
    flowchartDiagramsJS = '<script src="https://cdn.jsdelivr.net/npm/raphael@2.2.7/raphael.min.js" integrity="sha256-67By+NpOtm9ka1R6xpUefeGOY8kWWHHRAKlvaTJ7ONI=" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/flowchart.js@1.8.0/release/flowchart.min.js" integrity="sha256-zNGWjubXoY6rb5MnmpBNefO0RgoVYfle9p0tvOQM+6k=" crossorigin="anonymous"></script>'
    sequenceDiagramsCSS = '<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/bramp/js-sequence-diagrams@2.0.1/dist/sequence-diagram-min.css" integrity="sha384-6QbLKJMz5dS3adWSeINZe74uSydBGFbnzaAYmp+tKyq60S7H2p6V7g1TysM5lAaF" crossorigin="anonymous">'
    sequenceDiagramsJS = '<script src="https://cdn.jsdelivr.net/npm/webfontloader@1.6.28/webfontloader.js" integrity="sha256-4O4pS1SH31ZqrSO2A/2QJTVjTPqVe+jnYgOWUVr7EEc=" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/snapsvg@0.5.1/dist/snap.svg-min.js" integrity="sha256-oI+elz+sIm+jpn8F/qEspKoKveTc5uKeFHNNVexe6d8=" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/underscore@1.8.3/underscore-min.js" integrity="sha256-obZACiHd7gkOk9iIL/pimWMTJ4W/pBsKu+oZnSeBIek=" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/gh/bramp/js-sequence-diagrams@2.0.1/dist/sequence-diagram-min.js" integrity="sha384-8748Vn52gHJYJI0XEuPB2QlPVNUkJlJn9tHqKec6J3q2r9l8fvRxrgn/E5ZHV0sP" crossorigin="anonymous"></script>'

  # Display a message at the beginning of an article to warn the readers that it's content may be outdated.
  # 在文章开头显示提示信息,提醒读者文章内容可能过时。
  [params.outdatedInfoWarning]
    enable = false
    hint = 30               # Display hint if the last modified time is more than these days ago.    # 如果文章最后更新于这天数之前,显示提醒
    warn = 180              # Display warning if the last modified time is more than these days ago.    # 如果文章最后更新于这天数之前,显示警告

  [params.gitment]          # Gitment is a comment system based on GitHub issues. see https://github.com/imsun/gitment
    owner = ""              # Your GitHub ID
    repo = ""               # The repo to store comments
    clientId = ""           # Your client ID
    clientSecret = ""       # Your client secret

  [params.utterances]       # https://utteranc.es/
    owner = "longyue0521"              # Your GitHub ID
    repo = "longyue0521.github.io"               # The repo to store comments

  [params.gitalk]           # Gitalk is a comment system based on GitHub issues. see https://github.com/gitalk/gitalk
    owner = ""              # Your GitHub ID
    repo = ""               # The repo to store comments
    clientId = ""           # Your client ID
    clientSecret = ""       # Your client secret

  # Valine.
  # You can get your appid and appkey from https://leancloud.cn
  # more info please open https://valine.js.org
  [params.valine]
    enable = false
    appId = '你的appId'
    appKey = '你的appKey'
    notify = false  # mail notifier , https://github.com/xCss/Valine/wiki
    verify = false # Verification code
    avatar = 'mm'
    placeholder = '说点什么吧...'
    visitor = false

  [params.flowchartDiagrams]# see https://blog.olowolo.com/example-site/post/js-flowchart-diagrams/
    enable = false
    options = ""

  [params.sequenceDiagrams] # see https://blog.olowolo.com/example-site/post/js-sequence-diagrams/
    enable = false
    options = ""            # default: "{theme: 'simple'}"

  [params.busuanzi]         # count web traffic by busuanzi                             # 是否使用不蒜子统计站点访问量
    enable = false
    siteUV = true
    sitePV = true
    pagePV = true

  [params.reward]                                         # 文章打赏
    enable = false
    wechat = "/path/to/your/wechat-qr-code.png"           # 微信二维码
    alipay = "/path/to/your/alipay-qr-code.png"           # 支付宝二维码

  [params.social]                                         # 社交链接
    a-email = "mailto:longyueli0521@gmail.com"
    #b-stack-overflow = "http://localhost:1313"
    #c-twitter = "http://localhost:1313"
    #d-facebook = "http://localhost:1313"
    #e-linkedin = "http://localhost:1313"
    #f-google = "http://localhost:1313"
    g-github = "http://github.com/longyue0521"
    h-weibo = "https://weibo.com/longyue521"
    #i-zhihu = "http://localhost:1313"
    #j-douban = "http://localhost:1313"
    #k-pocket = "http://localhost:1313"
    #l-tumblr = "http://localhost:1313"
    #m-instagram = "http://localhost:1313"
    #n-gitlab = "http://localhost:1313"
    o-bilibili = "https://space.bilibili.com/86465982"

# See https://gohugo.io/about/hugo-and-gdpr/
[privacy]
  [privacy.googleAnalytics]
    anonymizeIP = true      # 12.214.31.144 -> 12.214.31.0
  [privacy.youtube]
    privacyEnhanced = true

# see https://gohugo.io/getting-started/configuration-markup
[markup]
  [markup.tableOfContents]
    startLevel = 1
  [markup.goldmark.renderer]
    unsafe = true

# 将下面这段配置取消注释可以使 hugo 生成 .md 文件
# Uncomment these options to make hugo output .md files.
#[mediaTypes]
#  [mediaTypes."text/plain"]
#    suffixes = ["md"]
#
#[outputFormats.MarkDown]
#  mediaType = "text/plain"
#  isPlainText = true
#  isHTML = false
#
#[outputs]
#  home = ["HTML", "RSS"]
#  page = ["HTML", "MarkDown"]
#  section = ["HTML", "RSS"]
#  taxonomy = ["HTML", "RSS"]
#  taxonomyTerm = ["HTML"]

修改配色

修改Even主题配色需要修改even/assets/sass/_variables.scss文件中相关变量的值。

在Github Action工作流配置文件.github/workflows/gh-pages.yml中添如下内容使修改配色自动化。

1
2
3
4
5
6
7
8
9
# Step 4 - Change theme/even's color
- name: Change color of even theme to Mint Green
  run: sed -i "0,/'Default'/{s/'Default'/'Mint Green'/}" ./themes/even/assets/sass/_variables.scss
      
- name: Change color of code background to Mint Green
  run: sed -i "0,/f5f5f5/{s/f5f5f5/eaf5e56c/}" ./themes/even/assets/sass/_variables.scss
    
- name: Change summary of all post to Mint Green
  run: sed -i "0,/danger/{s/danger/success/}" ./content/post/*.md

添加评论

定制域名

修改工作流文件

修改Github Action工作流配置文件.github/workflow/gh-pages.yml添加CNAME文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Step 6 - Push our generated site to Github Pages Repo
- name: Deploy it!
      uses: peaceiris/actions-gh-pages@v3
      with:
        deploy_key: ${{ secrets.GP_ACTIONS_DEPLOY_KEY }}
        external_repository: longyue0521/longyue0521.github.io
        publish_branch: main
        publish_dir: ./public
        # 添加CNAME域名 
        cname: gojustfor.fun

阿里云域名解析

  1. 使用ping命令,查看<username>.github.io的IP地址

  2. 在阿里云的云解析DNS/域名解析页面添加域名

添加域名

  1. 为域名添加解析记录,两条记录如下图所示

添加域名解析记录

设置远程博客库

如下图所示,在远程博客库Settings > Pages页面设置自定义域名安全协议

设置远程博客库域名

Reference