建站

sky

简略步骤

Hugo Quick Start

  1. 在 wsl 下装 Hugo
1
$ sudo apt install hugo
  1. TL;DR 一下 Hugo 的基础命令
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ tldr hugo
hugo
Template-based static site generator. Uses modules, components, and themes.More information: https://gohugo.io.

 - Create a new Hugo site:
   hugo new site {{path/to/site}}

 - Create a new Hugo theme (themes may also be downloaded from https://themes.gohugo.io/):
   hugo new theme {{theme_name}}

 - Create a new page:
   hugo new {{section_name}}/{{filename}}

 - Build a site to the ./public/ directory:
   hugo

 - Build a site including pages that are marked as a "draft":
   hugo --buildDrafts

 - Build a site to a given directory:
   hugo --destination {{path/to/destination}}

 - Build a site, start up a webserver to serve it, and automatically reload when pages are edited:
   hugo server
  1. 新建站点
 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
$ hugo new site loomt
Congratulations! Your new Hugo site is created in /home/loomt/temp/loomt.
Just a few more steps and you are ready to go:

1. Download a theme into the same-named folder.
 Choose a theme from https://themes.gohugo.io/ or
 create your own with the "hugo new theme <THEMENAME>" command.

2. Perhaps you want to add some content. You can add single files
 with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>".

3. Start the built-in live server via "hugo server".

Visit https://gohugo.io/ for quickstart guide and full documentation.

$ cd loomt
$ tree
.
├── archetypes
│   └── default.md
├── config.toml
├── content
├── data
├── layouts
├── static
└── themes
  1. 配置主题
$ git init
# 将皮肤作为submodule添加,以便更新
$ git submodule add https://github.com/reuixiy/hugo-theme-meme.git themes/meme # MeME,很喜欢的一个主题
$ rm config.toml && cp themes/meme/config-examples/zh-cn/config.toml config.toml #覆盖配置文件
  1. 发布文章
1
2
$ hugo new posts/HelloWorld.md #会在centent下面创建markdown文件,可以直接去编辑
Content "/home/loomt/temp/loomt/content/posts/HelloWorld.md" created
  1. 运行本地服务
1
$ hugo server -D --verbose # 在本地运行 -D是渲染草稿 --verbose显示详细输出

目前已经完成基本配置,可以访问运行在本机的站点了 🥳

接下来还可以将网站部署到 Github Page,Vercel 或者 Netlify 等免费的静态资源托管商。

Hugo 部署到 Github Page

Hugo 是一个网站构建工具,hugo命令生成的 public 文件夹存放的是静态的部署页面,我们只需要将其放在 Github Page 中即可。建议开两个仓库,一个仓库用于存放根目录,另一个用于存放./public 文件夹的内容,以便被 Github Page 部署。

因为根目录可能有敏感信息和暂时不希望公开的草稿,又为了让其得到有效的版本控制,可以开一个私有仓库存放根目录,

而 publishDir(./pubic) 作为输出的静态页面,则适合放在公开的仓库 而且 Github Page 不公开没法白嫖

默认看到这里的同学已经建好了仓库,并完成了仓库的初始化和配置 🤗

下面假设更新了文章,需要同步到两个仓库。

1
2
3
4
5
6
7
8
$ hugo --gc --cleanDestinationDir # 生成静态站点到./public的同时 清除缓存和静态站点用不着的文件
$ git add .
$ git commit -m "update source code"
$ git push
$ cd public
$ git add .
$ git commit -m "update Github Page"
$ git push

是不是感觉要 push 两次非常麻烦,可以写一个 push.sh 来简化操作,还可以加个 Github Actions,简化每次更新站点的步骤,具体可阅读 GitHub Actions 官方文档actions-gh-pages 以及 reuixiy 的博客

自动化部署

  • 如果源码仓库和 Github Page 仓库都是公有的话可以阅读 actions-gh-pages 进行简单的配置。

  • 如果源码仓库是私有的,Github Page 仓库是公开的话,可以参考以下配置方案。

  1. 配置公钥和私钥到仓库

需要生成 SSH key pair 以获取源码仓库对 Github Page 仓库修改的权限。

$ mkdir -p ~/.ssh/blog
$ cd ~/.ssh/blog
$ ssh-keygen -t rsa -b 4096 -C "yourname@users.noreply.github.com"
# 注意:不要无脑回车,最好开一个文件夹存公钥私钥,不然会覆盖掉以前的
  • id_rsa(私钥)

前往 Github Page 仓库,Settings > Deploy Keys > Add deploy key。

需要勾选 Allow write access。

  • id_rsa.pub(公钥)

前往源码仓库,Settings > Secrets > Actions > New repository secret。

Name 需要设为 ACTIONS_DEPLOY_KEY

  1. 新建 Workflow 配置文件

下面粘一下我的配置方案

 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
# .github/workflows/build.yml
name: Hugo automated deployment


on:
push:
  branches:
    - main  # Set a branch name to trigger deployment


jobs:
deploy:
  runs-on: ubuntu-latest
  permissions:
    contents: write
  concurrency:
    group: ${{ github.workflow }}-${{ github.ref }}
  steps:
    - uses: actions/checkout@v3
      with:
        submodules: true  # Fetch Hugo themes (true OR recursive)
        fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod


    - name: Setup Hugo
      uses: peaceiris/actions-hugo@v2
      with:
        hugo-version: '0.92.2'
        extended: true


    - name: Build
      run: hugo --gc --minify


    # - name: Deploy
    #   uses: peaceiris/actions-gh-pages@v3
    #   # If you're changing the branch from main,
    #   # also change the `main` in `refs/heads/main`
    #   # below accordingly.
    #   if: ${{ github.ref == 'refs/heads/main' }}
    #   with:
    #     github_token: ${{ secrets.GITHUB_TOKEN }}
    #     publish_dir: ./public
    - name: Deploy  # 此处需要按照自己实际修改
      uses: peaceiris/actions-gh-pages@v3
      with:
        deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
        external_repository: loomts/loomts.github.io
        publish_branch: main  # default: gh-pages
        publish_dir: ./public
  1. 推送到 Github
1
2
3
$ git add .
$ git commit -m "setup auto deploy"
$ git push

打开你的源码仓库页面,点击 Actions 查看日志,顺利的话已经搞定了,以后每次 git push Github Workflow 都会自动帮你更新网站了。

Github Page 绑定自定义域名

假设已经有了域名,还需要在域名的 DNS 服务商那里加一个 CNAME(用于 dns 跳转)。

name type value
www CNAME loomts.github.io

一段时间后,回到 Github Page 仓库,在 Settings > Pages > Custom domain 处填上自己的域名,等待几小时生成证书,然后勾选  Enforce HTTPS

还要记得添加 your domain到 static/CNAME,以生成到静态文件。。

1
$ echo "your domain" > static/CNAME

Custom domains are stored in a CNAME file in the root of your publishing source. You can add or update this file through your repository settings or manually. For more information, see " Managing a custom domain for your GitHub Pages site ." ——Github Docs

Hugo 部署到 Vercel

Vercel 可以看作是结合了 Github Page 还有 Github Actions 的用于前端框架和静态站点管的平台,直接导入 Github 仓库即可部署,感觉配置起来比 Github Action 无脑很多,很适合我,所以我已经放弃 Github Action,全面转为 Vercel 了。需要注意的是注册域名的时候要去域名注册商改一下 DNS 服务器,或者每个站点都添加一次 A 记录。

Hugo 个性化配置

皮肤配置

可以根据皮肤作者的文档改变皮肤的各种 feature,如 MeME 的 config.toml example

P.S. config.toml 的 baseURL 要加 https:// ,否则生成的静态页面 css 和 js 加载会出问题。

自定义 CSS 和 JS

  1. 利用 hugo 的替换规则

当你需要更改某个页面的生成规则(包括 CSS 和 JS),你可以将 themes/your-theme 里面的东西复制一份到你的根目录,然后爽改逻辑,hugo 生成静态页面时在同等情况下会优先用你根目录下的文件。

  1. 如果你想要自己新建一个 js 或者 css 文件,可以看hugo-pipes,但如果你不打算做一个开源主题,无脑在 html 文件里面堆 style 和 script 是能跑的选择!

比如,我有这样一个需求:在 Wiki 页面分层次显示我的笔记,但又不让笔记内容在主页面显示,并且 Wiki 页需要按照文件夹的内部结构展示,那就用categories的方式组织 content/wiki 的内容,仅需要改一下 MeME 主题 tree-sections 的内容,并配置一下 config.toml 即可。

 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
<main class="main list" id="main">
    <div class="main-inner">
        <div class="content categories">
            {{ if .Site.Params.displayListTitle }}
            <h1 class="list-title">
                {{ "Wiki"}}
            </h1>
            {{ end }}
            <div class="tree">
                <ul class="list-categories" style="display: block;">
                    {{ partial "utils/tree-sections.html" . }}
                    {{ $sections := .Scratch.Get "sections" }}
                    {{ $pages := .Scratch.Get "pages" }}
                    {{ range $index, $page := $pages }}
                    {{ $depth := (len (split (strings.TrimPrefix "/" $page) "/")) }}
                    {{ with $.Site.GetPage $page }}
                    {{ $linkTarget := .}}
                    {{ $depthPrev := 0 }}
                    {{ if ge $index 1 }}
                    {{ $pagePrev := index $pages (sub $index 1) }}
                    {{ $depthPrev = len (split (strings.TrimPrefix "/" $pagePrev) "/") }}
                    {{ end }}
                    {{ $depthNext := 0 }}
                    {{ if lt $index (sub (len $pages) 1) }}
                    {{ $pageNext := index $pages (add $index 1) }}
                    {{ $depthNext = len (split (strings.TrimPrefix "/" $pageNext) "/") }}
                    {{ end }}
                    {{ if or (le $depth $depthPrev) (eq $index 0) }}
                    <li>
                        {{ end }}
                        {{ if and (gt $depth $depthPrev) (ne $index 0) }}
                        <ul class="list-categories" style="display: block;">
                            <li>
                                {{ end }} {{ $name := index $sections $index }}
                                <div class="category-item">
                                    {{ .LinkTitle | default $name}}
                                    {{ if $.Site.Params.displayPostsCount }}
                                    {{ $sectionPage := .CurrentSection }}
                                    {{$.Scratch.Delete "pages" }}
                                    {{ range $.Site.RegularPages }}
                                    {{ if (.IsDescendant $sectionPage)}}
                                    {{ $.Scratch.Add "pages" (slice .) }}
                                    {{ end}} {{ end }}
                                    {{ $pages := $.Scratch.Get "pages"}}
                                    <span class="category-count">
                                        {{printf "(%d)" (len $pages)}}
                                    </span>
                                    {{ end }}
                                </div>

                                {{ if $.Site.Params.displayPosts }}
                                {{ $sectionPage := .CurrentSection }}
                                {{ $.Scratch.Delete "pages"}}
                                {{ range $.Site.RegularPages }}
                                {{ if (.InSection $sectionPage)}}
                                {{ $.Scratch.Add "pages" (slice .) }}
                                {{ end }} {{ end }}
                                {{ $pages := $.Scratch.Get "pages" }}
                                {{ partial "utils/limit-tree-posts.html" (dict "$" $ "pages" $pages
                                "linkTarget" $linkTarget) }}
                                {{ end }}
                                {{ if and (gt $depth $depthNext) (ne $index (sub (len $pages) 1)) }}
                                {{ range seq (sub $depth $depthNext) }}
                                {{ if le . (sub $depth $depthNext) }}
                            </li>
                        </ul>
                        {{ end }} {{ end }} {{ end }} {{ if ge $depth $depthNext
                        }}
                    </li>
                    {{ end }} {{ end }} {{ end }}
                </ul>
            </div>
        </div>
    </div>
</main>
<script>
    let lis = document.querySelectorAll("ul.list-categories > li");
    lis.forEach(li => {
        li.querySelector(".category-item").addEventListener("click", event => {
            event.stopPropagation(); // 阻止事件冒泡
            let sonul = li.querySelector("ul");
            sonul.style.display = sonul.style.display === "block" ? "none" : "block";
            if (sonul.nextElementSibling) {
                sonul.nextElementSibling.style.display = sonul.nextElementSibling.style.display === "block" ? "none" : "block";
            }
        });
    })
</script>
 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

[menu]
## 菜单栏
[[menu.main]]
pageref = "/posts/"
name = "Posts"
weight = 2
pre = "internal"
post = "archive"
[[menu.main]]
pageref = "/tags/"
name = "Tags"
weight = 4
pre = "internal"
post = "tags"
[[menu.main]]
pageref = "/about/"
name = "About"
weight = 5
pre = "internal"
post = "user-circle"
[[menu.main]] # add wiki page
pageref = "/wiki/"
name = "Wiki"
weight = 6
pre = "internal"
post = "wiki"
[[menu.main]]
weight = 7
identifier = "theme-switcher"
[[menu.main]]
weight = 8
identifier = "lang-switcher"
[[menu.main]]
weight = 9
identifier = "search"
post = "search"

Algolia 可以提供 AI 搜索服务,但需要在更新站点时用 POST 请求上传 algolia.json(站点信息) 给 Algolia,以帮助 Algolia 实现搜索服务。按照要求新建站点以及配置 api 即可,上传 algolia.json 可以使用hugo-algolia。因为 MeME 主题有一定的 algolia search 支持,下面仅给出上传 algolia.json 方面的配置。

1
$ npm install hugo-algolia

但 hugo-algolia 的 toml 解析在我这里好像有点问题,可能是 config.toml 的配置已经太乱了,无法优雅地解决

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ hugo-algolia -s -t --config config.toml
JSON index file was created in public/algolia.json

/usr/local/lib/node_modules/hugo-algolia/node_modules/algoliasearch/src/AlgoliaSearchCore.js:50
    throw new errors.AlgoliaSearchError('Please provide an application ID. ' + usage);
    ^
AlgoliaSearchError: Please provide an application ID. Usage: algoliasearch(applicationID, apiKey, opts)
    at AlgoliaSearchNodeJS.AlgoliaSearchCore (/usr/local/lib/node_modules/hugo-algolia/node_modules/algoliasearch/src/AlgoliaSearchCore.js:50:11)
    at AlgoliaSearchNodeJS.AlgoliaSearch (/usr/local/lib/node_modules/hugo-algolia/node_modules/algoliasearch/src/AlgoliaSearch.js:11:21)
    at AlgoliaSearchNodeJS.AlgoliaSearchServer (/usr/local/lib/node_modules/hugo-algolia/node_modules/algoliasearch/src/server/builds/AlgoliaSearchServer.js:17:17)
    at new AlgoliaSearchNodeJS (/usr/local/lib/node_modules/hugo-algolia/node_modules/algoliasearch/src/server/builds/node.js:83:23)
    at algoliasearch (/usr/local/lib/node_modules/hugo-algolia/node_modules/algoliasearch/src/server/builds/node.js:68:10)
    at HugoAlgolia.HugoAlgolia.sendIndex (/usr/local/lib/node_modules/hugo-algolia/lib/index.js:184:20)
    at HugoAlgolia.HugoAlgolia.index (/usr/local/lib/node_modules/hugo-algolia/lib/index.js:122:12)
    at Object.<anonymous> (/usr/local/lib/node_modules/hugo-algolia/bin/index.js:23:26)
    at Module._compile (internal/modules/cjs/loader.js:999:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)

无奈之下新建了一个 config.yaml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
---
baseurl: "/"
DefaultContentLanguage: "zh-cn"
hasCJKLanguage: true
languageCode: "zh-cn"
title: "loomt's Blog"
theme: "MeME"
metaDataFormat: "yaml"
algolia:
  index: "your index"
  key: "your admin key"
  appID: "your appID"
---
1
2
3
$ hugo-algolia -s
JSON index file was created in public/algolia.json
{ updatedAt: '2023-01-12T14:40:31.454Z', taskID: 173970040001 }

用 Vercel 还有一个原因是白嫖国外的服务器不用备案😭

reference

https://gohugo.io/getting-started

https://blog.aozaki.cc/blog/hugo-deployment-debugging

https://io-oi.me/tech/hugo-vs-hexo

https://github.com/MunifTanjim/minimo/issues/189

https://zenlian.github.io/posts/tools/github-actions-hugo

https://github.com/peaceiris/actions-gh-pages

https://gohugo.io/content-management/sections

https://gohugo.io/templates

https://gohugo.io/hugo-pipes

updatedupdated2024-06-122024-06-12