github+hexo博客搭建
搭建博客需求描述
说明:hexo可以快速部署个人博客,但是每次写文章需要使用hexo new post命令去新建文件就比较繁琐,我想搭建一个hexo项目和个人md笔记库分离。笔记就存放在md笔记库,当我git push推送到md笔记仓库会自动将md笔记发布到个人博客,搜寻资料整理了两种方案:
方案一:本地发布
通过脚本将md笔记拷贝到原始hexo项目并本地生成发布推送github的hexo博客仓库
- 优点:本地能发现脚本错误,方便调试
- 缺点:需依赖本地配置nodejs,hexo,git等多个环境,不方便跨设备
方案二:远程发布
md笔记库与hexo项目库分离,md笔记库推送后自动触发github上配置的action,自动进行打包生成发布到github上的博客仓库
总结
个人比较倾向于本地专注md仓库管理,初次配置调试好github工作流后后续不太需要进行维护工作。且发布不需要依赖在本地重新配置环境,适合跨设备使用。
远程发布设计
主要管理md文件仓库,内嵌hexo原始项目,当md文件仓库的文件push后,github通过action运行配置的脚本将变动的脚步拷贝到hexo原始项目(自动建立资源文件夹和图片),并安装依赖环境发布hexo到个人博客项目。
项目结构
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
| ./blog-markdown ├── docker │ └── 安装 │ └── 教程 │ └── 安装.md ├── generate.sh ├── golang ├── img.png ├── kubernetes ├── .hexo │ └── blog (hexo原始项目) │ ├── _config.landscape.yml │ ├── _config.yml │ ├── .deploy_git │ ├── .github │ ├── .gitignore │ ├── db.json │ ├── node_modules │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── scaffolds │ ├── source │ └── themes ├── Linux ├── README.md ├── update.sh └── 个人随记 └── github+hexo搭建博客.md
|
搭建流程
初始化hexo项目
安装hexo
安装部署插件
1
| npm install hexo-deployer-git --save
|
安装主题(建议主题本地安装插件并预览一下,github上配置的action不配置主题更换变动)
1
| npm install hexo-theme-butterfly --save
|
再_config.yaml配置git信息
1 2 3 4
| deploy: type: 'git' repo:https://{token}@github.com/MistFjord/mistfjord.github.io branch: master # GitHub Pages的默认分支
|
创建markdown笔记仓库

将hexo项目内嵌到该仓库(可参考上面的项目结构)
配置github的action工作流

