---
type: tutorial
title: 发布到应用市场
summary: 给你的仓库打标签、让索引器发现并固定它、理解回放自检，并在每次发布时从 CI 自动重新发布。
---

到这篇教程结束时，你的游戏在应用市场里列出，并能被任何用户按它的游戏 id 嵌入。发布有意做得仪式感很低：没有提交队列、没有审核、没有上传。你给一个公开的 GitHub 仓库打标签，平台就索引它。

这假定你已经构建了一个游戏（[构建一个应用市场游戏](/docs/marketplace-game-development/build-a-marketplace-game)）并写了它的 [清单](/docs/marketplace-game-development/the-caputchin-json-manifest)。

## 1. 给 GitHub 仓库打标签

把 GitHub topic `caputchin-game` 加到你的公开仓库（仓库页面上的 About 区）。索引器向 GitHub 查询这个 topic；那个标签就是你的全部选择加入。

你的游戏 id 从仓库坐标 `owner/repo`（或一个 [集合](/docs/marketplace-game-development/the-caputchin-json-manifest#collections) 子项的 `owner/repo/<leaf-dir>`）**派生** 而来。你不挑也不声明它。

## 2. 确认清单要点

三件事把守索引；确保你根部的 `caputchin.json` 有它们：

1. `"terms_accepted": true`（那个字面量布尔）确认 [应用市场提交条款](/legal/submission-terms)。
2. 一个在 [批准清单](/docs/marketplace-game-development/publish-failed-reference#approved-licenses) 上的 `license`。
3. 一个分发指针（`entry`、`npm` 或两者），好让包解析。

如果你想要 [发布失败邮件](#get-notified-when-a-publish-fails)，就加一个 `marketplace.author.email`（它绝不公开显示，而且它是收到它们的唯一办法）。

## 3. 发布或更新

落进索引有两种方式：

- **每日 cron** 自动拾起打了标签的仓库。
- 应用市场上的 **“发布或更新”按钮** 同步地跑逐仓库索引器，并把你重定向到你的详情页，或者显示那个确切的校验错误。第一次列出、或一次清单编辑刚结束后，用这个。

它一跑，索引器就拉取你的清单、**把包钉到一个不可变的 ref**（已发布的 npm 版本或解析出的提交 SHA）、算出一个 SHA-384 完整性哈希，并以你的游戏 id 为键存储 `{ url, integrity }`。新的发布在下一次索引运行时上线，或者经那个按钮即时上线。

## 4. 回放自检

在索引时，平台带一个确定性的种子在一个密封隔离体里把你的 [`run` 工件](/docs/marketplace-game-development/replay-contract) 跑一次：

- **通过** → 你的游戏 **可回放**：一个真实玩家的回合在服务端被重跑以得出那个决定，所以它能给一个站点密钥设关卡。
- **失败**（`run-not-conforming`）→ 游戏显示 **不可回放**。它仍被列出且可嵌入，但只作为 UX，直到你发布一个合规的版本。已经在一个更早的可回放版本上的站点保持跑那个确切的快照。

一次失败的自检几乎总是你 `run` 里的非确定性；[让你的模拟确定性](/docs/marketplace-game-development/publish-failed-fixes#run-not-conforming) 并重新发布。先在本地跑 [引擎套件的](/docs/marketplace-game-development/engine-kit) `selfCheck`，能在你推送之前逮住这个。

## 5. 核实发现

一次成功的发布之后，详情页把你的游戏嵌入一个实时预览里，带一个旋钮对应每个声明的 locale、skin 和 configuration 预设。在那里玩它，以确认这些预设如你所愿地解析。

## 当一次发布失败时收到通知 [#get-notified-when-a-publish-fails]

每日索引器重新检查每个已发布的游戏。如果一次重新索引撞上一个问题，你可以被邮件告知，但 **只有当你通过** 在你的 [清单](/docs/marketplace-game-development/the-caputchin-json-manifest#the-marketplace-block) 里设置 `marketplace.author.email` **选择加入** 时。没设邮件，你就收不到通知。没有别的渠道；这是你本来会错过的那一个信号，因为每日重新检查在你不盯着时跑。

发送两种邮件，匹配那两个结果：

| 邮件 | 何时 | 它意味着什么 |
|---|---|---|
| “`<game>` 的验证检查失败” | 一个重新索引的版本没通过回放自检（`run-not-conforming`） | 游戏保持被列出且可嵌入，但那个版本显示 **不可回放**，且在你发布一个合规的之前无法设关卡。 |
| “我们无法发布 `<game>`” | 一次重新索引撞上任何其他失败（一个清单、许可证或包错误） | 那个版本在你修好它之前 **不被列出**。 |

要紧的细节：

- **只有每日重新索引给你发邮件。** 手动“发布或更新”按钮上的一次失败，会在那一刻就在弹窗里显示给你，所以不为它发邮件。
- **去重。** 同一个游戏上的同一个失败发一次邮件，然后被压制 30 天。一个 *不同的* 失败（一个新的错误码或原因）立即发邮件。
- **随时退出**，办法是移除或更改 `marketplace.author.email`。这些邮件也带一个一键退订指针，指向 [提交条款](/legal/submission-terms)。你在这里不是一个 Caputchin 账户，所以这个清单字段是唯一的控制项。

## 6. 用 CI 自动化

因为发布就是“打标签加推到一个不可变的 ref”，你可以把它接进你的发布流水线，好让每次发布都自动重新索引。那个可靠的套路：

1. **在 CI 里把包发布到一个不可变的 ref**，即你清单的 `npm` / `entry` 据以解析的那个 npm `publish`（或一个打了标签的 GitHub release）。索引器总在它下一次运行时重新解析到你最新发布的版本，所以一个正常的发布流水线已经推进了那个固定。
2. **把发布把守在一次本地确定性检查之上。** 把引擎套件的 `selfCheck`（或你自己的回放测试）作为一个 CI 步骤来跑，好让一个非确定性的 `run` 让构建失败，而不是作为不可回放交付出去。
3. **可选地触发一次即时重新索引**，办法是调用“发布或更新”而不是等每日 cron，如果你想要新版本在 CI 一结束就上线的话。

```yaml
# sketch: release job
- run: npm ci
- run: npm test                 # includes your replay selfCheck
- run: npm publish              # advances the immutable ref the indexer pins
# the daily indexer picks up the new version; or hit Publish or update for instant
```

把那次确定性检查放在发布 **之前**，好让一个坏的 `run` 绝不作为一个默不作声不可回放的列表到达应用市场。

## 你永远不必做什么

- 没有代码签名、没有审核提交、没有平台 CDN 上传。
- 没有分成协议。
- 不挑 id；你的 GitHub 坐标就是那个 id。
- 没有版本字段；索引器钉住那个 ref。

## 如果发布失败

每一个发布路径错误都是你在你仓库里修好并重试的东西。在 [修复一次发布失败](/docs/marketplace-game-development/publish-failed-fixes) 里找你的代码；完整的代码清单是 [发布错误参考](/docs/marketplace-game-development/publish-failed-reference)。

## 另见

- [修复一次发布失败](/docs/marketplace-game-development/publish-failed-fixes)：按错误码一步步修复。
- [caputchin.json 清单](/docs/marketplace-game-development/the-caputchin-json-manifest)：索引器校验的那些字段。
- [回放契约](/docs/marketplace-game-development/replay-contract)：自检跑什么。
