publish —— 从个人自用到上架
读者:做工具的用户,和替他干活的 AI。两者读同一份。
目标
把一个自己在用的包,变成商店里别人能装、能看懂、装完不会一脸问号的包,然后发出去。
发布这个动作不在 CLI 里:CLI 只负责把包检到能发的状态(check --profile publish 零 error),签名与上传由桌面 App(Mac / Windows)工作台的发布面板完成 —— 那里有你的登录身份和签名能力。
前置
- 包在
personal档已经零 error(numable docs first-card走完)。 - 桌面 App 里已登录 —— 未登录发布面板会直接提示「需登录后才能发布」。
- 包是你自己在工作区目录里创作的那份,不是从商店装下来的别人的包。
步骤 1 · 弄清两档差在哪
numable check 有两档。personal 档跳过九段闸,它们全是「不影响包能不能跑,只影响别人能不能用」的东西 —— 靠自觉的结果就是一条都不会做,所以只有闸能挡住。
| 段 | publish 档多要求什么 | 不做的后果 |
|---|---|---|
| G6 组件档位 | 每包 ≥3 个 .xwidget,其中必须有一张 22 档,以及一张 42 或 44 档 |
22 是桌面小组件的最小档,没有就上不了桌面;只有小组件的包在仪表盘上撑不起一屏 |
| G11 / G11b 身份资产 | 包根有 logo.png,512×512 正方形,满幅、自己不烤圆角 |
缺图回落成首字母图标;自己烤了圆角,宿主还会再裁一次,两条弧对不齐,不透明底还会在四角漏出一圈底色 |
| G19 商店门面 | manifest.subtitle 必填且 ≤22 字;manifest.i18n["en-US"].title 与 .subtitle 必备 |
没有副标题,商店行只能长成「名字 + 版本号」;没有英文,英文用户看到的是一列中文 |
| G24 添加组件入口(与 G19 同段) | 添加组件入口是一个按钮(pickWidgets),不是在页面里手抄一份组件目录;别写「已添加」 |
手抄的目录与真组件目录两份真相,新增组件忘了改页面用户就永远加不到;加没加成功只有面板知道,包里那句「已添加」会撒谎 |
| G20 组件标题英文 | 每个 .xwidget 的 i18n["en-US"].title 与 .sub |
英文环境下仪表盘组件标题、组件面板、桌面小组件配置列表这几处显示中文 |
| G21 包名宽度 | 每个语言门的 title 显示宽 ≤24(全角算 2、半角算 1),建议 ≤16;manifest.i18n 结构合法、category 用平台枚举 key |
工具宫格瓦片标题恒两行,超出的部分不是不显示是被省略号吃掉,用户看到一个认不出的半截名字 |
| G8 / G8b 内容双语 | 包里的文案表(.rcn 的 rc.i18n、.xpage / .af / .xform 顶层 i18n)zh-CN 与 en-US 两门 key 齐全;用户可见字段里的中文硬编码提出来做 key |
英文环境下组件上露中文;缺 key 那一处直接渲出字面量 |
| G23 首屏缓存 | 有详情页的包:页面消费的 .df 要有 data.get / data.set 三态缓存(有缓存先渲、后台校新、下拉穿透、失败不覆盖);带缓存的页面根节点要有 events.onRefresh;组件消费的那条 .df 缓存写回必须由入参开关控制且默认关;缓存里不许有凭证 |
首屏每次白等一轮网络;下拉刷新又命中缓存,刷了等于没刷;组件那边刷新意图就是「跳过缓存直取」,数据层再压一层缓存 = 整条刷新链空转 |
| G25 凭证直达入口 | 声明了 required: true 凭证的包,page/ 里要有一处直达 numable://app/mine?section=credentials 的入口 |
用户装完看到一个空组件,不知道去哪里绑密钥;「先加组件再按组件上提示连接」是绕路,不算入口 |
| G27 添加组件按钮版式 | 添加组件按钮用平台基准版式(高度、宽度、形态、颜色都不自定义) | 加组件是平台动作,用户在任何一个包里都该一眼认出它;各包各画的结果是同一个按钮长出六种样子 |
除此之外,两档都跑的闸(结构、白名单闭合、DSL 静默失效、路由与凭证声明)一条不少。完整码表见 numable docs lint-codes。
步骤 2 · 跑全闸,逐条修
命令
numable check hn --profile publish
看到什么算对:目标是 0 error。刚从 personal 档过来时通常长这样:
静态闸 · 档位 publish(上线标准全闸)
── HN 榜首 (hn) ──
✗ [01M200QWNNX1RFPNTX5S7M8PGW] 只有 1 个组件,标准要求 ≥3
✗ [01M200QWNNX1RFPNTX5S7M8PGW] 没有 42/44 档组件(标准要求至少一个宽幅档)
! [01M200QWNNX1RFPNTX5S7M8PGW] 无 logo.png —— 会回落首字母图标
· [01M200QWNNX1RFPNTX5S7M8PGW] 1 组件 · layout[22] · 5KB · net[hn.algolia.com]
✗ check 完成: 2 error / 1 warn
逐条修的顺序建议:
- 补组件(G6)。别把同一个组件换个尺寸交差 —— 宽组件应该多回答一点东西(榜前几条、带趋势条),不是把小组件拉宽。补完每个组件都要重走一遍
run→render→ 三态图。 - 出 logo(G11)。512×512,内容铺满整块画布,四角不要留白也不要自己做圆角。
- 补商店门面(G19 / G21)。
subtitle一句话说清「这个包给我什么」,≤22 字;title短到一行能读完。英文不是把中文机翻一遍,是重写一句英文用户读得懂的。 - 补英文(G20 / G8)。每个
.xwidget一组title/sub;每张.rcn的rc.i18n两门齐平。做法见numable docs localize。 - 首屏缓存(G23)、凭证入口(G25)、添加组件按钮(G24 / G27)—— 只有带详情页 / 带凭证 / 带添加组件入口的包才会碰到。
警告(!)不挡发布,但要逐条看过。有几条警告的含义是「现在能用,英文用户 / 小屏用户看到的不是你以为的样子」。
修完复验两层:
numable run hn
numable render hn --locales zh-CN,en-US
英文那一列渲出来还是中文,说明文案表没接上;组件上出现 ${@i18n.xxx} 字面量,说明那个 key 在这一门里缺了。
发布前 · 把上屏文案再读一遍
check 管的是能不能跑,管不了话说得好不好。发给别人用之前,把组件、页面、提醒里用户看得到的每一句按下面几条过一遍:
- 写给用户,不写给自己。 用户只想知道:发生了什么、对我有什么影响、我能做什么。实现原因(为什么是 30 分钟、哪个接口挂了)不上屏。
- 用用户认得的词。 那个可视单元叫「组件」(英文 widget),不叫「卡片 / card」;你的包对用户来说叫「工具」(英文 tool),不叫「包 / bundle」;「取数 / fetch / host / token / 渲染」这类词换成「获取数据 / 网站 / 更新」。量词用「个」,提醒用「条」。
- 中文用全角标点(
,。:?()),中英文之间加空格(「每 5 分钟」「在 Mac 上」),省略号用…。 - 错误提示写三段:发生了什么 → 你能做什么。写「获取数据失败,请检查网络后重试。」,不写「请求失败 500」;绝不让用户去做开发者动作(看日志、换接口地址)。
- 别承诺做不到的事。 「实时」「每天准时」一句都别写(原因见
numable docs alerts)。 - 英文用 sentence case(只有句首大写),按钮用动词开头;中英各自写成地道的句子,不逐字对译。
步骤 3 · 在 App 工作台里发布
CLI 不发布。到桌面 App(Mac / Windows)→ 工作台 → 找到这个包 → 发布。
面板上有三处要看:
| 项 | 说明 |
|---|---|
| 版本 | 默认勾「发布为 v(下一版)」。同一个 (id, version) 只能发一次,重传会被挡下。内容改了就让它 +1 |
| 分发区域 | 只读,由平台决定:第一次发布进海外(中国大陆以外);要进中国大陆,需要平台审核后设定;以后每一版沿用线上那一版的区域。在中国大陆暂不能发布工具,面板会提示「中国大陆暂不支持发布工具」 |
| 过闸 | 点发布后面板会先跑一遍静态闸,用的就是 --profile publish 那一份规则。有 error 直接挡下并逐条列出来,不会上传 |
上传成功后面板有两种结果:
- 已发布上线 —— 自动通过,已在市场里,所有用户可见。这是常态。
- 已上传,进入待审(draft) —— 对普通用户不可见,经平台审核并盖签名后才上市场。
发布是外发动作:包里每一个字节都会被签名分发到所有用户。发之前确认包里没有密钥、没有本机夹具、没有私人数据 —— .numable/ 目录天然不进包,凭证走 manifest.credentials 声明而不是写在文件里(见 numable docs credentials)。
步骤 4 · 发布之后
包怎么到用户手上
上传时包被封成一个签过名的 .xbundle。用户在商店里点安装 → 客户端下载 → 校验签名与文件哈希 → 解压落盘 → 本地渲染。数据始终是用户设备自己去取的,不经过服务端中转。
更新靠 version +1
App 比对商店里的版本号与已装的版本号,高了才提示更新。manifest.version 不 +1 就重发,等于没发:更新不会被发现,已装的用户永远停在旧版。发新版之前把改动在 run / render 两层都复验一遍 —— 用户装的是包的全部内容,不是你改的那几个文件。
你的源目录和已发布的那一版是两份
你工作区目录里的那份是源,它不会被已发布的版本覆盖,也不会出现在更新红点里(更新只认从商店装下来的包)。所以:
- 继续在本机改、
check/run/render,不影响已经发出去的那一版; - 想让用户拿到改动,必须再发一次(版本 +1);
- 想看用户实际装到的是什么样子,用另一台设备(或另一个账号)从商店装一份来看。
组件的文件名就是它的身份
用户仪表盘上的每个组件,记的是「哪个包 + 哪个 .xwidget 文件名」。新版本删掉或改名了某个 .xwidget,用户盘上引用它的组件会显示「组件已下线」,并在包更新时被清掉。所以组件文件名定下来就别改;要换一个组件,新增一个文件,旧的保留或确认可以让用户失去它再删。
扩大访问范围的版本不会自动装上
用户默认开着「自动更新工具」,但新版本如果比用户已装的版本多访问了网站(manifest.network)、多用了凭证(新的 credentials[].id,或同一个凭证要发往新的网站)、或多了后台任务,就不会静默更新:用户要点更新,在安装面板上看到「这个版本新增:…」逐个确认。只收窄、不扩大的版本会自动装上。所以扩大访问范围的改动,单独发一版,别和用户急着要的修复绑在一起。
版本历史与回退
工作台里包那一行的 ··· → 版本历史,列出这个包发过的每一版和它们的状态。被新版本替换、或你自己撤下的那一版,可以点 回退到此版:它会把那一版的内容作为一个新的版本号重新上线,替换现在线上的版本(应用只接受版本号变大,所以回退也是往前发一版,不是把号改回去)。被平台下架的版本不能回退,要走申诉。
发出去之后还能做什么
工作台里可以自助下架自己已发布的包;被下架或被举报时可以申诉。这些都在工作台的包列表上。
常见错
| 现象 | 原因 | 修法 |
|---|---|---|
| 面板点发布后立刻弹一串红色条目,没上传 | 静态闸有 error | 照条目改;在终端跑 numable check <包> --profile publish 是同一份规则,改起来更快 |
| 提示版本被占用 | 没勾版本 +1,同一个 (id, version) 重传 |
勾上「发布为 v(下一版)」,或先手改 manifest.version |
| 发了新版,用户那边没有更新提示 | manifest.version 没 +1 |
改版本号再发一次 |
| 商店里包名显示成半截 + 省略号 | 标题超过宫格瓦片两行的容量(G21) | 缩短 title,全名放 subtitle |
| 英文环境下商店行 / 组件标题是中文 | manifest.i18n["en-US"](G19)或 .xwidget 的 i18n["en-US"](G20)缺 |
补上;numable docs localize |
| 图标四角有一圈白边 | logo.png 自己烤了圆角、角部垫的是不透明底,宿主再裁一次就漏出来(G11b) |
改满幅,用底板色把四角填平 |
| 装完是一个空组件,用户不知道要绑密钥 | 声明了 required 凭证但页面里没有直达入口(G25) |
首页写编号步骤 + 一个直达按钮 |
| 提示该账号已被限制发布 | 账号在发布黑名单里 | 通过 App 内的意见反馈联系平台 |
| 提示「中国大陆暂不支持发布工具」 | 在中国大陆发布 | 暂不支持,在中国大陆以外发布 |
| 发了新版,用户那边迟迟没有自动更新 | 新版本比已装版本多了网站、凭证或后台任务,要用户逐个确认 | 预期行为;扩大访问范围的改动单独发版 |
| 发了新版,用户盘上某个组件显示「组件已下线」 | 删掉或改名了那个 .xwidget |
别改组件文件名;要换组件就新增文件 |
下一步
- 补齐英文与内容文案表:
numable docs localize - 接需要密钥的数据源(以及凭证的四条红线):
numable docs credentials - 错误码逐条对照:
numable docs lint-codes