chore: 发布前检查在途任务;断言核心任务都由业务模块注册
- release.sh 替换容器前列出引擎里未结束的任务(任务名@版本、状态),有则停下, --allow-inflight 放行(新版本只认领自己注册的任务版本) - modules.test:生产模块装配下每个核心任务都由业务模块正式注册,不落到空壳兜底 - 刮削 / 任务入队的关联写回与引擎查询异常两处边界已在 v0.3.14 修复并有故障注入测试 (test/acquisition-organize/enqueue-recovery.test.ts) Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
b61335eaf6
commit
7cc3742d31
2 files changed
+37
-6
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
import { createRoute, z } from "@hono/zod-openapi";
|
import { createRoute, z } from "@hono/zod-openapi";
|
||||||
import { apiResponse, CAPABILITY_IDS, capabilitySpec } from "@umt/contracts";
|
import { apiResponse, CAPABILITY_IDS, capabilitySpec } from "@umt/contracts";
|
||||||
import { defineTask, TASK_NAMES } from "@umt/tasks";
|
import { defineTask, TASK_NAMES, TaskRegistry } from "@umt/tasks";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { buildTaskRegistry, collectOutboxRoutes, DeferredTaskEngine } from "../src/context.ts";
|
import { buildTaskRegistry, collectOutboxRoutes, DeferredTaskEngine } from "../src/context.ts";
|
||||||
import { createApp } from "../src/http/app.ts";
|
import { createApp } from "../src/http/app.ts";
|
||||||
@@ -91,6 +91,16 @@ describe("ServerModule 骨架", () => {
|
|||||||
expect(() => buildTaskRegistry([pingModule, { ...pingModule, name: "dup" }], fakeCtx)).toThrow(/重复注册/);
|
expect(() => buildTaskRegistry([pingModule, { ...pingModule, name: "dup" }], fakeCtx)).toThrow(/重复注册/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("生产装配:每个核心任务都由业务模块正式注册,不落到空壳兜底", () => {
|
||||||
|
const owned = new Set<string>();
|
||||||
|
for (const m of SERVER_MODULES) {
|
||||||
|
const own = new TaskRegistry();
|
||||||
|
m.registerTasks(own, fakeCtx);
|
||||||
|
for (const name of own.names()) owned.add(name);
|
||||||
|
}
|
||||||
|
expect(TASK_NAMES.filter((n) => !owned.has(n))).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
it("outbox 路由合并;同一事件被两个模块认领报错", () => {
|
it("outbox 路由合并;同一事件被两个模块认领报错", () => {
|
||||||
const merged = Object.keys(collectOutboxRoutes([...SERVER_MODULES, pingModule], fakeCtx));
|
const merged = Object.keys(collectOutboxRoutes([...SERVER_MODULES, pingModule], fakeCtx));
|
||||||
expect(merged).toContain("test.ping");
|
expect(merged).toContain("test.ping");
|
||||||
|
|||||||
+26
-5
@@ -1,16 +1,24 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 测试环境发布:从干净的提交构建并替换 app 容器。
|
# 测试环境发布:从干净的提交构建并替换 app 容器。
|
||||||
# 用法:bash scripts/release.sh 版本取根 deno.json
|
# 用法:bash scripts/release.sh [--no-build] [--allow-inflight]
|
||||||
# bash scripts/release.sh --no-build 只用已有镜像重建容器
|
# --no-build 只用已有镜像重建容器
|
||||||
|
# --allow-inflight 引擎里有未结束的任务也继续(缺省停下:新版本可能没有旧版本任务的处理器)
|
||||||
# 前提:工作区干净、CHANGELOG.md 已写好本版本条目、所有成员包版本号一致。
|
# 前提:工作区干净、CHANGELOG.md 已写好本版本条目、所有成员包版本号一致。
|
||||||
# 做的事:打 tag v<版本>(已存在则须指向 HEAD)→ 写 .env.deploy 的 APP_VERSION / SOURCE_REVISION
|
# 做的事:检查引擎里未结束的任务 → 打 tag v<版本>(已存在则须指向 HEAD)→ 写 .env.deploy 的 APP_VERSION / SOURCE_REVISION
|
||||||
# → 构建镜像 → 只替换 app(启动时自动迁移)→ 等 healthy 并核对健康接口版本。
|
# → 构建镜像 → 只替换 app(启动时自动迁移)→ 等 healthy 并核对健康接口版本。
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
ENV_FILE=.env.deploy
|
ENV_FILE=.env.deploy
|
||||||
BUILD=1
|
BUILD=1
|
||||||
[ "${1:-}" = "--no-build" ] && BUILD=0
|
ALLOW_INFLIGHT=0
|
||||||
|
for a in "$@"; do
|
||||||
|
case "$a" in
|
||||||
|
--no-build) BUILD=0 ;;
|
||||||
|
--allow-inflight) ALLOW_INFLIGHT=1 ;;
|
||||||
|
*) echo "未知参数 $a" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
die() { echo "✗ $*" >&2; exit 1; }
|
die() { echo "✗ $*" >&2; exit 1; }
|
||||||
|
|
||||||
@@ -25,6 +33,20 @@ for f in apps/*/deno.json apps/web/package.json packages/*/deno.json; do
|
|||||||
done
|
done
|
||||||
grep -q "^## \[$version\]" CHANGELOG.md || die "CHANGELOG.md 还没有 ## [$version] 条目"
|
grep -q "^## \[$version\]" CHANGELOG.md || die "CHANGELOG.md 还没有 ## [$version] 条目"
|
||||||
|
|
||||||
|
compose() { docker compose --env-file "$ENV_FILE" "$@"; }
|
||||||
|
|
||||||
|
# 升级前未结束的任务:新版本只认领自己注册的任务名 + 版本,旧版本的在途任务可能没人处理
|
||||||
|
if [ -n "$(compose ps -q postgres 2>/dev/null)" ]; then
|
||||||
|
inflight=$(compose exec -T postgres sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "
|
||||||
|
select workflow_name || '"'"'@'"'"' || version || '"'"' '"'"' || status || '"'"' x'"'"' || count(*)
|
||||||
|
from openworkflow.workflow_runs where status not in ('"'"'completed'"'"', '"'"'failed'"'"', '"'"'canceled'"'"')
|
||||||
|
group by workflow_name, version, status order by 1"' 2>/dev/null || true)
|
||||||
|
if [ -n "$inflight" ]; then
|
||||||
|
echo "引擎里有未结束的任务:"; echo "$inflight" | sed 's/^/ /'
|
||||||
|
[ $ALLOW_INFLIGHT -eq 1 ] || die "等它们结束,或确认新版本仍有对应处理器后加 --allow-inflight"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
head=$(git rev-parse --short HEAD)
|
head=$(git rev-parse --short HEAD)
|
||||||
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
|
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
|
||||||
[ "$(git rev-parse --short "$tag^{commit}")" = "$head" ] || die "$tag 已存在但不指向 HEAD($head)"
|
[ "$(git rev-parse --short "$tag^{commit}")" = "$head" ] || die "$tag 已存在但不指向 HEAD($head)"
|
||||||
@@ -39,7 +61,6 @@ set_env() {
|
|||||||
set_env APP_VERSION "$tag"
|
set_env APP_VERSION "$tag"
|
||||||
set_env SOURCE_REVISION "$head"
|
set_env SOURCE_REVISION "$head"
|
||||||
|
|
||||||
compose() { docker compose --env-file "$ENV_FILE" "$@"; }
|
|
||||||
if [ $BUILD -eq 1 ]; then
|
if [ $BUILD -eq 1 ]; then
|
||||||
echo "▶ 构建 uhd-media-tool:$tag"
|
echo "▶ 构建 uhd-media-tool:$tag"
|
||||||
compose build app
|
compose build app
|
||||||
|
|||||||
Reference in new issue
Block a user