# 一键发布到 OSS 升级源

> 版本要求：**1.1.16 及以上**
>
> ⚠️ **这份文档讲的是「站点后台」的功能，需要 OSS。**
> 站点的管理员在后台点一下按钮，就能把**本站机上**的代码打包发到 OSS 升级源。
>
> 如果你要的是「在电脑上改完代码、发布到下载站」，那是另一回事 ——
> 那条路 **1.1.26 起已经不需要 OSS 了**，见《一条命令发版手册.md》。
>
> | 你想干的事 | 看哪份 | 要 OSS 吗 |
> |---|---|---|
> | 站点后台点按钮，把本机代码发到升级源（给已部署站点更新用） | **本文档** | 要 |
> | 本机改代码，发布到下载站（给别人下载） | 《一条命令发版手册.md》 | **不要** |

## 它是做什么的

把「改完代码 → 打包 → 上传升级源」这三件事合并成一个按钮。点一下，系统自动完成：

```
打包本机改动 → 上传 OSS（avc-releases/）→ 写入本机升级源地址 → 归档历史包 → 校验发布结果
```

之后所有站点的管理员**登录后台就会看到红角标**提示有新版本，点进去即可一键升级。

---

## 前置条件

### 1. 配好 OSS

后台 →「系统设置 · 存储配置」→ 选择 **OSS 存储驱动**，填写：

- `region`（如 `oss-cn-hangzhou`）
- `bucket`
- `accessKeyId` / `accessKeySecret`

### 2. bucket 读权限设为「公共读」

各站点是通过裸 HTTP 拉取升级包的，需要能匿名读取。
（如果配了 CDN，也可以保持私有读 + CDN 回源授权）

### 3. 若用了自定义域名（cname endpoint），必须填 `OSS_CDN_DOMAIN`

这是最容易踩的坑：`ali-oss` 在 endpoint 为自定义域名时**无法推导默认公网域名**，
此时如果没配 CDN 域名，系统**无法生成稳定的公网地址**，发布会被前置检查直接拒绝。

> 为什么宁可拒绝也不放行？因为退而求其次只能生成**签名 URL，而签名 URL 只有 2 小时有效期**。
> 一旦这种地址被写进升级源配置，你的升级源过几小时就会全部失效，而且现象极诡异——
> 今天好用、明天就检查不到更新。系统选择「明确报错」而不是「悄悄埋雷」。

用默认 OSS 域名 + 公共读 bucket 的话，无需配 CDN。

---

## 使用方法

后台 →「系统升级」→ 找到「📤 发布新版本到 OSS」卡片：

| 按钮 | 何时用 |
|---|---|
| **🚀 一键发布（自动比对）** | 日常发版。只打包变动的文件，包体小、下载快 |
| **全量发布** | 升级异常时的兜底。打包整个项目，体积较大 |

点击后页面会实时显示六步进度：

```
发布前检查 → 生成升级包 → 上传到 OSS → 写入升级源 → 归档历史包 → 校验发布结果
```

---

## 几个重要行为

### 无改动时不会瞎打包

如果代码没有变动，脚本会友好退出，任务显示 **「✅ 无变动，未生成新包」**——
这是**成功**状态，不是失败。不会产生一个空包白白占空间。

> 若你确实改了文件却提示无变动，检查一下改动是否落在被排除目录
> （`node_modules/`、`data/`、`upgrade/`、`release/` 等），或改用全量发布。

### 历史版本只归档不删除

每次发布的包会复制一份到项目的 `release/` 目录，便于回滚。
**同名不会覆盖**，会加时间戳后缀。OSS 上的历史包也**从不删除**。

### 升级源地址会被自动改写

发布成功后，本机 `upgrade/source.json` 的 `url` 会被写成 OSS 的公开地址。
如果原来配的是别的源（比如 GitHub Release），页面上会给出**橙色警告**并在点击时二次确认。

### 发布与升级互斥

两者都会改写 `package.json` 版本号和文件指纹库，同时跑会冲突。
所以**有升级任务在跑时不能发布**，反之亦然。

---

## 常见问题

**Q：按钮是灰的，点不动**

按提示依次排查：
1. 「系统尚未安装」→ 先完成安装向导
2. 「OSS 未就绪」→ 去存储配置填写 OSS 信息
3. 「无法生成稳定的 OSS 公网地址」→ 配置 `OSS_CDN_DOMAIN`，或确认 bucket 是公共读

**Q：发布成功但其他站点检查不到更新**

1. 确认 bucket 是**公共读**——浏览器直接打开 `https://你的域名/avc-releases/latest.json` 试试
2. 其他站点的升级源地址要填 `https://.../avc-releases`（**不带** `latest.json`）
3. 检查是否有 CDN 缓存，必要时刷新

**Q：校验步骤提示「无法访问公网 latest.json」**

通常就是 bucket 读权限问题，或 CDN 还没生效。
注意这**只是告警**，包其实已经上传成功了（文件替换和索引都已就绪），不会回滚。

**Q：发布后发现传错了，想撤回**

OSS 上直接删除 `avc-releases/` 下对应对象即可。
但注意 `latest.json` 若还指向那个包，其他站点升级会失败——需要把 `latest.json` 也改回或删掉。
建议直接**重新发布一个正确版本**覆盖，比手工删更安全。

---

## 相关文件

| 路径 | 说明 |
|---|---|
| `server/lib/upgrade/publisher.js` | 发布编排（打包调用、六步流程、护栏） |
| `server/lib/storage/oss.js` | 新增绝对键上传（`uploadFileAbs` / `publicUrlAbs`） |
| `server/lib/upgrade/journal.js` | 任务类型与分组锁（升级/发布互不干扰） |
| `upgrade/source.json` | 本机升级源配置（发布后自动写入） |
| `release/` | 历史升级包归档目录 |
| `scripts/test-publish-integration.js` | 发布功能测试（81 项断言） |
