玲珑机括上手教程:从安装到用 Gogs 自动构建 Docker 镜像

ety001

发布于23小时前
远程工作,Steem开发者,Steem见证人

玲珑机括(linglong-webhook)是一个轻量级 Webhook 管理平台,兼容 Gogs / Gitea / GitHub / GitLab。
本文带你从零开始:在懒猫微服上安装 → 配置一个测试仓库 → 写一个脚本拉取代码并用 Docker 编译 →
创建 Webhook 把它和 Gogs 推送事件连起来,最终实现「推一下代码就自动构建」。

整个过程大约 20 分钟。


你需要准备什么

  • 一台懒猫微服设备
  • 跑在懒猫上的一个 Gogs 实例(本文以 Gogs 为例,Gitea 同理)

第一步:从懒猫商店安装

在懒猫微服的「应用商店」中搜索 玲珑机括,点击安装。安装完成后打开应用。

https://appstore.lazycat.cloud/#/shop/detail/ink.akawa.ety001.webhook

第二步:保存你的 API Key(只显示一次!)

第一次打开应用时,你会看到一个初始化页面,系统会自动生成一个 API Key

⚠️ 这个 Key 只显示这一次,请务必立即保存!
页面上会有醒目的黄色提示。一旦关闭页面,明文就再也看不到了。
如果日后丢失,只能手动清空数据库里的 api_key_hash 记录让程序重新初始化。

初始化页面,显示 API Key 与「只显示一次」警告

点击「复制」把它存到你的密码管理器里,然后点 「我已保存,进入系统」

进入系统后会看到主面板。之后每次重新打开应用,都会要求你输入这个 API Key 登录。


第三步:在文件管理中准备目录

进入左侧菜单的 「文件管理」。这里的根目录是 /data/workspace
它是持久化目录(懒猫上对应宿主机 /lzcapp/var/workspace),容器重建后文件不会丢。
你的脚本、克隆下来的代码都应该放在这里。

点击右上角 「新建文件夹」,输入 test,回车。

现在你的文件树应该是这样:

/data/workspace/
  └── test/

web 界面没有做删除和修改功能,如果需要可以去懒猫网盘=>应用数据修改


第四步:在 Gogs 上准备测试仓库

4.1 创建仓库

在 Gogs 上新建一个仓库,命名为 test(可以私有)。初始化时勾选生成 README,方便克隆。

4.2 准备克隆地址

我们走 git 协议克隆,地址形如:

git@gogs.xxx.heiyu.space:ety001/test.git

4.2.1 准备密钥

本机生成一对密钥(若还没有):

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""

在玲珑机括的文件管理里,于 /data/workspace 下新建 sshkey 目录,里面新建文件 id_ed25519
私钥完整内容粘贴进去并保存。

image.png

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

image.png

4.3 添加一个简单的 Dockerfile

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 一路传到了镜像内部」。

image.png


第五步:编写构建脚本

回到玲珑机括的文件管理,进入 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 "========== 全部完成 =========="

「保存」

image.png

脚本做的事:用 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。

image.png


第六步:创建 Webhook

进入左侧 「Webhook」 菜单,点 「新建」,按下面的内容填写:

字段填写内容说明
Hook IDtest-buildURL 里的标识,只能用字母/数字/-/_
名称测试构建显示名,随便起
执行命令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 编译过程」的实现方式。

image.png

触发规则(可选,但建议加上,限定只在 push 代码时触发):

📌 规则语法遵循 adnanh/webhook 格式。
这是社区里最通用的 webhook 规则约定,玲珑机括完全兼容。规则由 match / and / or / not
组合而成,数据来源(source)支持 headerpayloadurlrequest-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

image.png

把这个地址复制下来,下一步要用。

✅ 这个 /hooks/... 地址是公开端点,不需要 API Key 也不经过懒猫网关登录鉴权,
所以 Gogs 可以直接访问它。


第七步:在 Gogs 端配置 Webhook

回到 Gogs 的 test 仓库:设置Web 钩子添加 Web 钩子 → 选 Gogs

  • 目标 URL:粘贴上一步复制的 webhook 地址
  • HTTP 方法POST
  • 触发条件:选「自定义事件」,勾选 push
  • 激活:勾选

保存。

image.png


第八步:触发测试

随便在 test 仓库做个改动(比如改一下 README)然后 git push

或者直接在配置界面触发测试。

image.png

回到玲珑机括,进入 「日志」 页面,你会看到一条新的执行记录,状态从
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 构建出的镜像。

日志页面显示构建成功,环境变量已正确传入

🎉 恭喜!你已经打通了完整的自动化链路。


进阶:把常用 CI 流程封装成 Docker 镜像

