Skip to content

Commit 326e6d3

Browse files
committed
ci: core regression gates
- Add GitHub Actions core regression (MSVC/Ninja, Ubuntu/Ninja, MinGW) - Add optional short soak workflow_dispatch job + artifacts - Add ctest runtime-cli script replay gate (WorldDb cycle) - Add schema version constants + runtime-cli/GUI checks - Add doc/version contract tests + PR template
1 parent 90d61fe commit 326e6d3

20 files changed

Lines changed: 484 additions & 36 deletions

.github/pull_request_template.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
## 变更说明
2+
-
3+
4+
## 回归与门禁(必选)
5+
- [ ] `ctest --test-dir build_core --output-on-failure` 通过(含 `RuntimeCli.RunWorldCycleScript`
6+
- [ ] 如修改 `TickTelemetry` / `WorldAtlas` / Runtime API:已更新 `docs/architecture/foundation/telemetry-schema.md` / `docs/architecture/foundation/runtime-api.md`
7+
- [ ] 如新增/变更脚本/命令:已更新 `docs/handbook/CORE_REGRESSION.md``docs/guides/headless_runtime_cli.md`
8+
9+
## 风险与回滚
10+
- 风险点:
11+
- 回滚方式:
12+
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
name: core-regression
2+
3+
on:
4+
push:
5+
pull_request:
6+
workflow_dispatch:
7+
inputs:
8+
run_soak:
9+
description: "Run short soak (uploads artifacts)"
10+
type: boolean
11+
default: false
12+
13+
concurrency:
14+
group: core-regression-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
jobs:
18+
windows-core:
19+
name: Windows (Core)
20+
runs-on: windows-latest
21+
env:
22+
CPM_SOURCE_CACHE: ${{ github.workspace }}\.cpmcache
23+
steps:
24+
- uses: actions/checkout@v4
25+
26+
- uses: ilammy/msvc-dev-cmd@v1
27+
28+
- uses: seanmiddleditch/gha-setup-ninja@v5
29+
30+
- name: Cache CPM
31+
uses: actions/cache@v4
32+
with:
33+
path: ${{ env.CPM_SOURCE_CACHE }}
34+
key: ${{ runner.os }}-cpm-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
35+
restore-keys: |
36+
${{ runner.os }}-cpm-
37+
38+
- name: Configure (Core)
39+
shell: pwsh
40+
run: >
41+
cmake -S . -B build_core -G Ninja
42+
-DGENESISENGINE_ENABLE_TESTS=ON
43+
-DGENESIS_BUILD_GUI=OFF
44+
-DGENESIS_WITH_STYLE=OFF
45+
-DCMAKE_BUILD_TYPE=Debug
46+
47+
- name: Build (Core)
48+
shell: pwsh
49+
run: cmake --build build_core --parallel
50+
51+
- name: Test (ctest)
52+
shell: pwsh
53+
run: ctest --test-dir build_core --output-on-failure
54+
55+
ubuntu-soak:
56+
name: Ubuntu (Short Soak, optional)
57+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.run_soak }}
58+
runs-on: ubuntu-latest
59+
env:
60+
CPM_SOURCE_CACHE: ${{ github.workspace }}/.cpmcache
61+
steps:
62+
- uses: actions/checkout@v4
63+
64+
- name: Install Ninja
65+
run: sudo apt-get update && sudo apt-get install -y ninja-build
66+
67+
- name: Cache CPM
68+
uses: actions/cache@v4
69+
with:
70+
path: ${{ env.CPM_SOURCE_CACHE }}
71+
key: ${{ runner.os }}-cpm-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
72+
restore-keys: |
73+
${{ runner.os }}-cpm-
74+
75+
- name: Configure (Core)
76+
run: >
77+
cmake -S . -B build_soak -G Ninja
78+
-DGENESISENGINE_ENABLE_TESTS=OFF
79+
-DGENESIS_BUILD_GUI=OFF
80+
-DGENESIS_WITH_STYLE=OFF
81+
-DCMAKE_BUILD_TYPE=RelWithDebInfo
82+
83+
- name: Build (runtime-cli)
84+
run: cmake --build build_soak --parallel --target genesis_runtime_cli
85+
86+
- name: Run soak
87+
run: |
88+
mkdir -p out/ci
89+
./build_soak/src/genesis-runtime-cli soak data/world_multiagent --root . --steps 800 --agents 12 --out out/ci/soak_metrics.json --summary-out out/ci/soak_summary.md --quiet
90+
91+
- name: Upload artifacts
92+
uses: actions/upload-artifact@v4
93+
with:
94+
name: soak-ci
95+
path: out/ci
96+
97+
ubuntu-core:
98+
name: Ubuntu (Core)
99+
runs-on: ubuntu-latest
100+
env:
101+
CPM_SOURCE_CACHE: ${{ github.workspace }}/.cpmcache
102+
steps:
103+
- uses: actions/checkout@v4
104+
105+
- name: Install Ninja
106+
run: sudo apt-get update && sudo apt-get install -y ninja-build
107+
108+
- name: Cache CPM
109+
uses: actions/cache@v4
110+
with:
111+
path: ${{ env.CPM_SOURCE_CACHE }}
112+
key: ${{ runner.os }}-cpm-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
113+
restore-keys: |
114+
${{ runner.os }}-cpm-
115+
116+
- name: Configure (Core)
117+
run: >
118+
cmake -S . -B build_core -G Ninja
119+
-DGENESISENGINE_ENABLE_TESTS=ON
120+
-DGENESIS_BUILD_GUI=OFF
121+
-DGENESIS_WITH_STYLE=OFF
122+
-DCMAKE_BUILD_TYPE=Debug
123+
124+
- name: Build (Core)
125+
run: cmake --build build_core --parallel
126+
127+
- name: Test (ctest)
128+
run: ctest --test-dir build_core --output-on-failure
129+
130+
windows-mingw-core:
131+
name: Windows (Core, MinGW)
132+
runs-on: windows-latest
133+
env:
134+
CPM_SOURCE_CACHE: ${{ github.workspace }}\.cpmcache
135+
steps:
136+
- uses: actions/checkout@v4
137+
138+
- uses: msys2/setup-msys2@v2
139+
with:
140+
msystem: MINGW64
141+
update: true
142+
install: >-
143+
mingw-w64-x86_64-gcc
144+
mingw-w64-x86_64-cmake
145+
mingw-w64-x86_64-ninja
146+
147+
- name: Cache CPM
148+
uses: actions/cache@v4
149+
with:
150+
path: ${{ env.CPM_SOURCE_CACHE }}
151+
key: ${{ runner.os }}-mingw-cpm-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
152+
restore-keys: |
153+
${{ runner.os }}-mingw-cpm-
154+
155+
- name: Configure (Core, MinGW)
156+
shell: msys2 {0}
157+
run: >
158+
cmake -S . -B build_core_mingw -G Ninja
159+
-DGENESISENGINE_ENABLE_TESTS=ON
160+
-DGENESIS_BUILD_GUI=OFF
161+
-DGENESIS_WITH_STYLE=OFF
162+
-DCMAKE_BUILD_TYPE=Debug
163+
164+
- name: Build (Core, MinGW)
165+
shell: msys2 {0}
166+
run: cmake --build build_core_mingw --parallel
167+
168+
- name: Test (ctest, MinGW)
169+
shell: msys2 {0}
170+
run: ctest --test-dir build_core_mingw --output-on-failure