工作流脚本如下:
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 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
| name: Auto Sync on: push: branches: - main - develop
jobs: convert: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18'
- name: Install Hexo CLI run: npm install -g hexo-cli
- name: Install Hexo Git run: npm install hexo-deployer-git --save
- name: Convert Structure run: | #!/bin/bash # set -xe set -eo pipefail # 严格错误处理 echo "==========================================================================" echo "🚀 开始 Markdown 到 Hexo 的转换工作流" echo "==========================================================================" MD_REPO_ROOT=$(pwd) HEXO_DIR="${MD_REPO_ROOT}/.hexo/blog/source/_posts" mkdir -p "$HEXO_DIR" echo "源文件夹: $MD_REPO_ROOT" echo "目标目录: $HEXO_DIR" get_md_files_name() { local include_dir=$1 local exclude_dir=$2 find "$include_dir" -type f -name "*.md" -not -path "$include_dir/.hexo/**" -not -path "$include_dir/node_modules/**" | while IFS= read -r file; do [[ $file == *"/.hexo/"* ]] && continue [[ $file == *"/node_modules/"* ]] && continue [[ $file == *"$exclude_dir"* ]] && continue local rel_path=${file#"$include_dir/"} local hexo_name=$(echo "$rel_path" | tr '/' '-' | sed 's/\.md$//') echo "$hexo_name" done } init_sync() { echo "⚙️ 初始化同步环境..." mkdir -p "$HEXO_DIR" } clean_orphan_files() { echo "" echo "🧹 开始清理孤儿文件..." get_md_files_name "$MD_REPO_ROOT" ".hexo" > .current_files.tmp local clean_dir_list=() local clean_file_list=() find "$HEXO_DIR" -mindepth 1 -maxdepth 1 > tmpfile.txt while IFS= read -r target_file; do if [[ -d "$target_file" ]]; then local hexo_name=$(basename "$target_file") else local hexo_name=$(basename "$target_file" .md) fi if ! grep -q "^$hexo_name$" .current_files.tmp; then if [[ -d "$target_file" ]]; then rm -rf "$target_file" clean_dir_list+=("${hexo_name}") echo " 🗑️ 已删除孤儿目录: $hexo_name/" else rm -f "$target_file" clean_file_list+=("${hexo_name}.md") echo " 🗑️ 已删除孤儿文件: $hexo_name.md" fi fi done < tmpfile.txt rm -f tmpfile.txt .current_files.tmp echo "✅ 孤儿文件清理完成" echo " → 删除目录: ${#clean_dir_list[@]} 个" for dir in "${clean_dir_list[@]}"; do echo " - $dir/" done echo "" echo " → 删除文件: ${#clean_file_list[@]} 个" for file in "${clean_file_list[@]}"; do echo " - $file" done } path_to_hexo_name() { local rel_path="$1" echo "$rel_path" | sed 's#/#-#g' | sed 's/\.md$//' } process_images() { local file="$1" local output_dir="$2" mkdir -p "${output_dir}" matches=$(grep -E '!\[.*\]\(([^)]+)\)' "$file") || { if [ $? -ne 1 ]; then echo "❌ grep执行失败" >&2 exit 1 fi } if [[ -z "$matches" ]]; then echo "ℹ️ 文件 $file 中未发现Markdown图片引用" return 0 fi echo "$matches" | while IFS= read -r line || [[ -n "$line" ]]; do img_path=$(echo "$line" | sed -E 's/.*\(([^)]+)\).*/\1/') if [[ -z "$img_path" ]]; then echo "⚠️ 跳过空图片路径: $line" continue fi case "$img_path" in http*|//*) echo " 🌐 保持网络图片原样: $img_path" ;; /*) echo " ⚠️ 需要手动处理的绝对路径: $img_path" >&2 ;; *) sanitized_path=$(echo "$img_path" | sed 's/[;&|`$<>]//g') local_img_path="${sanitized_path}" # 4. 有效性验证(双路径检查) if [[ -f "${local_img_path}" ]]; then echo " ✅ 复制项目相对路径图片: $local_img_path" cp -f "${local_img_path}" "${output_dir}/" sed -i "s|([[:space:]]*${img_path//\//\\\/}[[:space:]]*)|($(basename "$local_img_path"))|g" "$file" elif [[ -f "$(dirname "$file")/${local_img_path}" ]]; then echo " 📁 复制同级目录图片: $(dirname "$file")/$local_img_path" cp -f "$(dirname "$file")/${local_img_path}" "${output_dir}/" sed -i "s|([[:space:]]*${img_path//\//\\\/}[[:space:]]*)|($(basename "$local_img_path"))|g" "$file" else echo "❌ 图片未找到: '$local_img_path'" >&2 printf " ├─ 尝试位置: ${local_img_path}\n └─ 相对位置: $(dirname "$file")/${local_img_path}\n" >&2 fi ;; esac done if ! grep -qE '!\[.*\]\(([^)]+)\)' "$file"; then echo "ℹ️ 文件 $file 中未发现Markdown图片引用" fi } get_rel_path() { local abs_path="$1" local rel_path="${abs_path#$MD_REPO_ROOT/}" [ "${rel_path}" == "${abs_path}" ] && rel_path="${abs_path#$MD_REPO_ROOT}" echo "${rel_path%.md}" # 返回时移除.md后缀 } # 创建 Hexo 文件 create_hexo_file() { local src_file="$1" local hexo_file="$2" local res_dir="$3" local rel_path=$(get_rel_path "$src_file") local dir_path=$(dirname "$rel_path") mkdir -p "$(dirname "$hexo_file")" process_images "$src_file" "$res_dir" local title=$(basename "$src_file" .md) echo "---" > "$hexo_file" echo "title: ${title}" >> "$hexo_file" echo "date: $(date +"%Y-%m-%d %H:%M:%S")" >> "$hexo_file" IFS='/' read -ra parts <<< "$dir_path" echo "categories: [${parts[0]}]" >> "$hexo_file" tags="" for ((i=1; i<${ tags+=", ${parts[$i]}" done echo "tags: [${tags:2}]" >> "$hexo_file" echo "---" >> "$hexo_file" cat "$src_file" >> "$hexo_file" } sync_files() { echo "" echo "🔄 开始同步 Markdown 文件到 Hexo 项目..." local created=() local updated=() local skipped=() find "$MD_REPO_ROOT" -type f -name "*.md" -not -path "$MD_REPO_ROOT/.hexo/**" -not -path "$MD_REPO_ROOT/node_modules/**" > tmpfile.txt while read -r src_file; do [[ $src_file == *"/.hexo/"* ]] && continue [[ $src_file == *"/node_modules/"* ]] && continue rel_path=$(get_rel_path "$src_file") hexo_name=$(path_to_hexo_name "$rel_path") local hexo_file="${HEXO_DIR}/${hexo_name}.md" local hexo_res_dir="${HEXO_DIR}/${hexo_name}" # 资源目录 # 判断同步条件 if [ ! -f "${hexo_file}" ]; then echo " 🆕 新创建: $rel_path" create_hexo_file "$src_file" "$hexo_file" "$hexo_res_dir" created+=("${rel_path}") else if [ "$src_file" -nt "$hexo_file" ]; then echo " 🔄 更新文件: $rel_path" create_hexo_file "$src_file" "$hexo_file" "$hexo_res_dir" updated+=("${rel_path}") else echo " ✅ $rel_path (无需更新)" skipped+=("${rel_path}") fi fi done < tmpfile.txt rm -f tmpfile.txt # 详细结果输出 echo "" echo "🎯 文件同步完成!" echo " → 新创建: ${#created[@]} 个" for file in "${created[@]:0:10}"; do echo " - $file" done [[ ${ echo "" echo " → 已完成更新: ${#updated[@]} 个" for file in "${updated[@]:0:10}"; do echo " - $file" done [[ ${ echo "" echo " → 已跳过: ${#skipped[@]} 个 (未更改)" } main() { init_sync clean_orphan_files sync_files echo "✅ 所有转换任务已完成!" echo "==========================================================================" } main
- name: Build working-directory: .hexo/blog run: | npm install hexo-theme-butterfly --save # 设置Git用户信息 git config --global user.name "MistFjord" git config --global user.email "1392792445@qq.com" # 调试信息 echo "Current directory: $(pwd)" ls -la npm install -g hexo-cli npm install echo "=== Hexo Version ===" hexo version echo "=== Cleaning ===" if ! hexo clean; then echo "::error::Hexo clean failed" exit 1 fi echo "=== Generating ===" if ! hexo generate; then echo "::error::Hexo generate failed" exit 1 fi echo "=== Deploying ===" if ! hexo deploy; then echo "::error::Hexo deploy failed" exit 1 fi echo "All commands executed successfully"
|