Skip to content

Latest commit

 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mica-docs-deploy

GitHub Webhook 服务,自动化部署多个开源项目的静态文档站(VuePress / VitePress 等) 到 nginx 目录。支持原子切换、tar.gz 备份保留、HMAC 签名校验、per-project 并发互斥。

特性

  • 一个端点,多个项目 —— 通过 ?project=...&branch=... 路由
  • 原子切换 —— nginx_dir 被 rename 成 .old,新文件解压到 .staging,再一次性 rename 切换。失败时 .old 自动还原
  • 备份 —— 每次部署把当前 nginx_dir 打成 tar.gz 归档(保留数量可配置)
  • 安全 —— GitHub HMAC-SHA256 签名校验,常量时间比较防时序攻击
  • 可观测 —— slog 结构化日志,同时输出到 stdout(journald 收集)和按天切分的文件(/health/status 端点)
  • 恢复 —— 启动时自动清理残留的 .staging / .old 与过期日志文件
  • 私有仓库 —— auth 段配 PAT 或 SSH 私钥即可拉私有 GitHub 仓库;URL 含凭据时日志自动脱敏

快速开始

1. 编译

本地快速构建:

go build -o mica-docs-deploy .
# 二进制在当前目录 mica-docs-deploy

Linux x86_64 静态构建(产物可丢到任何 glibc/musl 发行版直接跑,免装运行时;推荐部署用):

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
  -trimpath \
  -tags netgo,osusergo \
  -ldflags '-s -w -extldflags "-static"' \
  -o mica-docs-deploy .

# 验证确实是静态链接(输出含 "statically linked")
file mica-docs-deploy
# mica-docs-deploy: ELF 64-bit LSB executable, x86-64, statically linked, Go Go1.x, not stripped

参数说明:

  • CGO_ENABLED=0 + netgo,osusergo —— 避免依赖系统的 glibc / libnss / libpam,纯 Go 实现 DNS 与 user 查询
  • -trimpath —— 去掉二进制里的本地路径,构建可重现、泄露不了开发机目录
  • -ldflags '-s -w' —— 去掉符号表与 DWARF,体积更小(~10MB → ~7MB)
  • -extldflags "-static" —— 链接器参数,强制静态链接,生成 fully static ELF

如果只在部署机上编译,去掉 GOOS=linux GOARCH=amd64 即可;macOS 上交叉编译默认 cgo 会用本机 Clang,去掉 -extldflags "-static" 也能跑(macOS 不需要 fully static)。

2. 写配置

sudo mkdir -p /etc/mica-docs-deploy
sudo cp config.example.yaml /etc/mica-docs-deploy/config.yaml
sudo vi /etc/mica-docs-deploy/config.yaml
sudo chmod 600 /etc/mica-docs-deploy/config.yaml

字段说明见「配置说明」,完整示例见下方「config.yaml 配置示例」。

3. 建目录

sudo useradd --system --shell /usr/sbin/nologin mica-docs
sudo mkdir -p /var/lib/mica-docs-deploy/{repos,status,logs}
sudo chown -R mica-docs:mica-docs /var/lib/mica-docs-deploy
sudo mkdir -p /var/www/mica-mqtt-docs
sudo chown mica-docs:mica-docs /var/www/mica-mqtt-docs

4. 安装

sudo cp mica-docs-deploy /usr/local/bin/
sudo cp systemd/mica-docs-deploy.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now mica-docs-deploy
sudo systemctl status mica-docs-deploy

5. 配置 GitHub Webhook

在你的 GitHub 仓库 → Settings → Webhooks → Add webhook:

  • Payload URL: http://your-host:8080/webhook/github?project=mica-mqtt-docs&branch=dist
  • Content type: application/json
  • Secret:config.yaml 里 secret 字段相同
  • Events: 只勾选 "push event"

6. 配套 GitHub Action

在源仓库加 .github/workflows/deploy-docs.yml

name: 部署文档

on:
  push:
    branches:
      - main

permissions:
  contents: write

jobs:
  deploy-gh-pages:
    runs-on: ubuntu-latest
    steps:
      - name: 拉取代码
        uses: actions/checkout@v6
        with:
          fetch-depth: 0
      - name: 设置 pnpm
        uses: pnpm/setup@v1
        with:
          version: 11
          runtime: node@22
          cache: true
      - name: 安装依赖
        run: pnpm install --frozen-lockfile
      - name: 构建文档
        env:
          NODE_OPTIONS: --max_old_space_size=8192
        run: |-
          pnpm run build
          > src/.vuepress/dist/.nojekyll
      - name: 部署文档
        uses: JamesIves/github-pages-deploy-action@v4
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        with:
          # 部署文档
          branch: dist
          folder: src/.vuepress/dist

配置说明

配置文件为 YAML 格式(模板见 config.example.yaml),分为三部分:serverbackupprojects

config.yaml 配置示例:

# 服务端配置
server:
  listen: "0.0.0.0:8080"                  # HTTP 监听地址(GitHub Webhook 推送目标)
  data_dir: "/var/lib/mica-docs-deploy"   # 内部数据目录(repos/status/logs 子目录)
  log_level: "info"                       # 日志级别:debug / info / warn / error
  # log_dir: "/var/log/mica-docs-deploy"  # 日志文件目录;空则默认 <data_dir>/logs
  # log_keep_days: 7                      # 按天日志文件保留天数;<=0 表示不清理

# 备份全局默认值(可被项目级 keep 覆盖)
backup:
  default_keep: 5                         # 每个项目默认保留最近 5 份 tar.gz 备份

