fix(V0.2): OTA 发布说明改为 commit message, sha 单独展示; 版本附构建时间
Docker Build and Push / build-image (push) Successful in 1m2s
Docker Build and Push / build-image (push) Successful in 1m2s
- CI: notes 取 git log -1 的 commit message (标题+正文, 截断 400 字), commit sha 经 --commit 写入 manifest (不再把裸 sha 塞进发布说明) - build-ota: 新增 --commit 参数; latest.json 增加 commit 字段 - 服务端 /ota/check 与 /manifest-web 透传 commit 与 sequence - 我的页: 当前版本/新版本后附 seq 时间戳 (MM-dd HH:mm), 发布说明下方新增「提交」行显示短 sha
This commit is contained in:
@@ -85,13 +85,16 @@ jobs:
|
||||
|
||||
- name: Build & sign OTA package
|
||||
# 每次主分支提交生成 OTA 升级包, sequence 默认时间戳 (单调递增)
|
||||
# 发布说明 = 触发构建的 commit message (标题+正文, 截断 400 字),
|
||||
# commit sha 通过 --commit 单独写入 manifest, 前端分行展示
|
||||
# 产物 ota-package/ (tar.gz + latest.json + .minisig) 进入 docker build context, 由 Dockerfile COPY 进镜像
|
||||
env:
|
||||
MINISIGN_PRIVATE_KEY: ${{ secrets.MINISIGN_PRIVATE_KEY }}
|
||||
OTA_NOTES: ${{ github.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node scripts/build-ota.mjs --out ota-package --notes ${OTA_NOTES} --min-binary ${OTA_MIN_BINARY}
|
||||
MSG="$(git log -1 --pretty=format:%B | sed -e '/^[[:space:]]*$/d' | head -c 400)"
|
||||
echo "notes: ${MSG}"
|
||||
node scripts/build-ota.mjs --out ota-package --notes "${MSG}" --commit "${GITHUB_SHA}" --min-binary ${OTA_MIN_BINARY}
|
||||
echo "===== latest.json ====="
|
||||
cat ota-package/latest.json
|
||||
echo "===== bundle sha256 ====="
|
||||
|
||||
@@ -29,6 +29,8 @@ const OUT_DIR = path.resolve(getArg('--out', 'ota-package'))
|
||||
const SEQUENCE = Number(getArg('--sequence', String(Date.now()))) // 默认用时间戳做单调计数
|
||||
const MIN_BINARY = getArg('--min-binary', '')
|
||||
const NOTES = getArg('--notes', '')
|
||||
// 构建对应的 git commit sha (发布说明=commit message, sha 单独一行展示)
|
||||
const COMMIT = getArg('--commit', '')
|
||||
const NO_SIGN = args.includes('--no-sign')
|
||||
// 私钥优先来源: MINISIGN_PRIVATE_KEY 环境变量 (CI 注入) → 文件 (--key 路径)
|
||||
const KEY_PATH = path.resolve(getArg('--key', path.join(OUT_DIR, 'hotswap.key')))
|
||||
@@ -117,7 +119,8 @@ const manifest = {
|
||||
bundle_size: bundleStat.size,
|
||||
bundle_sha256: bundleSha256,
|
||||
signature,
|
||||
notes: NOTES,
|
||||
notes: String(NOTES).slice(0, 400),
|
||||
commit: COMMIT || undefined,
|
||||
pub_date: new Date().toISOString(),
|
||||
mandatory: false,
|
||||
// 内部用: 前端 web 模式比对 build_id
|
||||
|
||||
@@ -63,6 +63,7 @@ router.get('/ota/check/:current_sequence', (req, res) => {
|
||||
url: (base ? base : '') + '/api/ota/bundle/' + encodeURIComponent(latest.bundle),
|
||||
signature: latest.signature,
|
||||
notes: latest.notes || '',
|
||||
commit: latest.commit || '',
|
||||
pub_date: latest.pub_date || new Date().toISOString(),
|
||||
mandatory: !!latest.mandatory,
|
||||
bundle_size: latest.bundle_size || 0,
|
||||
@@ -91,6 +92,8 @@ router.get('/ota/manifest-web', (_req, res) => {
|
||||
res.json({
|
||||
available: true,
|
||||
version: latest.version,
|
||||
sequence: latest.sequence,
|
||||
commit: latest.commit || '',
|
||||
build_id: latest.build_id || latest.version,
|
||||
note: latest.notes || '',
|
||||
size: latest.bundle_size || 0,
|
||||
|
||||
@@ -115,6 +115,7 @@ async function checkByVersion() {
|
||||
version: manifest.version,
|
||||
sequence: manifest.sequence,
|
||||
notes: manifest.notes || '',
|
||||
commit: manifest.commit || '',
|
||||
mandatory: !!manifest.mandatory,
|
||||
bundle_size: manifest.bundle_size || 0,
|
||||
},
|
||||
@@ -166,7 +167,9 @@ async function checkOtaUpdateWeb(devMode) {
|
||||
reason: 'ok',
|
||||
info: {
|
||||
version: data.version,
|
||||
sequence: data.sequence,
|
||||
notes: data.note || '',
|
||||
commit: data.commit || '',
|
||||
// web 端没有强制/mandatory 等字段, 仅展示
|
||||
mandatory: false,
|
||||
bundle_size: data.size || 0,
|
||||
|
||||
+14
-2
@@ -14,6 +14,15 @@ import {
|
||||
setOtaDevMode,
|
||||
} from '../api/ota.js'
|
||||
|
||||
// seq/build 时间戳 → 'MM-dd HH:mm' (sequence 为毫秒时间戳, build_id 形如 seq-<ms>)
|
||||
export function fmtSeqTime(v) {
|
||||
const ms = typeof v === 'string' && v.startsWith('seq-') ? Number(v.slice(4)) : Number(v)
|
||||
if (!Number.isFinite(ms) || ms <= 0) return ''
|
||||
const p = (n) => String(n).padStart(2, '0')
|
||||
const d = new Date(ms)
|
||||
return `${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
|
||||
}
|
||||
|
||||
// "我" 页: 顶部 NavBar + 用户头像卡片 + 信息列表 + OTA 升级区
|
||||
// OTA 区:
|
||||
// - 显示当前 APP_VERSION (前端包, 对应内置 dist)
|
||||
@@ -137,7 +146,7 @@ export default function MePage() {
|
||||
|
||||
{/* OTA 升级区 */}
|
||||
<List className="me-list" mode="card" header="前端升级 (OTA)">
|
||||
<List.Item extra={'v' + cur.version + ' · ' + cur.build}>当前版本</List.Item>
|
||||
<List.Item extra={'v' + cur.version + (fmtSeqTime(cur.build) ? ' · ' + fmtSeqTime(cur.build) : '')}>当前版本</List.Item>
|
||||
<List.Item extra={otaVersion?.active ? 'v' + (otaVersion.version || '?') : '内置'}>
|
||||
运行资源
|
||||
</List.Item>
|
||||
@@ -197,7 +206,7 @@ export default function MePage() {
|
||||
</div>
|
||||
</List.Item>
|
||||
{otaState === 'available' && otaInfo && (
|
||||
<List.Item extra={'v' + otaInfo.version}>
|
||||
<List.Item extra={'v' + otaInfo.version + (fmtSeqTime(otaInfo.sequence) ? ' · ' + fmtSeqTime(otaInfo.sequence) : '')}>
|
||||
新版本
|
||||
{otaInfo.bundle_size ? ` (${(otaInfo.bundle_size / 1024).toFixed(0)} KB)` : ''}
|
||||
</List.Item>
|
||||
@@ -217,6 +226,9 @@ export default function MePage() {
|
||||
{otaInfo?.notes && (
|
||||
<List.Item description={otaInfo.notes}>发布说明</List.Item>
|
||||
)}
|
||||
{otaInfo?.commit && (
|
||||
<List.Item extra={String(otaInfo.commit).slice(0, 10)}>提交</List.Item>
|
||||
)}
|
||||
{error && (
|
||||
<List.Item>
|
||||
<span className="me-ota-error">{error}</span>
|
||||
|
||||
Reference in New Issue
Block a user