chore(release): 版本发布 - 准备 v1.5.7.1 预览版
This commit is contained in:
2
.github/scripts/promote-image.sh
vendored
2
.github/scripts/promote-image.sh
vendored
@@ -15,7 +15,7 @@ done
|
||||
|
||||
tags=(--tag "$IMAGE:sha-$GITHUB_SHA" --tag "$IMAGE:sha-${GITHUB_SHA:0:7}")
|
||||
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
|
||||
[[ "${VERSION_TAG:?}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]] || { echo "Invalid version image tag" >&2; exit 1; }
|
||||
[[ "${VERSION_TAG:?}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?([.-][0-9A-Za-z.-]+)?$ ]] || { echo "Invalid version image tag" >&2; exit 1; }
|
||||
tags+=(--tag "$IMAGE:$VERSION_TAG")
|
||||
elif [[ "$GITHUB_REF" == refs/heads/main ]]; then
|
||||
current_sha=$(gh api "repos/${GITHUB_REPOSITORY:?}/git/ref/heads/main" --jq '.object.sha')
|
||||
|
||||
2
.github/workflows/publish-images.yml
vendored
2
.github/workflows/publish-images.yml
vendored
@@ -204,7 +204,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
prerelease_args=()
|
||||
if [[ "$GITHUB_REF_NAME" == *-* || "$GITHUB_REF_NAME" == *.rc* ]]; then
|
||||
if [[ "$GITHUB_REF_NAME" == *-* || "$GITHUB_REF_NAME" == *.rc* || "$GITHUB_REF_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
prerelease_args+=(--prerelease)
|
||||
fi
|
||||
gh release create "$GITHUB_REF_NAME" \
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v1.5.7.1
|
||||
|
||||
- 预览版:包含 v1.5.7 之后主分支已合入的画布批量生成一致性、素材批量删除、Live2D 配置导入、模型选择、Agent 运行稳定性、声明式文本流和后台运维界面改进。
|
||||
- 修复在线更新版本比较,使四段版本号 `v1.5.7.1` 可以被 Host Updater 识别并高于 `v1.5.7`;该版本标记为 GitHub 预发布,不覆盖正式版 `latest`。
|
||||
|
||||
## v1.5.7
|
||||
|
||||
- 合并 PR #549(原作者 @15657306288):新增图片 / 视频列表分析、动态多维表格、视频分镜转换与关键帧绑定,以及批量生成设置;参考图上限前后端统一为 10。
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
)
|
||||
|
||||
type versionPart struct {
|
||||
major, minor, patch int
|
||||
pre []string
|
||||
core []int
|
||||
pre []string
|
||||
}
|
||||
|
||||
func IsReleaseVersion(raw string) bool {
|
||||
@@ -32,11 +32,18 @@ func CompareVersions(left, right string) int {
|
||||
}
|
||||
|
||||
func compareParsedVersions(a, b versionPart) int {
|
||||
for _, pair := range [][2]int{{a.major, b.major}, {a.minor, b.minor}, {a.patch, b.patch}} {
|
||||
if pair[0] < pair[1] {
|
||||
for index := 0; index < len(a.core) || index < len(b.core); index++ {
|
||||
left, right := 0, 0
|
||||
if index < len(a.core) {
|
||||
left = a.core[index]
|
||||
}
|
||||
if index < len(b.core) {
|
||||
right = b.core[index]
|
||||
}
|
||||
if left < right {
|
||||
return -1
|
||||
}
|
||||
if pair[0] > pair[1] {
|
||||
if left > right {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
@@ -78,16 +85,18 @@ func parseVersion(raw string) (versionPart, bool) {
|
||||
value = strings.SplitN(value, "+", 2)[0]
|
||||
parts := strings.SplitN(value, "-", 2)
|
||||
core := strings.Split(parts[0], ".")
|
||||
if len(core) != 3 {
|
||||
if len(core) < 3 {
|
||||
return versionPart{}, false
|
||||
}
|
||||
major, err1 := strconv.Atoi(core[0])
|
||||
minor, err2 := strconv.Atoi(core[1])
|
||||
patch, err3 := strconv.Atoi(core[2])
|
||||
if err1 != nil || err2 != nil || err3 != nil {
|
||||
return versionPart{}, false
|
||||
coreNumbers := make([]int, len(core))
|
||||
for index, value := range core {
|
||||
number, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return versionPart{}, false
|
||||
}
|
||||
coreNumbers[index] = number
|
||||
}
|
||||
parsed := versionPart{major: major, minor: minor, patch: patch}
|
||||
parsed := versionPart{core: coreNumbers}
|
||||
if len(parts) == 2 && parts[1] != "" {
|
||||
parsed.pre = strings.FieldsFunc(parts[1], func(r rune) bool { return r == '.' || r == '-' })
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ func TestCompareVersions(t *testing.T) {
|
||||
{"v1.2.0-preview.10", "v1.2.0-preview.3", 1},
|
||||
{"v1.2.0-preview.3", "v1.2.0", -1},
|
||||
{"v1.2.1", "v1.2.0", 1},
|
||||
{"v1.5.7.1", "v1.5.7", 1},
|
||||
{"v1.5.7.1", "v1.5.7.2", -1},
|
||||
{"v1.2.0", "v1.2.0", 0},
|
||||
{"vsha-5509b17", "v1.5.0", -1},
|
||||
{"sha-5509b17", "v1.5.0", -1},
|
||||
|
||||
@@ -14,7 +14,7 @@ description: Host Updater、Release 检查、数据库备份迁移、健康验
|
||||
## 安全更新顺序
|
||||
|
||||
1. 从 GitHub Releases 读取包含预览版在内的最高语义版本,并与 `.env` 中固定的 `CANVAS_IMAGE_TAG` 比较。`sha-*` 这类非正式镜像标签一律视为低于正式 Release,避免把 `vsha-5509b17` 字典序比较成比 `v1.5.0` 更新。
|
||||
2. 下载目标版本的 `docker-compose.deploy.yml`,执行 `docker compose config --quiet`,检查部署文件、磁盘空间和当前版本。当前标签不是 `vX.Y.Z` 时,健康检查只确认服务就绪,不要求构建号和镜像标签逐字相同。
|
||||
2. 下载目标版本的 `docker-compose.deploy.yml`,执行 `docker compose config --quiet`,检查部署文件、磁盘空间和当前版本。当前标签不是 `vX.Y.Z` 或 `vX.Y.Z.N` 时,健康检查只确认服务就绪,不要求构建号和镜像标签逐字相同。
|
||||
3. 创建 `backup-YYYYMMDD-HHMMSS.zip.partial`,其中包含 PostgreSQL custom-format dump、后端数据目录 tar 和元数据;写入完成后 `fsync`、原子改名,并复核 ZIP 结构和 SHA-256。
|
||||
4. 拉取目标 backend/web 镜像并要求存在仓库 `sha256` 摘要。任何失败都在停服前中止。
|
||||
5. 停止 web/backend,等待 Backend 的 SIGTERM drain 完成;PostgreSQL 和 Redis 保持运行。
|
||||
|
||||
@@ -124,7 +124,11 @@ function adminPopupContainer(node?: HTMLElement) {
|
||||
|
||||
export function AdminShell() {
|
||||
const userId = useUserStore((state) => state.user?.id);
|
||||
return <AdminDensityProvider key={userId || "anonymous"} userId={userId}><AdminShellLayout /></AdminDensityProvider>;
|
||||
return (
|
||||
<AdminDensityProvider key={userId || "anonymous"} userId={userId}>
|
||||
<AdminShellLayout />
|
||||
</AdminDensityProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function AdminShellLayout() {
|
||||
|
||||
@@ -91,7 +91,9 @@ export default function AnalyticsPanel({ users, channels }: Props) {
|
||||
else next.delete("rangePreset");
|
||||
setSearchParams(next, { replace: true });
|
||||
void reload();
|
||||
return () => { requestSequence.current += 1; };
|
||||
return () => {
|
||||
requestSequence.current += 1;
|
||||
};
|
||||
}, [filters, rangePreset]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -275,7 +277,19 @@ export default function AnalyticsPanel({ users, channels }: Props) {
|
||||
</ListToolbar>
|
||||
|
||||
{financeUnavailable && <Alert type="warning" showIcon title="后端未返回完整财务统计,缺失金额显示为 --。请确认后端已更新并重启后刷新。" />}
|
||||
{loadError && <Alert type="error" showIcon title="统计数据读取失败" description={loadError} action={<Button size="small" onClick={() => void reload()}>重试</Button>} />}
|
||||
{loadError && (
|
||||
<Alert
|
||||
type="error"
|
||||
showIcon
|
||||
title="统计数据读取失败"
|
||||
description={loadError}
|
||||
action={
|
||||
<Button size="small" onClick={() => void reload()}>
|
||||
重试
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<section className="admin-analytics-health-grid" aria-label="运营健康指标">
|
||||
<AnalyticsHealthCard
|
||||
@@ -300,19 +314,8 @@ export default function AnalyticsPanel({ users, channels }: Props) {
|
||||
detail={data ? (data.kpi.upstreamRequests ? "真实上游请求成功率" : "当前范围暂无请求") : undefined}
|
||||
tone={!data || !data.kpi.upstreamRequests ? "neutral" : data.kpi.successRate < 90 ? "warning" : "success"}
|
||||
/>
|
||||
<AnalyticsHealthCard
|
||||
icon={<Clock3 className="size-4" />}
|
||||
label="P95 请求耗时"
|
||||
value={data && data.kpi.upstreamRequests > 0 ? formatDuration(data.kpi.p95DurationMs) : "--"}
|
||||
detail="筛选范围内的上游请求"
|
||||
/>
|
||||
<AnalyticsHealthCard
|
||||
icon={<Workflow className="size-4" />}
|
||||
label="当前排队"
|
||||
value={data ? formatNumber(data.kpi.currentQueuedTasks) : "--"}
|
||||
detail="实时快照 · 非历史累计"
|
||||
tone={data?.kpi.currentQueuedTasks ? "warning" : "neutral"}
|
||||
/>
|
||||
<AnalyticsHealthCard icon={<Clock3 className="size-4" />} label="P95 请求耗时" value={data && data.kpi.upstreamRequests > 0 ? formatDuration(data.kpi.p95DurationMs) : "--"} detail="筛选范围内的上游请求" />
|
||||
<AnalyticsHealthCard icon={<Workflow className="size-4" />} label="当前排队" value={data ? formatNumber(data.kpi.currentQueuedTasks) : "--"} detail="实时快照 · 非历史累计" tone={data?.kpi.currentQueuedTasks ? "warning" : "neutral"} />
|
||||
<AnalyticsHealthCard
|
||||
icon={<CircleDollarSign className="size-4" />}
|
||||
label="费用统计(积分)"
|
||||
|
||||
Reference in New Issue
Block a user