# 发布第一条更新

上传手机上的安装包，改一处文字，发布并确认手机收到更新。

## 准备 {#overview}

手机已经安装带 Provider 的 Release App，页面显示 `Pakta demo A`。本页将它变成 `Pakta demo B`，**无需再次安装原生包**。

本例使用独立测试应用，投放比例 100%。生产环境先[灰度发布](/docs/channels)。

## 先判断这次改了什么 {#release-flow}

| 只改 JS、样式和 JS 引用的资源 | 改原生代码、权限、原生依赖或运行时 |
| :---: | :---: |
| **发布热更新** | **发布新安装包** |
| 修改并测试代码 | 修改并测试原生工程 |
| ↓ | ↓ |
| `pakta bundle` 生成 `.ppk` | 构建 Release APK / AAB / IPA / APP |
| ↓ | ↓ |
| 上传更新包 | `pakta uploadApk` / `uploadAab` / `uploadIpa` / `uploadApp` |
| ↓ | ↓ |
| 选择已登记的渠道和原生版本 | 保存同一份安装包，上架或分发给用户 |
| ↓ | ↓ |
| 设置比例，预览并发布 | 后续更新发布到这个原生版本 |
| ↓ | ↓ |
| **已安装匹配原生包的设备检查、下载、生效** | **用户先安装新原生版本** |

首次接入先走右侧，手机安装好后再走左侧。以下按 Android 演示；iOS 和 HarmonyOS 的上传命令在第 2 步列出。

## 1. 确认应用与原生版本 {#setup}

在应用根目录执行，已登录仍可用 `me` 检查身份：

```bash
pakta me
pakta selectApp --platform android
```

后续以 `android`、渠道 `default`、原生版本 `1.0.0` 为例。**把 `1.0.0` 换成设备实际显示的原生版本**，不要改成热更新名称，也不要为了匹配命令去重新构建设备上的包。

## 2. 登记手机上那一份原生包 {#register-native}

APK 路径示例适用于没有 flavor 的标准 Gradle 工程；替换为自己的真实产物路径。

```bash
pakta uploadApk ./android/app/build/outputs/apk/release/app-release.apk
pakta packages --platform android
```

CLI 列表和控制台原生包体页可找到该渠道、原生版本与构建。不要用重新打出的另一份包代替设备安装的构建。

| 格式 | 登记命令 | 说明 |
| --- | --- | --- |
| APK | `uploadApk 路径` | Android 首次本地演练最直接 |
| AAB | `uploadAab 路径` | 对应实际分发构建，留意 splits |
| IPA | `uploadIpa 路径` | 同次归档的 iOS 安装包 |
| Harmony APP | `uploadApp 路径` | DevEco 构建的 `.app` |

渠道来自原生包元数据，缺省 `default`；`--channel` 只能检查是否一致，不能改渠道。CLI 会提取安装包中更新所需的文件。用户收到的是后续发布的 `.ppk` 内容。自定义渠道的文件配置见[配置渠道](/docs/channels#identity)。

## 3. 修改文字，生成更新包 {#bundle}

将 `Pakta demo A` 改为 `Pakta demo B`。此次不改原生依赖、原生版本号、Hermes 或 RN 版本，也不要重新安装 App。

```bash title="应用根目录"
pakta bundle --platform android --output .pakta/output/android.ppk --no-interactive
```

`.pakta/output/android.ppk` 已生成，打包无错误。默认 sourcemap 位于 `.pakta/intermedia/android/index.bundlejs.map`；自定义入口与配置时以打包日志为准。Expo 工程加 `--expo`。

`bundle` 加 `--name` 会直接进入发布，本例不加。

## 4. 预检并发布到测试设备 {#publish}

先替换原生版本、渠道和路径。以下命令采用单行写法，可用于 PowerShell、Bash、zsh：

```bash title="只预检，不发布"
pakta publish .pakta/output/android.ppk --platform android --name demo-b --channel default --packageVersion 1.0.0 --rollout 100 --dryRun --no-interactive
```

预检只解析目标与比例，不代表设备兼容性、上传和最终发布都成功。确认目标后执行：

```bash title="发布到独立测试应用"
pakta publish .pakta/output/android.ppk --platform android --name demo-b --channel default --packageVersion 1.0.0 --rollout 100 --sourcemap .pakta/intermedia/android/index.bundlejs.map --no-interactive
```

保存返回的 `packageId`、`deploymentIds`。在控制台进入该应用的**热更新包**，确认本次更新的目标渠道、原生版本和 100% 比例。也可以[直接在控制台上传并发布](/docs/console#upload)。差分未生成时设备可回退全量下载，不必等待差分任务。

## 5. 在手机上验收 {#verify}

1. **打开原来的 App**：确认是已登记的 Release 安装包，设备联网。
2. **触发检查**：点击 Check update 或回到前台，观察下载事件并等待完成。
3. **彻底关闭再启动**：`silentAndLater` 不会马上改当前页面。下载后结束进程再打开，不卸载、不清除数据。
4. **确认两处变化**：文字变为 `Pakta demo B`，`currentHash` 从 embedded 变为本次更新 hash。
5. **确认健康**：打开关键页面，在[实时数据](/docs/analytics)查看成功或回滚事件，没有异常再继续。

| | 更新前 | 更新后 |
| --- | --- | --- |
| 页面文字 | Pakta demo A | Pakta demo B |
| 原生版本 | 1.0.0 | 1.0.0（不变） |
| currentHash | 空，显示 embedded | 本次更新 hash |

没有看到 B？按[故障排查](/docs/faq#no-update)检查，先确认下载，再判断激活。

## 日常如何发布 {#versions}

只改兼容 JS / 资源时重复第 3–5 步。原生能力变化时，构建分发新原生包并重新登记，再为新原生版本发布更新。

```bash
pakta versions --platform android
```

包内容与 hash 不可替换，名称和说明可以编辑。保留原生安装包、`.ppk`、匹配的 sourcemap 和发布记录。

## 生产异常时怎么恢复 {#rollback}

| 操作 | 尚未更新的设备 | 已经安装问题更新的设备 |
| --- | --- | --- |
| 取消问题目标的发布 | 不再从该投放获取更新 | 不会撤销已安装内容 |
| 缩小灰度比例 | 减少后续命中 | 不会主动回退 |
| 将稳定历史包重新全量投放到原目标 | 可获取稳定内容 | 后续检查与激活可回到稳定包 |

在控制台找到稳定的历史包，按[回滚操作](/docs/console#rollback)重新发布到相同渠道和原生版本。设备下一次检查可收到这个稳定包，下载后按 App 设置生效。

下一步：[渠道与灰度](/docs/channels) · [生产检查清单](/docs/bestpractice)。
