在上一篇搭建Hugo网站中,我们完成了静态网站的搭建。为了实现网站的持续更新,这一篇我们来实现编写-上传-构建-部署的自动化流程

由于hugo的构建和部署是分开的,因此可行的思路非常开阔。笔者将提供几种思路以供参考

首先,不建议使用hugo server部署至web端口或将web端口转发至hugo server的方式。hugo server仅为本地开发调试而设计,其默认行为不具备生产环境所需的安全性与性能优化

对博客的版本管理

无论使用什么方式,都建议对内容与配置进行版本管理。版本控制不仅有助于追踪修改历史、恢复误删内容,也便于多设备协作、自动化部署等操作。在本文我们使用git进行版本管理,其余的CVS工具可以使用类似的思路

在建立git仓库时,建议排除public/(或者仓库目录中你规定的其他输出路径(你为什么要这么干))目录。.gitignore中加入

public/

本地部署

如果你的web服务就部署在本地,那么每次编辑完后手动构建一次目标目录即可

远程部署

本地构建 + 上传构建文件(不推荐)

当然,你可以选择本地build完后,上传publc/等输出仓库至服务器的方式。这也可以通过让git管理输出目录来完成上传的部分,思路和下文类似

本地上传修改 + 远程构建

先看需求:我们需要在完成编辑时,先将本地的修改上传,然后在远程自动触发一次构建,并且使用nginx等工具来运行web server。

能完成需求的思路都是好思路,这里提供两个

建立公共远程仓库,服务器同步并构建

    graph LR 

A[本地] --push--> B(公共仓库) --pull--> C[Web Server] --build & run--> D([用户]) 

笔者选择的方式,因此这部分个人的经验会多一些

首先选择一个公共的远程仓库,需要你的本地和服务器都可以访问到。笔者的服务器位于中国大陆且不想配置代理,因此将仓库建立在了自己托管的git平台上。接下来在服务器建立仓库并添加远程,过程不再赘述。

WebServer从公共仓库的同步也可以采取多种思路。最简单的方式就是规定时间自动拉取。笔者采用了当公共仓库的主分支变动时,向服务器发送一个Webhook并触发拉取和构建的流程,笔者团队托管的gitea自带发送Webhook的功能,这部分也可以用其他平台的类似服务代替。

Webhook的客户端,可以使用apt库中的webhook。为Webhook写一个配置文件,

// hooks.json
[
  {
    "id": "DFSiteHook",
    "execute-command": "/home/dedfaf/DF-Site/build.sh",
    "command-working-directory": "/var/www/DF-Site",
    "response-message": "Hugo rebuild.",
    "trigger-rule": {
      "match": {
        "type": "value",
        "secret":"xxx",
        "value": "refs/heads/master",
        "parameter": {
          "source": "payload",
          "name": "ref"
        }
      }
    }
  }
]

令其接收到hooks后执行脚本

# build .sh
#!/bin/bash

# Auto pull and build hugo site when webhook is triggered.

set -e

REPO_DIR="/var/www/DF-Site"
HUGO_BIN="hugo"

cd "$REPO_DIR"
# echo "[`date`] pulling latest changes..."
# git pull

git fetch
git reset origin/master --hard

# echo "[`date`] building hugo site..."
"$HUGO_BIN"

# echo "[`date`] adjusting permissions..."
# chown -R www-data:www-data "$REPO_DIR/public"
为什么使用`git reset origin/master --hard`? 如果使用`git pull`,当公共仓库主分支和远程仓库的分支冲突时,就无法直接拉取,例如笔者有经常`commit --amend`并且强推的习惯,因此笔者使用这种方式。这一部分可以根据实际情况修改

另:在实际操作中,建议为Webhook启用sercet并且为其配置反向代理和https。此处不再赘述

在服务器设置裸仓库直接上传

    graph LR 

A[本地] --push--> C[Web Server]

C -- 构建文件 --> C
C --build & run--> D([用户]) 

笔者没有实操过这个思路,在这里仅简单讲解思路

什么是裸仓库:32.裸仓库| Git_Learning

总之需要知道普通仓库作为别的仓库的远程仓库因为存在工作区,推送会出现问题,因此服务器上只能建立裸仓库。而问题正是裸仓库没有工作区,因此需要在每次推送后执行一次构建工作区的操作,并以构建的工作区来构建网站。

首先建立好本地上传仓库和远程裸仓库,然后以ssh的方式为本地添加远程仓库。

我们在远程仓库设置一个post-receive hook,编辑.git/hooks/post-receive,参考如下内容

#!/bin/bash
set -e

TARGET="/var/www/myproject"
GIT_DIR="/var/git/myproject.git"

# 如果目录不存在,先创建
mkdir -p "$TARGET"

# 如果工作区已经是 git 仓库,强制同步;否则 clone 一份
if [ -d "$TARGET/.git" ]; then
    echo "[INFO] Pulling latest changes into $TARGET"
    git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f
else
    echo "[INFO] Cloning into $TARGET"
    git clone "$GIT_DIR" "$TARGET"
fi

# 可选:设置权限或执行构建
# chown -R www-data:www-data "$TARGET"

# 构建
cd "$TARGET"
hugo build

# echo "[DONE] Deployment finished"

记得赋予post-receive执行权限。

另:如果你的主题是用submodule的方式加入的,那么构建工作区时默认不会下载子模块中的内容,可以在脚本的构建部分添加

# 构建
cd "$TARGET"
git submodule init
git submodule update --recursive --remote
hugo build