Files
skills/skills/publish-app/scripts/check-publish-workflow.sh
T
sunjunzhao 8abc6a4abf feat: add publish-app skill; rename dev-app to app-dev
- publish-app: convert/add Gitea Actions deploy job to publish TickShield
  apps to the marketplace via aigrego-release (Go static binary) —
  --store auto-publish + --wait review gate; local-first CLI fetch
  (ensure-cli.sh) with minIO platform-matrix download + sha256 verify;
  workflow template and 12-item check script
- rename dev-app to app-dev per app-centric naming convention (no
  tickshield- prefix), documented in AGENTS.md and README
- app-dev boundary routes CI publish workflow work to publish-app;
  both skills are project-level installs only
2026-10-19 16:38:22 +08:00

142 lines
5.6 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# publish-app 发布工作流规范自检(零依赖:bash + grep + sed;YAML 语法解析优先 python3,缺失则 SKIP)。
# 用法:bash check-publish-workflow.sh <workflow 路径>
# 退出码:任一 die 项 FAIL → 1;仅 WARN/SKIP → 0。
set -u
WF="${1:-}"
if [ -z "$WF" ] || [ ! -f "$WF" ]; then
echo "用法: bash $0 <workflow 路径>" >&2
exit 2
fi
ROOT="$(cd "$(dirname "$WF")/../.." && pwd)"
MANIFEST="$ROOT/app.manifest.json"
FAIL=0
# 多数检查针对剥掉注释后的正文(模板里的禁令注释本身含敏感字样,不能误报)
CODE="$(mktemp)"
trap 'rm -f "$CODE"' EXIT
sed 's/#.*$//' "$WF" > "$CODE"
die() { echo "FAIL(die) $1"; FAIL=1; }
ok() { echo "PASS $1"; }
wrn() { echo "WARN $1"; }
skp() { echo "SKIP $1"; }
echo "== check-publish-workflow: $WF =="
# ① --wait 必须出现在 publish 命令里
if grep -q 'publish' "$CODE" && grep -q -- '--wait' "$CODE"; then
ok "① publish 带 --wait(202 进审核退出码仍为 0,靠它门禁)"
else
die "① publish 缺 --wait ——治理变更 202 进审核但流水线绿,商店不生效(加 --wait 600)"
fi
# ② 密钥卫生:APP_KEY/APP_SECRET 只经 ${{ secrets.* }} 引用(兼容 shell `=` 与 YAML `:` 两种写法);docker login 走 --password-stdin
if grep -nE 'APP_(KEY|SECRET)[=:]' "$CODE" | grep -vE 'secrets\.APP_(KEY|SECRET)' | grep -q .; then
die "② APP_KEY/APP_SECRET 疑似明文/硬编码 ——密钥只走 \${{ secrets.* }} env 引用"
elif grep -q 'docker login' "$CODE" && ! grep -q -- '--password-stdin' "$CODE"; then
die "② docker login 未走 --password-stdin ——密码进 argv 会留在日志与进程列表"
else
ok "② 密钥引用卫生(secrets 引用 / password-stdin)"
fi
# ③ CLI 获取:禁旧分发仓与 .mjs(平台已显式删除);须按平台矩阵走 TICKSHIELD_RAW_BASE
if grep -q 'tickshield-dist' "$CODE"; then
die "③ CLI 地址抄了已停用的 code.innev.cn tickshield-dist 旧仓 ——改用 TICKSHIELD_RAW_BASE + cli/aigrego-release-<os>-<arch>"
elif grep -q 'aigrego-release\.mjs' "$CODE"; then
die "③ 引用了 aigrego-release.mjs ——Go 重写后平台已显式删除该物料,引用必 404/拿到不更新的旧版"
elif grep -q 'TICKSHIELD_RAW_BASE' "$CODE"; then
ok "③ CLI 按平台矩阵走 TICKSHIELD_RAW_BASE"
else
wrn "③ 未见 TICKSHIELD_RAW_BASE 变量 ——若 CLI 为本地预装/vendored 二进制,确认随平台发版更新(对照 cli/sha256sums.txt)"
fi
# ④ publish 必须带 --manifest(image/dist 不可单飞)
if grep -q -- '--manifest' "$CODE"; then
ok "④ publish 带 --manifest(CLI 内 manifest 先行,避免 409 submit a manifest first)"
else
die "④ publish 缺 --manifest ——无生效版本时单独 --image 会 409"
fi
# ⑤ micro dist:CI 产物无法静态预检则提示
if grep -q -- '--dist' "$CODE"; then
if grep -q 'index.html' "$CODE"; then
ok "⑤ 含 dist 包根 index.html 防线(CI 内 test -f)"
else
wrn "⑤ --dist 已配但 workflow 无 index.html 包根自检 ——确认传的是产物目录本身(tar -C dir . 打包,套层即 400)"
fi
else
skp "⑤ 无 --dist(非 micro 或未上传产物)"
fi
# ⑥ listingKey 不另配
if grep -Eq -- '--key |LISTING_KEY=' "$CODE"; then
wrn "⑥ 出现 --key/LISTING_KEY 另配 ——listingKey 应从 app.manifest.json 取(另配易 403 mismatch)"
else
ok "⑥ listingKey 未另配(单一事实源 = manifest)"
fi
# ⑦ 旧「本机重建容器」部署段残留(改造模式)
if grep -Eq 'docker rm -f|compose up -d|docker-compose up|docker run[^|]* -d ' "$CODE"; then
wrn "⑦ 疑似残留本机重建容器部署段 ——部署权责已移交平台编排,确认是有意保留并进汇报"
else
ok "⑦ 无旧部署段残留"
fi
# ⑧ 触发:推 main 不发版
if grep -q 'branches:' "$CODE"; then
wrn "⑧ 触发含 branches ——确认发版只走 v* tag(推 main 不发版)"
else
ok "⑧ 触发不含 branches(v* tag 模型)"
fi
# ⑨ --store 必须出现(否则永不上架,本 skill 目的落空)
if grep -q -- '--store' "$CODE"; then
ok "⑨ publish 带 --store 自动上架"
else
die "⑨ publish 缺 --store ——不会上架,发布目的落空"
fi
# ⑩ manifest 侧静态预检(仓库根有 manifest 时)
if [ -f "$MANIFEST" ]; then
if grep -q '"deployDescriptor"' "$MANIFEST"; then
die "⑩ app.manifest.json 含 deployDescriptor ——平台管理员专属字段,提交必 403"
elif ! grep -Eq '"listingKey"[[:space:]]*:[[:space:]]*"[a-z0-9][a-z0-9-]{0,62}"' "$MANIFEST"; then
die "⑩ listingKey 缺失或不符 ^[a-z0-9][a-z0-9-]{0,62}$"
else
ok "⑩ manifest 静态预检(无 deployDescriptor、listingKey 合法)"
fi
else
skp "⑩ 未找到 ${MANIFEST}(非仓库根执行?)"
fi
# ⑪ YAML 语法(python3 + pyyaml 可用时)
if command -v python3 >/dev/null 2>&1; then
python3 - "$WF" <<'PY' >/dev/null 2>&1
import sys
try:
import yaml
except ImportError:
sys.exit(3)
yaml.safe_load(open(sys.argv[1]))
PY
rc=$?
if [ "$rc" -eq 0 ]; then ok "⑪ YAML 语法解析通过"
elif [ "$rc" -eq 3 ]; then skp "⑪ python3 无 pyyaml,语法未检"
else die "⑪ YAML 语法解析失败"
fi
else
skp "⑪ 无 python3,语法未检"
fi
# ⑫ 新建模式模板槽位不得残留({{SLOT}} 无 $ 前缀,与 Gitea 表达式 ${{ }} 区分)
if grep -Eq '\{\{[A-Z_]+\}\}' "$CODE"; then
die "⑫ 模板槽位 {{...}} 未渲染 ——渲染后不得残留(Gitea 表达式是 \${{ }},不受影响)"
else
ok "⑫ 无未渲染的 {{SLOT}} 残留"
fi
echo "== done =="
exit "$FAIL"