设计交付自动化里,App Icon 最常翻车的不是审美,而是PNG 实际像素与 Contents.json 声明错位:工程里有时能编过,却在合并分支、归档或上架前才暴露。本文给出可复现流程:在远程 Mac 上用 OpenClaw 把「读 JSON → 量文件 → 出修复报告」做成批量校验闸门,并可接文件夹监听。请先对照《iOS App Icon 交付矩阵与 Contents.json 约定》统一尺寸表与命名,再落地本文脚本。
① 目标与输出物
对 AppIcon.appiconset 中每条带 filename 的 images[] 条目:根据 size(如 60x60)与 scale(如 2x)计算期望像素,与磁盘上 PNG 的真实宽高比对。不一致或缺文件时,写入结构化结果,便于前端与设计师按表修复。产出建议两类:reports/report.json(机读)与 reports/fix_report.md(人读)。同一套规则可被 CI、夜间任务或监听重复调用,减少「口头验收」漂移。
在完整的设计交付自动化链路里,建议把该校验放在「导出落盘之后、入库或打版本 Tag 之前」:fix_report.md 中的文件路径与期望像素可直接贴进工单,研发不必再开 Xcode 逐张对尺寸,协作成本明显下降。
② 安装与配置
- 接入远程 Mac:SSH 登录后确认
python3 --version≥ 3.10(与《OpenClaw 安装与批量校验》一致)。 - Python 依赖:
python3 -m pip install pillow(读 PNG 宽高,跨平台稳定)。 - 目录:例如
~/appicon_jobs/inbox放各版本AppIcon.appiconset,~/appicon_jobs/reports写报告,logs保留 OpenClaw 与脚本 stdout。 - PATH:
launchd或非登录 shell 下在脚本内写python3绝对路径,避免「本机能跑、守护进程找不到解释器」。
③ 运行:批处理校验与报告
将下列脚本存为 ~/bin/validate_appicon.py,对单个 .appiconset 目录执行:python3 ~/bin/validate_appicon.py /path/to/AppIcon.appiconset。脚本打印 FAIL 行并在有错误时以非零退出码结束,便于 OpenClaw 或 CI 判定失败。
#!/usr/bin/env python3
# 校验 AppIcon.appiconset/Contents.json 与 PNG 像素是否一致;输出 JSON + Markdown 报告
import json, sys, os
from pathlib import Path
from PIL import Image
def parse_scale(s):
return int(s.replace("x", "").strip())
def expected_wh(size_str, scale_str):
a, b = size_str.lower().split("x")
m = parse_scale(scale_str)
return int(a) * m, int(b) * m
def main(appiconset):
root = Path(appiconset)
cj = root / "Contents.json"
data = json.loads(cj.read_text(encoding="utf-8"))
rows, errors = [], 0
for img in data.get("images", []):
fn = img.get("filename")
if not fn:
continue
path = root / fn
exp = expected_wh(img["size"], img["scale"])
if not path.is_file():
rows.append({"file": fn, "expected": exp, "actual": None, "ok": False, "reason": "missing"})
errors += 1
continue
w, h = Image.open(path).size
ok = (w, h) == exp
if not ok:
errors += 1
rows.append({"file": fn, "expected": list(exp), "actual": [w, h], "ok": ok})
rep = {"appiconset": str(root), "items": rows, "error_count": errors}
out = Path(os.environ.get("REPORT_DIR", "."))
out.mkdir(parents=True, exist_ok=True)
(out / "report.json").write_text(json.dumps(rep, ensure_ascii=False, indent=2), encoding="utf-8")
lines = ["# App Icon 校验报告", "", f"- 目录: `{root}`", f"- 错误数: **{errors}**", ""]
for r in rows:
if r["ok"]:
continue
lines.append(f"- **{r['file']}**: 期望 {r.get('expected')} 实际 {r.get('actual')} ({r.get('reason', 'size')})")
(out / "fix_report.md").write_text("\n".join(lines), encoding="utf-8")
for r in rows:
if not r["ok"]:
print("FAIL\t", r["file"], r.get("expected"), r.get("actual"), r.get("reason", ""))
sys.exit(1 if errors else 0)
if __name__ == "__main__":
if len(sys.argv) != 2:
print("usage: validate_appicon.py /path/AppIcon.appiconset", file=sys.stderr)
sys.exit(2)
main(sys.argv[1])
批处理示例:对 inbox 下多个套图循环导出报告子目录。可与《OpenClaw PNG 素材质检》中的失败 manifest 思路结合,只重跑未通过批次。
#!/usr/bin/env bash
set -euo pipefail
IN=~/appicon_jobs/inbox
for d in "$IN"/*.appiconset "$IN"/*/AppIcon.appiconset; do
[[ -d "$d" ]] || continue
name=$(basename "$(dirname "$d")")
export REPORT_DIR=~/appicon_jobs/reports/"$name"
mkdir -p "$REPORT_DIR"
python3 ~/bin/validate_appicon.py "$d" || true
done
④ 文件夹监听模板
设计从 Figma / Sketch 导出到共享目录后,可用 fswatch 防抖触发校验(勿监听 iCloud 同步中的临时文件)。忽略 .DS_Store 与 *.tmp。
brew install fswatch
WATCH=~/appicon_jobs/inbox
fswatch -o "$WATCH" | while read -r; do
sleep 20
~/appicon_jobs/batch_validate.sh
done
⑤ OpenClaw 编排要点
把上述脚本封装为 OpenClaw 的固定技能步骤:入参为 appiconset 路径或 job id;成功仅当退出码 0;失败时附带 fix_report.md 路径推送通知。长任务用 tmux 或 launchd,避免 SSH 断开中断批次。设计侧仍负责母版与 sRGB 导出策略;自动化负责「像素与 JSON 合同」——这与规模化设计交付中的职责切分一致。
⑥ 常见报错 FAQ
期望 120×120 实际 180×180 是怎么回事?
多为导出时误用 3× 母版落到 2× 槽位,或 Contents.json 从模板复制后未改 scale。以 JSON 为准重导出对应 PNG,或修正 JSON(须与 Xcode 槽位一致)。
PIL.UnidentifiedImageError
扩展名是 .png 实为 WebP/HEIC,或文件损坏。用 file 命令检查魔数,要求设计导出为真 PNG。
监听触发过于频繁
增大 sleep 防抖秒数,或在导出完成后再移动整文件夹到 inbox,采用「原子落盘」式交接。
OpenClaw 报找不到模块 PIL
远程机多 Python 并存时,用与 OpenClaw 相同的解释器执行 pip install,或在技能脚本里指定该解释器的绝对路径。
限免登录即可打开站内 购买 / 租用、价格与节点,按 帮助中心 接入 SSH,把上述校验与监听固定在专属远程 Mac 上跑,本机专注设计与同步。