# 项目列表:每个开源文档站一个条目
projects:
  - name: "mica-mqtt-docs"                              # 项目名(唯一,webhook 用 ?project= 匹配)
    repo_url: "https://github.com/dromara/mica-mqtt-docs.git"  # 公开仓库地址
    branch: "dist"                                      # 目标分支(存放构建产物)
    nginx_dir: "/var/www/mica-mqtt-docs"                # nginx 站点根目录
    secret: "github-webhook-secret-1"                   # Webhook Secret(≥16 字符)
    keep: 10                                            # 可选:覆盖默认保留份数
    backup_dir: "/var/backups/mica-mqtt-docs"           # 可选:自定义备份目录;默认 <data_dir>/backups/<project>,勿放站点根目录内(避免外网可访问)

  # ----- 私有仓库示例 -----
  # 在项目下加 auth 段即可拉私有 GitHub 仓库。两类认证:
  #   type: token   —— GitHub PAT(推荐 fine-grained,只授 contents: read)
  #   type: ssh_key —— 私钥文件(绝对路径;加密时填 ssh_key_password)
  # 不配 auth = 公开仓库,行为不变。
  - name: "internal-secret-docs"
    repo_url: "https://github.com/your-org/secret-docs.git"   # 也可写 git@github.com:your-org/secret-docs.git
    branch: "dist"
    nginx_dir: "/var/www/internal-secret-docs"
    secret: "another-webhook-secret-16+chars"
    auth:
      type: "token"
      token: "ghp_xxxxxxxxxxxxxxxxxxxx"

校验规则: 启动时校验所有配置,错误一次性全部列出(每个错误一行),校验失败直接退出。路径会做规范化处理(去尾斜杠、解析 ..)。

auth 字段说明:

字段 必填 说明
type token(HTTPS + PAT) 或 ssh_key(SSH 私钥)
token type=token 时必填 GitHub PAT。Username 固定为 x-access-token(GitHub 约定)
ssh_key type=ssh_key 时必填 私钥文件绝对路径,建议 chmod 600,owner 与服务运行用户一致
ssh_key_password 加密私钥的 passphrase

GitHub 端配套:

  • Token:推荐用 Fine-grained PAT,只勾选目标仓库的 Contents: Read-only 权限。旧式 classic token 用 repo 范围。
  • Deploy Key:在私有仓库 → Settings → Deploy keys → Add key,Allow read only,把公钥贴进去;ssh_key 字段填对应的私钥路径。
  • Webhook:私有仓库的 webhook 推送不需要仓库读权限——它是 GitHub → 你的服务方向,跟拉取方向无关。Secret 照常配。

部署流程

一次 webhook 触发后,服务按以下顺序执行(任一步失败可回滚,nginx_dir 不会半新半旧):

  1. 备份 —— 把当前 nginx_dir 打成 tar.gz(命名 YYYY-MM-DDTHHMMSSZ.tar.gz),并清理超出保留份数的旧备份
  2. 拉取 —— go-git 从 repo_url 建立 bare 缓存仓库并 fetch 目标分支
  3. 导出 —— 把分支的 commit tree 导出到 <nginx_dir>.staging
  4. 原子切换 —— nginx_dir rename 成 .oldstaging rename 成 nginx_dir;任一步失败自动回滚
  5. 清理 —— 删除 .old,记录部署状态(commit / 耗时)到内存与 status/ 目录

同一项目并发 webhook 会串行执行(信号量锁,等待上限 lock_timeout_secs);不同项目互不影响。

状态

/status 返回每个项目最近一次部署结果,字段:

字段 说明
last_deploy_at 最近一次部署时间(RFC3339 UTC)
last_commit 部署的 commit SHA(40 位)
last_status Success / Failed / Pending
last_duration_ms 部署耗时(毫秒)
last_error 失败时的错误信息
recovered_at 启动恢复完成时间(如有)

日志

日志同时输出到 stdout(systemd 收集到 journald)和按天切分的文件

  • 目录:默认 <data_dir>/logs,可通过 server.log_dir 自定义
  • 文件名:mica-docs-deploy.YYYY-MM-DD.log(日期用 UTC,方便多机一致)
  • 保留:默认 7 天,<= 0 表示永久保留;启动时清理过期文件
# 实时看文件
tail -f /var/lib/mica-docs-deploy/logs/mica-docs-deploy.$(date -u +%F).log

# 看 systemd 收集的(与上面同一份内容)
journalctl -u mica-docs-deploy -f

HTTP API

方法 路径 说明
POST /webhook/github?project=X&branch=Y Webhook 入口
GET /health 存活探针,返回 ok
GET /status 所有项目最近部署状态(JSON)
GET /status/{project} 单个项目状态(未部署过 → 404)

POST /webhook/github 响应

状态码 含义 Body
202 已入队,部署在后台执行 {"status":"queued","project":"...","branch":"..."}
400 请求头/参数不合法(非 GitHub User-Agent、非 push 事件、branch 或 ref 不匹配) {"error":"bad_request","detail":"..."}
401 HMAC 签名校验失败 {"error":"invalid_signature","detail":"..."}
404 未知 project {"error":"unknown_project","detail":"..."}
503 锁等待超时(同项目部署进行中) {"error":"queue_timeout","detail":"..."}
500 内部错误 {"error":"internal","detail":"..."}

恢复

如果部署中途被 kill,下次启动时本服务会:

  • 删除残留的 <nginx_dir>.staging(永远安全)
  • <nginx_dir>.old 自动还原成 <nginx_dir>(当 nginx 不存在时)
  • 如果 nginx_dir.old 同时存在 → 记录 ERROR 保留现场,等人工介入

从备份手动恢复:

tar -xzf /var/lib/mica-docs-deploy/backups/mica-mqtt-docs/2026-08-17T103000Z.tar.gz -C /var/www/mica-mqtt-docs --strip-components=1

开源协议

Apache 2.0

About

mica 相关文档发布更新项目。

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages