
ety001
玲珑机括(linglong-webhook)是一个轻量级 Webhook 管理平台,兼容 Gogs / Gitea / GitHub / GitLab。
本文带你从零开始:在懒猫微服上安装 → 配置一个测试仓库 → 写一个脚本拉取代码并用 Docker 编译 →
创建 Webhook 把它和 Gogs 推送事件连起来,最终实现「推一下代码就自动构建」。
整个过程大约 20 分钟。
在懒猫微服的「应用商店」中搜索 玲珑机括,点击安装。安装完成后打开应用。
第一次打开应用时,你会看到一个初始化页面,系统会自动生成一个 API Key。
⚠️ 这个 Key 只显示这一次,请务必立即保存!
页面上会有醒目的黄色提示。一旦关闭页面,明文就再也看不到了。
如果日后丢失,只能手动清空数据库里的api_key_hash记录让程序重新初始化。

点击「复制」把它存到你的密码管理器里,然后点 「我已保存,进入系统」。
进入系统后会看到主面板。之后每次重新打开应用,都会要求你输入这个 API Key 登录。
进入左侧菜单的 「文件管理」。这里的根目录是 /data/workspace,
它是持久化目录(懒猫上对应宿主机 /lzcapp/var/workspace),容器重建后文件不会丢。
你的脚本、克隆下来的代码都应该放在这里。
点击右上角 「新建文件夹」,输入 test,回车。
现在你的文件树应该是这样:
/data/workspace/
└── test/
web 界面没有做删除和修改功能,如果需要可以去懒猫网盘=>应用数据修改
在 Gogs 上新建一个仓库,命名为 test(可以私有)。初始化时勾选生成 README,方便克隆。
我们走 git 协议克隆,地址形如:
git@gogs.xxx.heiyu.space:ety001/test.git
本机生成一对密钥(若还没有):
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
在玲珑机括的文件管理里,于 /data/workspace 下新建 sshkey 目录,里面新建文件 id_ed25519,
把私钥完整内容粘贴进去并保存。

再把公钥(id_ed25519.pub)添加到 Gogs:
头像 → 设置 → SSH 密钥 → 添加。

在 test 仓库根目录创建 Dockerfile,内容如下:
FROM alpine:3.20
# 这两个 ARG 接收脚本在 docker build 时通过 --build-arg 传入的值
ARG APP_VERSION=unknown
ARG BUILD_ENV=unknown
# 写进环境变量,方便容器运行时验证它们确实传进来了
ENV APP_VERSION=${APP_VERSION}
ENV BUILD_ENV=${BUILD_ENV}
# 启动时打印这两个值,用来证明「从 webhook 注入的环境变量」成功传到了镜像里
CMD ["sh", "-c", "echo \"APP_VERSION=$APP_VERSION BUILD_ENV=$BUILD_ENV\""]
提交并推送。这个 Dockerfile 的作用很单纯:把构建时传入的参数打印出来,
好让我们一眼确认「环境变量是不是真的从 webhook 一路传到了镜像内部」。