上面的脚本是「现场装环境」式的——直接在基础镜像里跑构建。如果你的构建流程比较固定
(比如永远需要 Node + pnpm,或者 Go + 交叉编译工具链),更省时的做法是
把这些工具一次性打成一个镜像,之后每次触发只挂载代码运行即可,省去反复安装依赖的时间。

1. 准备一个 CI 镜像

在本地或任意能构建的地方,写一个 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 .

2. 钩子脚本简化为一行 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   # 镜像内真正干活的脚本(构建逻辑放在这里)

好处很直接:

  • 省时:依赖只装一次,后续触发可秒级进入构建;
  • 一致:每次都在同一套环境里构建,告别「我这能跑你那不行」;
  • 干净:宿主 workspace 不会被污染,构建产物都留在挂载目录里。

挂载 /var/run/docker.sock 是为了让镜像内部能调用宿主的 Docker(即所谓的 out-of-Docker 方案)。
玲珑机括跑在懒猫容器里,脚本中的 docker 命令默认连的是内层 Daemon,挂载这个 socket
后镜像内也能复用同一套 Docker,避免再起一套 Docker-in-Docker。


进阶:让 AI Agent 替你写钩子脚本并自动部署

如果你用 AI Agent(比如 ZCode、Claude Code 等支持 Skill 的 Agent)来管理懒猫微服,
本程序专门提供了一个 Skill 技能,让 Agent 学习后,能直接帮你写好钩子脚本、
配置好 Webhook 并部署到位——省去手写脚本和在界面里逐字段填写的麻烦。

这个技能是什么

程序自带一份名为 linglong-webhook-hook-dev 的技能文档(随应用安装时分发)。
它把「玲珑机括的 REST API、Hook 字段规范、触发规则语法、脚本编写约定」全部整理成了
Agent 可读的参考手册。Agent 加载这份技能后,就掌握了:

  • 完整的 Hook 字段含义和取值规范(超时、并发策略、环境变量、触发规则……);
  • REST API 的调用方式(创建 / 更新 / 删除 Hook、查日志、停任务);
  • 懒猫网关鉴权细节(怎么用 hc 命令拿 Lzc-Api-Auth-Token 绕过 *.heiyu.space 登录);
  • 一套可直接复用的 shell 请求模板。

怎么用

  1. 确认 Agent 已加载技能:在本程序的技能列表里应能看到 linglong-webhook-hook-dev
  2. 把信息给 Agent:告诉它你的 Gogs 地址、要构建的仓库、想注入的环境变量。
  3. Agent 自动完成:它会用 REST API 创建 Hook、把生成好的脚本放到
    /data/workspace/... 下、配好触发规则和并发策略,最后把 Webhook 地址交还给你。
  4. 你去 Gogs 配 Webhook:把 Agent 给你的 /hooks/<hook_id> 地址填进 Gogs 即可。

举几个适合交给 Agent 的活儿:

  • 「帮我做一个 webhook:Gogs 的 app 仓库每次 push 到 main 就用 Docker 重新构建并重启服务」;
  • 「给 blog 仓库加个自动部署,构建时注入 DEPLOY_TARGET=lzc 环境变量」;
  • 「查一下最近失败的构建日志,定位原因」。

一句话总结:你提需求,Agent 写脚本 + 调 API 部署,你只管去 Gogs 贴 webhook 地址。

技能里特别说明了「Webhook 触发端点 /hooks/{hook_id} 无需认证、由 Gogs 等直接访问」,
所以 Agent 配出来的 Hook 你拿到地址就能用,不必再处理鉴权。


附录:用 SSH 协议拉取(备选)

由于懒猫网关的存在,因此跨应用访问的情况下, Gogs 只能通过 SSH 访问。

A.2 脚本改用 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,国内网络典型问题。
解决:

  1. 「设置」→「Docker 代理设置」 填上可用的 HTTP/HTTPS 代理(如 http://192.168.1.1:7890)并保存,
    系统会自动重启内层 Docker Daemon
  2. 确认脚本里 docker build 之前有 source /etc/docker/proxy.env——这是让 BuildKit 继承代理的关键
    (代理默认只对 docker pull 生效,build 需要脚本主动加载进环境);
  3. 检查「不走代理的地址」没误拦 docker.io。
    若手边没有代理,可临时用国内镜像源:把 Dockerfile 的 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 记录,重启应用让它重新初始化。
所以——第一次显示时一定要存好。


小结

整个流程串起来其实就三件事:

  1. 文件管理里放好脚本(持久化,不会丢);
  2. Webhook 里把「脚本路径」和「要注入的环境变量」填好;
  3. 把生成的 webhook 地址 配到 Gogs。

之后每一次 git push,玲珑机括都会自动拉代码、跑 Docker 构建——你只管推代码,剩下的交给它。

评论

0

暂无评论

说点什么呢~
收藏
0
0
0