feat(gitea-actions): prefer internal images namespace for base images with forced network detection

This commit is contained in:
sunjunzhao committed 2026-10-03 13:30:30 +08:00
1 parent 1b9a40bdc6
commit 5737e9e66a
3 files changed
+98 -4

No files matched your search

+10 -3
View File
@@ -1,6 +1,6 @@
---
name: gitea-actions
description: 按统一规范给项目生成或评审优化 Gitea Actions 构建部署工作流(.gitea/workflows/docker-deploy.yaml):推 v 开头 tag 触发 → buildx 构建推 Gitea Packages → 本机 docker 重建容器 → 清理本机与注册表旧版本;零第三方 action、INNEV_ 前缀变量/密钥、凭证回退链。凡用户要求「给项目加构建工作流 / 加个 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。
description: 按统一规范给项目生成或评审优化 Gitea Actions 构建部署工作流(.gitea/workflows/docker-deploy.yaml):推 v 开头 tag 触发 → buildx 构建推 Gitea Packages → 本机 docker 重建容器 → 清理本机与注册表旧版本;零第三方 action、INNEV_ 前缀变量/密钥、凭证回退链、基础镜像强制网络检测选址(内网 livebook:8418 / 外网 code.innev.cn)并优先 {registry}/images 内网源。凡用户要求「给项目加构建工作流 / 加个 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 — 按统一规范给项目装构建部署工作流
@@ -10,6 +10,7 @@ description: 按统一规范给项目生成或评审优化 Gitea Actions 构建
- **版本号即镜像 tag**:发布 = 打 v 开头 git tag,镜像 tag 与之一一对应,可回溯;推 main 不发版
- **runner 与部署目标同机**:self-hosted 实例级 runner 直连宿主 docker,构建完即本机部署
- **零外部依赖**:不用任何第三方 action,run 步骤直连 git/docker,内网 runner 无需访问 github.com
- **基础镜像内网优选**:FROM 优先 `{registry}/images`(组织已把常用基础镜像同步到该命名空间),未命中回退官方源;拉取前强制网络检测选址——内网 `livebook:8418`、外网 `code.innev.cn`,写死任一地址换网络环境必挂;改写只落在 CI 克隆副本,仓库 Dockerfile 保持任意网络可构建
## 默认流程(不带任何附加提示词也照此执行)
@@ -84,6 +85,7 @@ description: 按统一规范给项目生成或评审优化 Gitea Actions 构建
| B8 | 明文密钥 / clone 日志未 `grep -v` 过滤 token / docker login 未走 `--password-stdin` | ⑦ | 必须修(安全项,优先处理) |
| B9 | 健康检查缺失或窗口与启动模型不匹配(runtime-node 首启数分钟) | ⑧ | 建议修 |
| B10 | 自定义变量/密钥未用 INNEV_ 前缀 | 命名约定 | 建议修 |
| B11 | 缺基础镜像内网优选(无强制网络检测选址步 / FROM 未优先 {registry}/images) | ⑩ | 必须修 |
级别定义:**必须修** = 会挂/泄密/误删邻项目产物;**建议修** = 能跑但稳定性/可维护性受损。
@@ -91,13 +93,13 @@ description: 按统一规范给项目生成或评审优化 Gitea Actions 构建
- 结论一行:当前流程 → 规范流程差距概述(一致则明说"已符合规范")
- 问题表:级别 | 行号 | 问题 | 依据(约束编号) | 修法
- 已过项也要逐条报"已过"(B1-B10 逐条过,漏报和误报一样有害)
- 已过项也要逐条报"已过"(B1-B11 逐条过,漏报和误报一样有害)
报告摊开、用户确认要修 → 转第 2 步给 diff,再进第 3 步验证。用户说"只评审不动手" → 报告即交付物。
## 第 3 步:验证
1. `bash scripts/check-workflow.sh <生成的 workflow 路径>` 规范自检 8 项,全过(SKIP 项人工知悉)
1. `bash scripts/check-workflow.sh <生成的 workflow 路径>` 规范自检 10 项,全过(SKIP 项人工知悉)
2. YAML 语法由脚本第 ⑧ 项覆盖(pyyaml 可用时)
3. 真触发(打 tag 发版)不在授权范围——**显式声明"未真机验证"**,给出手动验证路径:仓库 Actions → Run workflow 单跑 `image` 任务,绿了再全链路
@@ -127,6 +129,7 @@ description: 按统一规范给项目生成或评审优化 Gitea Actions 构建
- ⑦ **密钥全部走 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`,拉取前强制网络检测选址(内网 `livebook:8418` / 外网 `code.innev.cn`),未命中回退官方源不硬失败;FROM 改写只作用 CI 克隆副本**。为什么:内网 runner 直连 docker.io 慢/超时,images 命名空间已同步常用基础镜像,优先内网源又快又稳;livebook:8418 是 LAN 明文、code.innev.cn 是 WAN TLS,写死任一地址换网络环境必挂——必须运行时探测;仓库 Dockerfile 要在任意网络可构建,不能把内网地址烙进源码。
## 凭证与配置
@@ -145,6 +148,9 @@ description: 按统一规范给项目生成或评审优化 Gitea 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 |
| 优选步骤全部显示「内部源未命中」 | images 命名空间未同步该基础镜像 | 在 Gitea images 组织确认/同步对应镜像;流水线自动回退官方源,不阻塞 |
| 优选/构建拉 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 后自然恢复 |
@@ -168,3 +174,4 @@ die 消息统一三段式:是什么 + 为什么(后果) + 怎么办。
- 提交推送代码并写日报(归 push-and-report)
- 手工构建推送业务镜像发版(归 xgent-image-push / xgent-app-release)
- 修改 runtime 基础镜像本身(归 runtime-base 仓库维护)
- 基础镜像同步进 `{registry}/images` 命名空间(组织级镜像维护动作;本 skill 只管流水线侧的优选与回退)
@@ -18,6 +18,8 @@
# 命名约定:自定义变量/密钥一律 INNEV_ 前缀(仓库 → 设置 → Actions 配置,清单见本文件尾部注释)。
# 凭证回退链:INNEV_REGISTRY_USER / INNEV_REGISTRY_TOKEN → gitea.actor / 内置 GITHUB_TOKEN;
# INNEV_CI_TOKEN → GITHUB_TOKEN。加了即生效。
# 基础镜像:拉取前强制网络检测选址(内网 livebook:8418 / 外网 code.innev.cn),FROM 优先
# <registry>/images 命名空间,未命中回退官方源;改写只作用 CI 克隆副本,不动仓库 Dockerfile。
# 镜像 tag = 版本号(git tag,如 v1.2.3)+ latest;手动 dispatch 无 tag 上下文时回退提交短 sha。
name: docker-deploy
@@ -46,6 +48,9 @@ env:
INNEV_HOST_PORT: ${{ vars.INNEV_HOST_PORT || '{{HOST_PORT}}' }}
# 服务器上单独维护的 .env 绝对路径(key 与仓库 .env.example 一致);.env 不进 git/镜像
INNEV_ENV_FILE: ${{ vars.INNEV_ENV_FILE || '/etc/{{PROJECT}}/.env' }}
# 基础镜像内网源 registry——默认空 = 强制网络检测自动选址(内网 livebook:8418 / 外网
# code.innev.cn);显式设置覆盖探测结果。FROM 命中 <该值>/images 即改写为内部引用,缺失回退官方源
INNEV_IMAGES_REGISTRY: ${{ vars.INNEV_IMAGES_REGISTRY || '' }}
jobs:
# 任务 1:构建镜像并推送到本机 Gitea Packages($INNEV_IMAGE_PREFIX/{{PROJECT}})
@@ -66,6 +71,23 @@ jobs:
# 日志开头明示推送目标,变量配错时第一时间可见
echo "构建目标镜像:$PREFIX/{{PROJECT}}:$TAG"
# 从 <registry>/images 拉基础镜像之前必须强制网络检测选址:livebook:8418 是 LAN 明文 HTTP、
# code.innev.cn 是 WAN TLS——写死任一地址,换个网络环境流水线必挂。均不可达则置空,
# 后续优选步整体回退官方源(buildkitd.toml 的 docker.io mirrors 兜底)。
- name: 强制网络检测(基础镜像源选址)
run: |
REG="${INNEV_IMAGES_REGISTRY#*://}"
if [ -n "$REG" ]; then
echo "仓库变量显式指定基础镜像源:$REG"
elif curl -fsS -m 5 http://livebook:8418/api/v1/version >/dev/null 2>&1; then
REG=livebook:8418; echo "内网探测命中,基础镜像源:$REG/images"
elif curl -fsS -m 5 https://code.innev.cn/api/v1/version >/dev/null 2>&1; then
REG=code.innev.cn; echo "外网探测命中,基础镜像源:$REG/images"
else
REG=''; echo "::warning::内网/外网镜像源均不可达,基础镜像全部回退官方源"
fi
echo "INNEV_IMAGES_REGISTRY=$REG" >> "$GITHUB_ENV"
- name: 取出源码(免 actions/checkout,纯 git)
env:
GIT_TOKEN: ${{ secrets.INNEV_CI_TOKEN || secrets.GITHUB_TOKEN }}
@@ -79,7 +101,12 @@ jobs:
env:
LOGIN_USER: ${{ secrets.INNEV_REGISTRY_USER || gitea.actor }}
LOGIN_TOKEN: ${{ secrets.INNEV_REGISTRY_TOKEN || secrets.GITHUB_TOKEN }}
run: echo "$LOGIN_TOKEN" | docker login "$INNEV_REGISTRY" -u "$LOGIN_USER" --password-stdin
run: |
echo "$LOGIN_TOKEN" | docker login "$INNEV_REGISTRY" -u "$LOGIN_USER" --password-stdin
# 基础镜像源与推送 registry 不同端点(如外网 code.innev.cn)时补登录——同一 Gitea 实例,凭证通用
if [ -n "$INNEV_IMAGES_REGISTRY" ] && [ "$INNEV_IMAGES_REGISTRY" != "$INNEV_REGISTRY" ]; then
echo "$LOGIN_TOKEN" | docker login "$INNEV_IMAGES_REGISTRY" -u "$LOGIN_USER" --password-stdin
fi
# buildkitd.toml 里对 $INNEV_REGISTRY 声明 http=true(强制明文 HTTP):buildkitd 不继承宿主
# daemon.json 的 insecure-registries,纯 HTTP registry 不声明会在 push 阶段失败。
@@ -93,10 +120,49 @@ jobs:
echo ''
echo "[registry.\"$INNEV_REGISTRY\"]"
echo ' http = true'
# LAN 明文的基础镜像源与推送 registry 不同端点时补 http 声明(TLS 端点如 code.innev.cn 不需要)
if [ "$INNEV_IMAGES_REGISTRY" = "livebook:8418" ] && [ "$INNEV_IMAGES_REGISTRY" != "$INNEV_REGISTRY" ]; then
echo ''
echo "[registry.\"$INNEV_IMAGES_REGISTRY\"]"
echo ' http = true'
fi
} > buildkitd.toml
cat buildkitd.toml
docker buildx create --use --name {{PROJECT}}builder --config buildkitd.toml
# 基础镜像强制优选内部源:src 副本 Dockerfile 里命中 <registry>/images 的 FROM 改写为内部引用,
# 未命中保持原样(构建时经 buildkitd.toml 的 docker.io mirrors 回退官方源),不硬失败。
# 只改 CI 克隆出的副本,不动仓库 Dockerfile——同一 Dockerfile 在任意网络环境都要可构建。
- name: 基础镜像优选内部源(images 命名空间,缺失回退官方源)
run: |
if [ -z "$INNEV_IMAGES_REGISTRY" ]; then
echo "内部镜像源不可用,跳过优选,基础镜像全部走官方源"
exit 0
fi
cd src
# 构建阶段名(FROM ... AS <name>)不是镜像引用,收集起来跳过
STAGES=$(awk 'toupper($1)=="FROM"{for(i=2;i<=NF;i++) if(toupper($i)=="AS") print $(i+1)}' Dockerfile)
{ grep -niE '^[[:space:]]*FROM[[:space:]]' Dockerfile || true; } | cut -d: -f1 | while read -r N; do
REF=$(awk -v n="$N" 'NR==n{for(i=2;i<=NF;i++){if($i ~ /^--/) continue; print $i; exit}}' Dockerfile)
case "$REF" in ''|scratch|'$'*|*@*) echo "跳过 ${REF:-空引用}(scratch/ARG/digest)"; continue ;; esac
W="${REF#docker.io/}" # 显式 docker.io 前缀视同官方源,纳入优选
# 仅当含 / 时才可能带 registry:首段含 . 或 : 或是 localhost 即视为已带 registry,保持原样
case "$W" in */*) case "${W%%/*}" in *.*|*:*|localhost) echo "跳过 $REF(已带 registry)"; continue ;; esac ;; esac
if echo "$STAGES" | grep -qx "$W"; then echo "跳过 $REF(构建阶段名)"; continue; fi
NAME="${W%%:*}"; if [ "$NAME" = "$W" ]; then TAG=latest; else TAG="${W##*:}"; fi
NAME="${NAME#library/}"
CAND="$INNEV_IMAGES_REGISTRY/images/$NAME:$TAG"
if docker buildx imagetools inspect "$CAND" >/dev/null 2>&1; then
ESC=$(printf '%s' "$REF" | sed 's/[.[\*^$/]/\\&/g')
sed -i "${N}s|$ESC|$CAND|" Dockerfile
echo "优选命中:$REF → $CAND"
else
echo "内部源未命中,保留官方源:$REF"
fi
done
echo '--- 优选后的 FROM 行 ---'
grep -niE '^[[:space:]]*FROM[[:space:]]' Dockerfile || true
# [OPTIONAL:PRE_BUILD] 项目有特殊预构建步骤(如从源码构建工具二进制)时取消注释并改写本步,
# 否则连同标记整块删除。插入位置必须在"构建并推送镜像"步之前。
# - name: 预构建(按项目需要改写)
@@ -269,6 +335,10 @@ jobs:
# host-gateway 访问不通——实测踩坑,切勿写错。
# 7. 日常发布:git tag v1.2.3 && git push origin v1.2.3(tag 需 v 开头才会触发流水线);
# 部署成功后 cleanup 自动删除本机与 Gitea Packages 里的旧版本镜像,无需人工干预。
# 8. 基础镜像内网源:组织已在 Gitea images 命名空间同步常用基础镜像(如 images/golang:1.25-alpine)。
# 构建前强制网络检测选址(内网 livebook:8418 / 外网 code.innev.cn),命中即把 src 副本
# Dockerfile 的 FROM 改写为 <registry>/images/*,未命中自动回退官方源——全自动无需人工干预,
# 只需保证 images 命名空间持续同步需要的基镜像。
#
# 仓库 → 设置 → Actions 配置清单
# 变量(Variables):
@@ -279,6 +349,9 @@ jobs:
# registry.hub.docker.com,docker.1panel.live,docker.1panel.dev,docker.1ms.run
# INNEV_HOST_PORT 容器对外发布端口,默认 {{HOST_PORT}}(容器内固定 {{APP_PORT}})
# INNEV_ENV_FILE 服务器上 .env 的绝对路径,默认 /etc/{{PROJECT}}/.env(见上方准备 6)
# INNEV_IMAGES_REGISTRY 基础镜像内网源 registry——默认空 = 强制网络检测自动选址
# (内网 livebook:8418 / 外网 code.innev.cn);显式设置覆盖探测结果。
# FROM 命中 <该值>/images 即改写为内部引用,缺失回退官方源
# 密钥(Secrets):
# INNEV_REGISTRY_USER docker login 账号——跨命名空间推包时必需(同命名空间可回退 gitea.actor)
# INNEV_REGISTRY_TOKEN docker login token(scope: write:package + read:package);
@@ -84,6 +84,20 @@ else
skip "⑧ python3+pyyaml 不可用,YAML 语法未校验"
fi
# ⑨ 基础镜像源强制网络检测(内网 livebook:8418 / 外网 code.innev.cn 运行时选址,写死必挂)
if grep -q 'INNEV_IMAGES_REGISTRY' "$FILE" && grep -q 'livebook:8418' "$FILE" && grep -q 'code.innev.cn' "$FILE"; then
pass "⑨ 基础镜像源强制网络检测(内外网选址)"
else
bad "⑨ 缺基础镜像源强制网络检测——内网 livebook:8418 / 外网 code.innev.cn 必须运行时探测选址"
fi
# ⑩ FROM 强制优选 <registry>/images 内部源,未命中回退官方源
if grep -q 'INNEV_IMAGES_REGISTRY/images/' "$FILE"; then
pass "⑩ FROM 优选 <registry>/images 内部源(未命中回退官方源)"
else
bad "⑩ 缺 FROM 优选内部源步骤——基础镜像应优先 <registry>/images,未命中回退官方源"
fi
if [ "$fail" -ne 0 ]; then
say "---"
say "规范自检未通过:存在 FAIL 项,逐条修正后重跑。"