回到玲珑机括的文件管理,进入 test 目录,新建文件 test.sh,填入下面内容。
把脚本里的
REPO_URL换成你自己的 Gogs 克隆地址(见 4.2)。
#!/bin/bash
set -euo pipefail
# ============================================
# 测试构建脚本(子容器方案)
# 被 webhook 触发,通过子容器完成 Git 拉取和 Docker 构建
# 环境变量 APP_VERSION / BUILD_ENV 由 webhook pass_environment 注入
# ============================================
WORKSPACE_DIR="/data/workspace"
TEST_DIR="${WORKSPACE_DIR}/test"
SSH_KEY_DIR="${WORKSPACE_DIR}/sshkey"
REPO_DIR="${TEST_DIR}/repo"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
log "========== 开始构建 =========="
# 验证 SSH 密钥存在
if [ ! -f "${SSH_KEY_DIR}/id_ed25519" ]; then
log "ERROR: SSH 密钥不存在: ${SSH_KEY_DIR}/id_ed25519"
exit 1
fi
# ---- 1. 通过子容器拉取最新代码 ----
# 关键点:
# --network host —— 玲珑跑在懒猫容器里,子容器默认访问不到内网 Gogs,必须用 host 网络
# -v .ssh:/root/.ssh —— 整个 .ssh 目录读写挂载,便于写入 known_hosts
# ssh-keyscan —— 预填 known_hosts,免去首次交互确认
# chmod 600 —— ssh 要求私钥权限必须 600,否则拒绝使用
# alpine/git —— 同时带 git 和 ssh 客户端
log ">>> 通过子容器拉取代码..."
# 把 Gogs 主机和你的仓库地址换成实际的
GOGS_HOST="gogs.<你的懒猫微服名称>.heiyu.space"
REPO_URL="git@${GOGS_HOST}:ety001/test.git"
if [ ! -d "${REPO_DIR}/.git" ]; then
log " 首次 clone..."
mkdir -p "${REPO_DIR}"
docker run --rm \
--network host \
--entrypoint sh \
-v "${SSH_KEY_DIR}:/root/.ssh" \
-v "${REPO_DIR}:/repo" \
alpine/git:latest \
-c ' \
chmod 600 /root/.ssh/id_ed25519; \
ssh-keyscan -t ed25519 '"${GOGS_HOST}"' >> /root/.ssh/known_hosts 2>/dev/null; \
GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new" \
git clone --depth 1 '"${REPO_URL}"' /repo'
else
log " 已有仓库,执行 pull..."
docker run --rm \
--network host \
--entrypoint sh \
-v "${SSH_KEY_DIR}:/root/.ssh" \
-v "${REPO_DIR}:/repo" \
alpine/git:latest \
-c ' \
chmod 600 /root/.ssh/id_ed25519; \
ssh-keyscan -t ed25519 '"${GOGS_HOST}"' >> /root/.ssh/known_hosts 2>/dev/null; \
cd /repo && GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new" git pull'
fi
log " Git 操作完成"
# ---- 2. 把 webhook 注入的环境变量传给 docker build ----
log "APP_VERSION=${APP_VERSION:-(未设置)}"
log "BUILD_ENV=${BUILD_ENV:-(未设置)}"
# ---- 2. 构建 Docker 镜像 ----
# ⚠️ 关键:docker build 默认用 BuildKit,它解析 FROM 基础镜像(load metadata)时
# 不会自动走 dockerd 的代理,所以连 auth.docker.io 拿 token 会超时。
# 解决:把「设置 → Docker 代理设置」里配的代理加载进当前 shell,
# 这样 BuildKit 就能走代理拉取基础镜像了。
# (/etc/docker/proxy.env 由后端写入,与本脚本在同一容器内,可直接读取)
if [ -f /etc/docker/proxy.env ]; then
set -a; . /etc/docker/proxy.env; set +a
log "已加载 Docker 代理: ${HTTPS_PROXY:-${HTTP_PROXY:-(未配置)}}"
else
log "提示:未检测到代理配置。若拉取基础镜像超时,请到「设置 → Docker 代理设置」配置代理。"
fi
log ">>> 构建 Docker 镜像..."
docker build \
--build-arg APP_VERSION="${APP_VERSION}" \
--build-arg BUILD_ENV="${BUILD_ENV}" \
-t test-app:latest \
"${REPO_DIR}"
# ---- 3. 跑一下镜像,验证环境变量确实进去了 ----
log ">>> 构建完成,运行验证..."
docker run --rm test-app:latest
log "========== 全部完成 =========="
点 「保存」。

脚本做的事:用 git 协议拉取/更新 Gogs 代码 → 用 Docker 编译镜像
(并把 webhook 注入的环境变量通过 --build-arg 交给 Dockerfile)→ 运行镜像打印出这些值。
💡 拉取基础镜像超时?配代理!
上面的docker build解析FROM alpine:3.20时要连auth.docker.io,国内网络容易超时
(报错形如failed to fetch anonymous token ... i/o timeout)。
先到 「设置」→「Docker 代理设置」 填好 HTTP/HTTPS 代理并保存——保存后系统会重启内层 Docker Daemon。注意:代理设置主要对
docker pull生效;而docker build默认走 BuildKit,
它拉基础镜像时不会自动用 dockerd 的代理。这正是本脚本在构建前
source /etc/docker/proxy.env的原因——把代理加载进 shell,BuildKit 才能继承并走代理。
如果配了代理仍超时,确认代理地址正确,并检查「不走代理的地址」没误拦 docker.io。

进入左侧 「Webhook」 菜单,点 「新建」,按下面的内容填写:
| 字段 | 填写内容 | 说明 |
|---|---|---|
| Hook ID | test-build | URL 里的标识,只能用字母/数字/-/_ |
| 名称 | 测试构建 | 显示名,随便起 |
| 执行命令 | sh /data/workspace/test/test.sh | 上一步创建的脚本路径 |
| 工作目录 | /data/workspace | 脚本运行的工作目录 |
| 超时(秒) | 600 | 构建可能较慢,留足时间 |
| 并发策略 | skip | 已有构建在跑就跳过本次,避免冲突 |
| 环境变量 | 见下方 | 传给脚本的环境变量 |
环境变量(每行一个,格式 KEY=值):
APP_VERSION=1.0.0
BUILD_ENV=production
📌 关于环境变量:这里的值是固定注入的——每次触发都会把
APP_VERSION=1.0.0、
BUILD_ENV=production注入到脚本环境里。脚本再通过docker build --build-arg
把它们交进 Dockerfile。这就是「读取环境变量并赋给 Docker 编译过程」的实现方式。

