17 KiB
name, description
| name | description |
|---|---|
| gitea-actions | 按统一规范给项目生成或评审优化 Gitea Actions 构建部署工作流(.gitea/workflows/docker-deploy.yaml):推 v 开头 tag 触发 → buildx 构建推 Gitea Packages → 本机 docker 重建容器 → 清理本机与注册表旧版本;零第三方 action、INNEV_ 前缀变量/密钥、凭证回退链、基础镜像强制网络检测选址(内网 livebook:8418 / 外网 code.innev.cn),FROM 未命中 {registry}/images 时自动拉官方源推送落盘后再改写。不带任何输入调用本 skill = 审查当前项目 CI/CD 是否符合本规范。凡用户要求「给项目加构建工作流 / 加个 CI / 配发版流水线 / 加 Gitea Actions / 生成 docker-deploy.yaml / 打 tag 自动构建部署 / 这个项目怎么自动发版 / 看看这个 workflow 规范不规范 / 帮我评审一下这个流水线」,或出现 推了 tag Actions 没动静 / workflow 不触发 / push 镜像 denied / unauthorized / 健康检查超时 / 拉基础镜像超时 等症状,或提到 .gitea/workflows / workflow_dispatch / buildx / Gitea Packages / self-hosted runner / INNEV_ 变量 时,务必使用本 skill——即使用户没说「Gitea Actions」这几个字。Use whenever the user wants to add, review, or standardize a Gitea Actions build/deploy workflow for a project. 边界:写/改 Dockerfile 用 runtime-image;提交推送代码+写日报用 push-and-report;手工推镜像发版用 xgent-image-push。 |
gitea-actions — 按统一规范给项目装构建部署工作流
把实战中固化下来的 docker-deploy 流水线规范安装到任意项目。核心理念(推理链,所有约束都挂在这上面):
- 版本号即镜像 tag:发布 = 打 v 开头 git tag,镜像 tag 与之一一对应,可回溯;推 main 不发版
- runner 与部署目标同机:self-hosted 实例级 runner 直连宿主 docker,构建完即本机部署
- 零外部依赖:不用任何第三方 action,run 步骤直连 git/docker,内网 runner 无需访问 github.com
- 基础镜像内网落盘:FROM 命中
{registry}/images直接改写为内部引用;未命中则由流水线把官方源镜像拉下来、推送进 images 命名空间后再改写——新基础镜像一次构建即落盘,后续构建全部内网命中、越构建越快。拉取/推送失败回退官方源不硬失败;拉取前强制网络检测选址——内网livebook:8418、外网code.innev.cn,写死任一地址换网络环境必挂;改写只落在 CI 克隆副本,仓库 Dockerfile 保持任意网络可构建
默认流程(不带任何附加提示词也照此执行)
用户不带任何附加输入调用本 skill = 审查当前项目 CI/CD 是否符合本规范:走评审优化模式的只读审计——已有 workflow 逐条对照审计清单;连 .gitea/workflows/ 都没有则如实报告"未配置 CI/CD"并给出新建建议,不默认动手生成。
带明确意图输入(如"加个 CI""生成 workflow")时,检测项目是否存在 .gitea/workflows/ 下的工作流文件(yml/yaml),自动分流:
- 不存在 → 新建模式:按规范从零生成
docker-deploy.yaml - 已存在 → 评审优化模式:对照规范逐条审计,差异摊开,用户确认后按规范改写优化(默认不直接覆盖)
总流程
第 0 步 探测项目(绝不可跳过)→ 第 1 步 槽位定参 → 第 2 步 生成/改写(确认闸)
→ 第 3 步 验证 → 第 4 步 汇报
除非用户明确只要聊聊思路,完整走全部 5 步。
第 0 步:探测项目(绝不可跳过)
不许凭用户一句话生成——读项目文件坐实,缺料就停下来问,不猜、不翻别的项目抄。
| 探测项 | 怎么探 | 判定/用途 |
|---|---|---|
| 已有 workflow(最先做,决定模式) | glob .gitea/workflows/*.yml、.yaml |
有 → 评审优化模式;无 → 新建模式 |
| 项目名 | git remote basename → package.json name → 目录名,用户确认 | {{PROJECT}}:镜像名/容器名/builder 名 |
| Dockerfile | glob 项目内 Dockerfile* |
无 → die:先走 runtime-image 生成 Dockerfile——流水线对着空气构建无意义 |
| 启动模型 | 读 Dockerfile 的 FROM | runtime-node 系(容器启动时 install+build)→ 健康检查窗口放宽 ~7.5 分钟;预构建产物 → 窗口可缩短 |
| 端口 | Dockerfile EXPOSE / 项目配置 / 问用户 | {{APP_PORT}} 容器内端口 + {{HOST_PORT}} 对外端口默认值 |
| 健康检查端点 | grep 代码里 /health、/healthz、/api/health 等路由 |
有 → {{HEALTH_PATH}};无 → warn:不虚构,生成时整块省略健康检查步,汇报大字声明 |
| 必需 .env key | 读 .env.example |
渲染 deploy 步的 env key 检查行;无 .env.example → 整块省略 |
| 特殊预构建 | 问用户(如从源码构建工具二进制) | 对应模板 [OPTIONAL:PRE_BUILD] 块,插入"构建并推送镜像"步之前 |
| 额外运行参数 | 读 Dockerfile/运行时要求(如 runtime-node 的 APP_ENTRY)、.env 是否引用宿主主机名 |
对应 [OPTIONAL:EXTRA_ENV] / [OPTIONAL:ADD_HOST] 块 |
→ 验收:模式(新建/评审优化)有 glob 结论背书;每个槽位有文件证据(路径)或用户确认,拿不准的在生成前问清。
第 1 步:槽位定参
读 references/docker-deploy-template.yaml(模板即规范,含全部机制与尾部人工准备清单),把探测结论映射到槽位:
| 槽位/可选块 | 来源 | 无对应值时 |
|---|---|---|
{{PROJECT}} / {{APP_PORT}} / {{HOST_PORT}} / {{HEALTH_PATH}} |
第 0 步探测 | 前三个必给(问用户);HEALTH_PATH 无 → 删 [OPTIONAL:HEALTH_CHECK] 整块 |
[OPTIONAL:ENV_KEY_CHECKS] |
.env.example 渲染,每个必需 key 一行 grep 检查 |
整块删除 |
[OPTIONAL:PRE_BUILD] |
用户确认的特殊预构建步骤 | 整块删除 |
[OPTIONAL:EXTRA_ENV] / [OPTIONAL:ADD_HOST] |
运行时/依赖拓扑要求 | 整块删除 |
槽位写作 {{SLOT}}(无 $ 前缀),与 Gitea 表达式 ${{ ... }} 区分;渲染后模板里不应残留任何 {{...}}。
→ 验收:槽位对照表逐项有值或有"整块删除"结论,摊开给用户过目。
第 2 步:生成/改写(确认闸)
默认动作:先把渲染后的 workflow 全文摊给用户确认,再落盘到 .gitea/workflows/docker-deploy.yaml;改写已有文件时给 diff。已有 workflow 里与规范不冲突的定制(额外 job、项目专用步骤)保留,并在汇报里列出。
→ 验收:用户确认后文件落盘;保留的定制逐项在汇报出现,不静默丢弃(红线)。
评审优化模式(第 0 步发现已有 workflow 时走此分支)
先做完第 0、1 步得出"应该长什么样",再拿现状逐条对照审计。只读审计,审计报告出来之前不改任何文件。
审计清单(依据列挂着硬约束编号)
| # | 检查项 | 依据 | 级别 |
|---|---|---|---|
| B1 | 出现 uses:(第三方 action) |
① | 必须修 |
| B2 | 触发不是 push v* tag(如推 main 即构建部署) | ② | 必须修 |
| B3 | 缺 workflow_dispatch job 单跑选项 | ② | 建议修 |
| B4 | buildx 未经 buildkitd.toml 注入 registry http=true + docker.io mirrors |
③ | 必须修 |
| B5 | 凭证写死,或无 INNEV_* → gitea.actor/GITHUB_TOKEN 回退链 |
④ | 必须修 |
| B6 | cleanup 全局 prune,或容器 name 过滤无 ^...$ 锚点 |
⑤ | 必须修 |
| B7 | 镜像只打 latest、无版本 tag | ⑥ | 必须修 |
| B8 | 明文密钥 / clone 日志未 grep -v 过滤 token / docker login 未走 --password-stdin |
⑦ | 必须修(安全项,优先处理) |
| B9 | 健康检查缺失或窗口与启动模型不匹配(runtime-node 首启数分钟) | ⑧ | 建议修 |
| B10 | 自定义变量/密钥未用 INNEV_ 前缀 | 命名约定 | 建议修 |
| B11 | 缺基础镜像同步落盘(无强制网络检测选址步 / FROM 未命中 {registry}/images 时未先拉取推送落盘再改写、仅被动回退官方源) | ⑩ | 必须修 |
级别定义:必须修 = 会挂/泄密/误删邻项目产物;建议修 = 能跑但稳定性/可维护性受损。
审计报告格式(固定)
- 结论一行:当前流程 → 规范流程差距概述(一致则明说"已符合规范")
- 问题表:级别 | 行号 | 问题 | 依据(约束编号) | 修法
- 已过项也要逐条报"已过"(B1-B11 逐条过,漏报和误报一样有害)
报告摊开、用户确认要修 → 转第 2 步给 diff,再进第 3 步验证。用户说"只评审不动手" → 报告即交付物。
第 3 步:验证
bash scripts/check-workflow.sh <生成的 workflow 路径>规范自检 10 项,全过(SKIP 项人工知悉)- YAML 语法由脚本第 ⑧ 项覆盖(pyyaml 可用时)
- 真触发(打 tag 发版)不在授权范围——显式声明"未真机验证",给出手动验证路径:仓库 Actions → Run workflow 单跑
image任务,绿了再全链路
完成定义 = 规范自检全过 + 用户拿到可执行的首次发布步骤;不是"文件写完了"。
→ 验收:check-workflow.sh 输出全 PASS;未验证项在汇报显式声明。
第 4 步:汇报
固定结构:
- 模式:新建 / 评审优化
- 探测结论:项目名 / Dockerfile / 启动模型 / 端口 / 健康端点 / env key(附证据文件路径)
- 【评审优化】审计结果:必须修 N 条 / 建议修 N 条 / 已过 N 条(附问题表),用户是否授权修正
- 写了什么:workflow 文件路径(新建 / 修改 diff);保留的项目定制清单
- 被跳过/未做的:如无健康端点省略健康检查步、无 .env.example 省略 key 检查
- 验证做到哪一步:check-workflow.sh 结果;真触发没做就说没做
- 待人工补:runner 实例级登记 / 宿主 daemon.json insecure-registries 登记并重启 docker / 仓库 Units 启用 Actions / 服务器按 .env.example 建 .env / 仓库 Variables+Secrets 配置(清单在生成的 workflow 尾部注释)/ 首次打 tag 发布
硬约束
- ① 零第三方 action。为什么:self-hosted runner 在内网,访问不了 github.com,
uses:一出现 workflow 直接跑不起来;run 步骤直连 git/docker 即可。 - ② 仅 push v tag 触发全链路,推 main 不触发*。为什么:版本号即镜像 tag 的模型——每次提交都发版会让 Packages 堆满无版本意义的镜像,且无法回溯;手动兜底用 workflow_dispatch 的 job 选项单跑。
- ③ buildx 必须经 buildkitd.toml 注入 registry
http=true与 docker.io mirrors。为什么:docker-container 驱动的 buildkitd 不继承宿主 daemon.json 的 insecure-registries/registry-mirrors——不声明 http=true 推送阶段必失败,不注入 mirrors 拉基础镜像超时。 - ④ 凭证回退链
INNEV_REGISTRY_USER/INNEV_REGISTRY_TOKEN→gitea.actor/GITHUB_TOKEN,INNEV_CI_TOKEN→GITHUB_TOKEN。为什么:内置 token 同命名空间够用,配了 INNEV_* 即覆盖生效——不强制每个仓库都配密钥,也不许把凭证写死。 - ⑤ cleanup 只动本项目名下的镜像 tag 与
^<项目名>$锚点过滤的容器。为什么:部署机上还跑着其他项目的镜像与容器,全局 prune 或无锚点过滤(名字包含项目名的 buildx_buildkit_* 容器)就是误删事故。 - ⑥ 镜像 tag = git tag 版本号 + latest,禁 latest-only。为什么:latest 无法回溯版本;部署出问题想回退时,没有版本 tag 就没有可拉的历史镜像。
- ⑦ 密钥全部走 secrets 引用,clone 日志
grep -v "$GIT_TOKEN"过滤,docker login 走--password-stdin。为什么:明文 token 进 workflow 文件 = 进 git 历史;密码进 argv 会留在日志与进程列表。 - ⑧ 健康检查窗口按启动模型放宽。为什么:runtime-node 系容器首启要 install+build 数分钟,窗口太短会把正常启动当失败杀掉(spms 实测放宽到 ~7.5 分钟);项目没有健康端点时不虚构——省略该步并显式声明。
- ⑨ 生成/改写前先摊全文或 diff 给用户确认。为什么:workflow 直接决定发布行为,未经确认的写入超出授权;默认动作永远是"不写"。
- ⑩ 基础镜像强制落盘内部源
{registry}/images:命中直接改写 FROM;未命中先docker pull官方源 →docker push到{registry}/images落盘 → 再改写 FROM;仅拉取/推送失败才回退官方源(告警不硬失败)。拉取前强制网络检测选址(内网livebook:8418/ 外网code.innev.cn);改写只作用 CI 克隆副本。为什么:内网 runner 直连 docker.io 慢/超时——新基础镜像首次构建即同步落盘,后续构建全部命中内网源、越构建越快,不再依赖组织提前手工同步;livebook:8418 是 LAN 明文、code.innev.cn 是 WAN TLS,写死任一地址换网络环境必挂——必须运行时探测;同步失败只告警回退,不让镜像源问题阻塞发版;仓库 Dockerfile 要在任意网络可构建,不能把内网地址烙进源码。
凭证与配置
本 skill 自身无外部凭证。生成的 workflow 引用的 INNEV_* 变量/密钥由人在 Gitea「仓库 → 设置 → Actions」里配置(配置清单自动生成在 workflow 尾部注释里,照单配即可)。用户在对话中贴过来的真实令牌/密钥:不落进任何文件,并提醒轮换。
人机权责
- 写操作:生成/覆盖
.gitea/workflows/docker-deploy.yaml——默认摊全文确认后落盘。 - 终态是人的按钮:首次发版(
git tag v0.1.0 && git push origin v0.1.0)、仓库 Variables/Secrets 配置、服务器 .env 创建,agent 一律不执行。 - 待人工补:runner 登记、宿主 daemon.json insecure-registries、仓库 Units 启用 Actions、数据库迁移(如适用)——落成清单交付,不绕道、不假装完成。
排障对照表
| 症状 | 原因 | 怎么办 |
|---|---|---|
| 推 tag 后 Actions 没动静 | 仓库未启用 Actions Units / tag 不是 v 开头 | 仓库设置启用 Actions;tag 改 v 开头重打 |
| 镜像 push 阶段失败(http 相关报错) | buildkitd.toml 未声明 http=true(buildkitd 不继承宿主 insecure-registries) |
按约束③补齐注入(本规范模板已含) |
| 构建拉基础镜像超时 | docker-container 驱动不继承宿主 registry-mirrors | 经 buildkitd.toml 注入 INNEV_DOCKERHUB_MIRRORS |
| 同步步反复出现「同步失败」告警 | 宿主 pull 官方源超时(mirrors 未配)/ push 到 images 被拒(token 缺 write:package 或对 images 组织无写权限 / 宿主 daemon.json 未登记 livebook:8418 insecure-registries) | 配宿主 registry-mirrors;确认 INNEV_REGISTRY_TOKEN scope 与 images 组织写权限、daemon.json 登记并重启 docker——同步失败只告警回退官方源,不阻塞构建 |
| 同步/构建拉 livebook:8418 报 http 相关错误 | buildkitd.toml 缺该 registry 的 http=true 声明 | 模板已含条件注入;旧 workflow 按约束③⑩补齐 |
| 外网 runner 检测命中 code.innev.cn 但拉取失败 | token 失效或 runner 无外网 | 确认 INNEV_REGISTRY_TOKEN 有效(同一 Gitea 实例内外网凭证通用)、runner 可出外网 |
部署机 docker pull 报 HTTP response to HTTPS client |
宿主 daemon.json 未登记 LAN 明文 registry | 登记 insecure-registries 并重启 docker(人工),或前缀改 TLS 地址 |
| 健康检查超时/失败 | runtime-node 首启 install+build 数分钟窗口太短;或 .env 缺 key | 按约束⑧放宽窗口;看容器日志确认缺哪个 key |
| cleanup 删注册表版本 403 | token 缺 write:package scope | 该步 best-effort 只告警不阻塞;补 scope 后自然恢复 |
| 容器内连宿主数据库 5432 不通 | 容器经 host-gateway 访问不到宿主系统服务端口 | .env 改指同机 docker 发布端口(如 5433)——实测踩坑 |
| git clone 源码失败 | INNEV_CI_TOKEN 缺失或 scope 不足 | 配 INNEV_CI_TOKEN(read:repository),或确认内置 GITHUB_TOKEN 够用 |
die 消息统一三段式:是什么 + 为什么(后果) + 怎么办。
红线(任何情况下不违反)
- 不替用户打 tag、不替用户触发首次发布——终态是人的按钮
- 不往任何文件里写真实密钥/令牌;用户贴过来的提醒轮换
- 不静默丢弃已有 workflow 的项目定制——差异必须摊开、保留项必须进汇报
- 不虚构健康检查端点——探测不到就省略该步并显式声明,不写个
/api/health假装有 - 不擅自加第三方 action——用户坚持要加时,先复述约束①的后果再让其拍板
- 不静默截断——被省略的可选块、被跳过的检查必须出现在汇报里
边界:这些不归本 skill 管
- 写/改/评审项目 Dockerfile(归 runtime-image;本 skill 第 0 步发现没有 Dockerfile 时指路过去)
- 提交推送代码并写日报(归 push-and-report)
- 手工构建推送业务镜像发版(归 xgent-image-push / xgent-app-release)
- 修改 runtime 基础镜像本身(归 runtime-base 仓库维护)
{registry}/images命名空间的组织级批量同步与清理(本 skill 让流水线在构建时对未命中的 FROM 按需同步落盘,日常构建即自然积累;批量预同步/下架清理不归本 skill)