docs/guides/build_pipeline.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ ctest --test-dir build --output-on-failure
5959
```
6060

6161
`ctest` 将自动运行已注册的 gtest 二进制及若干 CTest 条目(以 `tests/CMakeLists.txt` 为准)。
62+
其中包含一条 Runtime CLI 回放用例(`RuntimeCli.RunWorldCycleScript`),用于覆盖 “脚本→命令队列→执行→世界保存” 的最小闭环。
6263

6364
## 5. 世界生成(Worldgen)
6465
- Worldgen 可独立运行(通过 Runtime 命令 `world.db.generate` 或 headless 脚本),不再依赖旧的 `LocationGraph`
@@ -77,5 +78,7 @@ ctest --test-dir build --output-on-failure
7778
4. (可选)通过沙盒 GUI 的 World Generation 面板或运行时命令脚本验证世界生成流程
7879
5.`build/src/genesis-sandbox-gui` 目录下启动 GUI 进行回归验证
7980

81+
Windows 一键回归(Core only):`powershell -NoProfile -ExecutionPolicy Bypass -File scripts\run_regression_core.ps1 -Config Debug -BuildDir build_core`
82+
8083
若引入新依赖或修改工具链,请记得更新本文件并在 PR 描述中说明。
8184

docs/guides/headless_runtime_cli.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ cmake --build build_headless --target genesis_runtime_cli
2525
- `--world <folder>`:启动时先加载一次世界(可选;脚本也可自行 `world.db.load`
2626
- `--events-out <file>`:把执行过的 `RuntimeEventReport` 列表写到 JSON 文件
2727
- `--after-steps <n>`:脚本执行完后额外推进 n 步(用于观察非事件驱动的演化)
28+
- `--no-schema-check`:禁用 `schema_version` 校验(不推荐;仅用于排障/临时兼容)
2829

2930
## 短 Soak 指标报告(软指标,不做硬门禁)
3031

docs/handbook/CORE_REGRESSION.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ cmake --build build_core --parallel
1919
ctest --test-dir build_core --output-on-failure
2020
```
2121