触发规则(可选,但建议加上,限定只在 push 代码时触发):
📌 规则语法遵循 adnanh/webhook 格式。
这是社区里最通用的 webhook 规则约定,玲珑机括完全兼容。规则由match/and/or/not
组合而成,数据来源(source)支持header、payload、url、request-query等,
匹配方式(type)支持value(精确匹配)、regex(正则)、payload-hmac-sha256(签名校验)等。
完整语法请参考 adnanh/webhook 的 Hook 规则文档。
下面这条规则的含义是:仅当请求头 X-Gogs-Event 的值为 push 时才触发执行。
{
"match": {
"type": "value",
"value": "push",
"parameter": { "source": "header", "name": "X-Gogs-Event" }
}
}
💡 想限定只构建某个分支? 可以再加一条对 payload 的匹配(Gogs 会在 body 里带
ref):{ "and": [ { "match": { "type": "value", "value": "push", "parameter": { "source": "header", "name": "X-Gogs-Event" } } }, { "match": { "type": "value", "value": "refs/heads/main", "parameter": { "source": "payload", "name": "ref" } } } ] }有了
and/or组合,分支过滤、事件过滤、甚至 HMAC 签名校验都能自由拼装。🔁 另外,设置 → 导入/导出用的就是 adnanh/webhook 的
hooks.json格式,
如果你已有现成的 adnanh/webhook 配置,可以直接导进来复用,不必从头重建。
填好后点 创建。
创建完成后,详情页会显示一个 Webhook 地址,形如:
https://webhook.<你的设备名>.heiyu.space/hooks/test-build

把这个地址复制下来,下一步要用。
✅ 这个
/hooks/...地址是公开端点,不需要 API Key 也不经过懒猫网关登录鉴权,
所以 Gogs 可以直接访问它。
回到 Gogs 的 test 仓库:设置 → Web 钩子 → 添加 Web 钩子 → 选 Gogs。
POST保存。

随便在 test 仓库做个改动(比如改一下 README)然后 git push。
或者直接在配置界面触发测试。

回到玲珑机括,进入 「日志」 页面,你会看到一条新的执行记录,状态从
running 变成 success。点进去能看到实时的 stdout 输出,类似:
[2026-06-29 12:58:12] ========== 开始构建 ==========
[2026-06-29 12:58:12] >>> 通过子容器拉取代码...
[2026-06-29 12:58:12] 已有仓库,执行 pull...
Already up to date.
[2026-06-29 12:58:35] Git 操作完成
[2026-06-29 12:58:35] APP_VERSION=1.0.0
[2026-06-29 12:58:35] BUILD_ENV=production
[2026-06-29 12:58:35] 已加载 Docker 代理: http://192.168.199.11:8001
[2026-06-29 12:58:35] >>> 构建 Docker 镜像...
[2026-06-29 12:59:24] >>> 构建完成,运行验证...
APP_VERSION=1.0.0 BUILD_ENV=production
[2026-06-29 12:59:27] ========== 全部完成 ==========
最后那行 APP_VERSION=1.0.0 BUILD_ENV=production 由容器内打印出来了,这就证明了:webhook 注入的环境变量,经过脚本,成功传进了 Docker 构建出的镜像。

