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