22+
提示:
23+
- 当前 `ctest` 已包含一条 Runtime CLI 端到端回放用例(`RuntimeCli.RunWorldCycleScript`),用于覆盖 JSON 脚本→命令队列→执行→世界保存的最小闭环。
24+
2225
关注的回归信号(示例):
2326
- `genesis_runtime_tests`:事件队列、JSON 命令、快照 diff、涌现 smoke、多人回归
2427
- `RuntimeShortSoak.*`:短时稳定性与可观测性基线(默认纳入 `genesis_runtime_tests`
@@ -40,8 +43,18 @@ cmake --build build_core --target genesis_runtime_cli
4043
.\build_core\src\genesis-runtime-cli.exe run-script data\scripts\world_cycle.json --root . --max-steps 256 --after-steps 32
4144
```
4245

46+
注意:`genesis-runtime-cli` 默认会校验 `TickTelemetry.schema_version``WorldAtlas.schema_version`(避免误用旧 DLL 导致“静默错读”);如需临时绕过可追加 `--no-schema-check`(不推荐)。
47+
4348
更多说明见:`docs/guides/headless_runtime_cli.md``docs/architecture/foundation/runtime-api.md`
4449

50+
## 3.0 一键回归(推荐)
51+
52+
在 Windows PowerShell 下可直接使用脚本完成:配置→编译→ctest→CLI 回放:
53+
54+
```powershell
55+
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\run_regression_core.ps1 -Config Debug -BuildDir build_core
56+
```
57+
4558
## 3.1 短 Soak:软指标报告(多样性 / 资源经济)
4659

4760
说明:Soak 报告用于版本间对比与调参,不作为 CI 硬门禁;CI 侧仍以单元/集成测试的“硬契约”与短 soak 的不变量断言为主。
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
5+
namespace Genesis::Runtime {
6+
7+
inline constexpr std::uint32_t kWorldAtlasSchemaVersion = 2;
8+
9+
} // namespace Genesis::Runtime
10+

include/genesis/runtime/WorldAtlas.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
#include <optional>
55
#include <vector>
66

7+
#include "genesis/runtime/SchemaVersions.hpp"
78
#include "genesis/world/WorldDatabase.hpp"
89

910
namespace Genesis::Runtime {
1011

1112
struct WorldAtlas {
12-
std::uint32_t schema_version{2};
13+
std::uint32_t schema_version{kWorldAtlasSchemaVersion};
1314
std::uint32_t world_version{0};
1415

1516
struct PerMap {
@@ -26,4 +27,3 @@ struct WorldAtlas {
2627
};
2728

2829
} // namespace Genesis::Runtime
29-
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
5+
namespace genesis::telemetry {
6+
7+
inline constexpr std::uint32_t kTickTelemetrySchemaVersion = 5;
8+
9+
} // namespace genesis::telemetry
10+

include/genesis/telemetry/TelemetryBuffer.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <string>
66
#include <vector>
77

8+
#include "genesis/telemetry/SchemaVersions.hpp"
89
#include "genesis/world/WorldTypes.hpp"
910
#include "genesis/world/WorldDatabase.hpp"
1011

@@ -93,7 +94,7 @@ struct MovementSnapshot {
9394
};
9495

9596
struct TickTelemetry {
96-
std::uint32_t schema_version{5};
97+
std::uint32_t schema_version{kTickTelemetrySchemaVersion};
9798
std::uint64_t step{0};
9899
float stepSeconds{0.0f};
99100
std::vector<ResourceSnapshot> resources;

scripts/run_regression_core.ps1

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,67 @@
11
param(
22
[string]$Generator = "Ninja",
33
[string]$Config = "Release",
4-
[string]$BuildDir = "build_regression_core"
4+
[string]$BuildDir = "build_regression_core",
5+
[switch]$SkipRuntimeCliScript,
6+
[string]$RuntimeCliScript = "data/scripts/world_cycle.json",
7+
[UInt64]$RuntimeCliMaxSteps = 256,
8+
[UInt64]$RuntimeCliAfterSteps = 32
59
)
610

711
$ErrorActionPreference = "Stop"
812

913
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
14+
$buildPath = Join-Path $repoRoot $BuildDir
1015

11-
$args = @("-S", $repoRoot, "-B", (Join-Path $repoRoot $BuildDir), "-G", $Generator)
16+
function Ensure-Dir([string]$Path) {
17+
New-Item -ItemType Directory -Force -Path $Path | Out-Null
18+
}
19+
20+
$args = @("-S", $repoRoot, "-B", $buildPath, "-G", $Generator)
1221
$args += "-DGENESISENGINE_ENABLE_TESTS=ON"
1322
$args += "-DGENESIS_BUILD_GUI=OFF"
1423
$args += "-DGENESIS_WITH_STYLE=OFF"
1524

1625
& cmake @args | Write-Host
17-
& cmake --build (Join-Path $repoRoot $BuildDir) --parallel --config $Config | Write-Host
18-
& ctest --test-dir (Join-Path $repoRoot $BuildDir) --output-on-failure -C $Config | Write-Host
26+
& cmake --build $buildPath --parallel --config $Config | Write-Host
27+
& ctest --test-dir $buildPath --output-on-failure -C $Config | Write-Host
28+
29+
if (-not $SkipRuntimeCliScript) {
30+
& cmake --build $buildPath --parallel --config $Config --target genesis_runtime_cli | Write-Host
31+
32+
$exe = Join-Path $buildPath "src\\genesis-runtime-cli.exe"
33+
if (!(Test-Path $exe)) {
34+
throw "Missing executable: $exe"
35+
}
36+
37+
Ensure-Dir (Join-Path $repoRoot "out")
38+
Ensure-Dir (Join-Path $repoRoot "out\\regression")
39+
Ensure-Dir (Join-Path $repoRoot "out\\regression\\scripts")
40+
41+
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
42+
$backupFolder = Join-Path $repoRoot ("out\\regression\\world_new_backup_{0}" -f $timestamp)
43+
44+
$scriptPath = Resolve-Path (Join-Path $repoRoot $RuntimeCliScript)
45+
if (!(Test-Path $scriptPath)) {
46+
throw "Missing runtime-cli script: $scriptPath"
47+
}
48+
49+
$scriptJson = Get-Content -Raw $scriptPath | ConvertFrom-Json
50+
if ($null -eq $scriptJson.commands) {
51+
throw "Invalid runtime-cli script (expected object with 'commands'): $scriptPath"
52+
}
53+
54+
foreach ($cmd in $scriptJson.commands) {
55+
if ($cmd.action -eq "world.db.save") {
56+
$cmd.folder = $backupFolder
57+
}
58+
}
59+
60+
$patchedScriptPath = Join-Path $repoRoot ("out\\regression\\scripts\\world_cycle_{0}.json" -f $timestamp)
61+
($scriptJson | ConvertTo-Json -Depth 16) | Out-File -FilePath $patchedScriptPath -Encoding utf8
1962

63+
& $exe run-script $patchedScriptPath --root $repoRoot --max-steps $RuntimeCliMaxSteps --after-steps $RuntimeCliAfterSteps | Write-Host
64+
if ($LASTEXITCODE -ne 0) {
65+
throw ("runtime-cli run-script failed (exit={0})" -f $LASTEXITCODE)
66+
}
67+
}

0 commit comments

Comments
 (0)