🎉 恭喜!你已经打通了完整的自动化链路。
上面的脚本是「现场装环境」式的——直接在基础镜像里跑构建。如果你的构建流程比较固定
(比如永远需要 Node + pnpm,或者 Go + 交叉编译工具链),更省时的做法是
把这些工具一次性打成一个镜像,之后每次触发只挂载代码运行即可,省去反复安装依赖的时间。
在本地或任意能构建的地方,写一个 Dockerfile,把常用工具装进去:
# ci-builder/Dockerfile
FROM alpine:3.20
RUN apk add --no-cache git openssh docker-cli
# 按需追加:nodejs npm pnpm、go、make、rust ……
WORKDIR /workspace
ENTRYPOINT ["sh"]
构建并推送到你的镜像仓库,或者直接导入到懒猫的内层 Docker:
docker build -t my-ci:latest .
docker run把原来的 test.sh 换成在已装好依赖的镜像里跑:
#!/bin/sh
set -e
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /data/workspace:/workspace \
-e APP_VERSION="${APP_VERSION}" \
-e BUILD_ENV="${BUILD_ENV}" \
-w /workspace/test \
my-ci:latest \
/workspace/test/build.sh # 镜像内真正干活的脚本(构建逻辑放在这里)
好处很直接:
挂载
/var/run/docker.sock是为了让镜像内部能调用宿主的 Docker(即所谓的 out-of-Docker 方案)。
玲珑机括跑在懒猫容器里,脚本中的docker命令默认连的是内层 Daemon,挂载这个 socket
后镜像内也能复用同一套 Docker,避免再起一套 Docker-in-Docker。
如果你用 AI Agent(比如 ZCode、Claude Code 等支持 Skill 的 Agent)来管理懒猫微服,
本程序专门提供了一个 Skill 技能,让 Agent 学习后,能直接帮你写好钩子脚本、
配置好 Webhook 并部署到位——省去手写脚本和在界面里逐字段填写的麻烦。
程序自带一份名为 linglong-webhook-hook-dev 的技能文档(随应用安装时分发)。
它把「玲珑机括的 REST API、Hook 字段规范、触发规则语法、脚本编写约定」全部整理成了
Agent 可读的参考手册。Agent 加载这份技能后,就掌握了:
hc 命令拿 Lzc-Api-Auth-Token 绕过 *.heiyu.space 登录);linglong-webhook-hook-dev。/data/workspace/... 下、配好触发规则和并发策略,最后把 Webhook 地址交还给你。/hooks/<hook_id> 地址填进 Gogs 即可。举几个适合交给 Agent 的活儿:
app 仓库每次 push 到 main 就用 Docker 重新构建并重启服务」;blog 仓库加个自动部署,构建时注入 DEPLOY_TARGET=lzc 环境变量」;一句话总结:你提需求,Agent 写脚本 + 调 API 部署,你只管去 Gogs 贴 webhook 地址。
技能里特别说明了「Webhook 触发端点
/hooks/{hook_id}无需认证、由 Gogs 等直接访问」,
所以 Agent 配出来的 Hook 你拿到地址就能用,不必再处理鉴权。
由于懒猫网关的存在,因此跨应用访问的情况下, Gogs 只能通过 SSH 访问。
把 test.sh 里的克隆段落换成:
# 修正私钥权限(ssh 要求 600),并用 GIT_SSH_COMMAND 指定密钥
chmod 600 /data/workspace/sshkey/id_ed25519
export GIT_SSH_COMMAND="ssh -i /data/workspace/sshkey/id_ed25519 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
REPO_URL="git@<gogs-host>:<用户名>/test.git"
if [ -d "$WORK/test/.git" ]; then
cd "$WORK/test" && git pull
else
git clone "$REPO_URL" "$WORK/test"
cd "$WORK/test"
fi
其余(docker build、环境变量等)保持不变。
Q:日志显示 Permission denied (publickey)(SSH 方式)
A:检查 sshkey/id_ed25519 里的私钥是否完整(含首尾标记行),以及 Gogs 是否已添加配对的公钥。
脚本里已自动 chmod 600,一般不用手动改权限。
Q:构建报错 failed to fetch anonymous token ... i/o timeout(卡在 load metadata)
A:这是 BuildKit 解析基础镜像(FROM alpine:3.20)时连不上 auth.docker.io,国内网络典型问题。
解决:
http://192.168.1.1:7890)并保存,docker build 之前有 source /etc/docker/proxy.env——这是让 BuildKit 继承代理的关键docker pull 生效,build 需要脚本主动加载进环境);FROM alpine:3.20 改成FROM docker.m.daocloud.io/library/alpine:3.20 之类。Q:日志一直 running 最后变成 timeout
A:把 hook 的「超时」调大;网络拉取镜像或克隆大仓库会比较慢。
Q:Gogs 测试推送后,日志页没有新记录
A:检查 Gogs 里 webhook 的「最近交付」记录,看返回的状态码。
若返回 307 或 HTML 登录页,说明地址写错(webhook 地址必须是 /hooks/... 这个公开路径)。
Q:忘记保存 API Key 了
A:只能 SSH 进懒猫微服,清空数据库里的 api_key_hash 记录,重启应用让它重新初始化。
所以——第一次显示时一定要存好。
整个流程串起来其实就三件事:
之后每一次 git push,玲珑机括都会自动拉代码、跑 Docker 构建——你只管推代码,剩下的交给它。
评论
0暂无评论