Skip to content

Commit b3e9a1d

Browse files
committed
VGR-9009 add Vergil CI pipeline
1 parent c8ca021 commit b3e9a1d

File tree

7 files changed

+568
-4
lines changed

7 files changed

+568
-4
lines changed

.github/workflows/vergil-ci.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: Vergil CI
2+
3+
on:
4+
push:
5+
branches:
6+
- Experimental
7+
paths:
8+
- '.github/workflows/vergil-ci.yml'
9+
- 'Config/**'
10+
- 'Plugins/**'
11+
- 'Source/**'
12+
- 'TensAiExample.uproject'
13+
pull_request:
14+
paths:
15+
- '.github/workflows/vergil-ci.yml'
16+
- 'Config/**'
17+
- 'Plugins/**'
18+
- 'Source/**'
19+
- 'TensAiExample.uproject'
20+
workflow_dispatch:
21+
inputs:
22+
lane:
23+
description: 'Vergil CI lane to run'
24+
required: true
25+
default: 'All'
26+
type: choice
27+
options:
28+
- All
29+
- Build
30+
- HeadlessAutomation
31+
- GoldenTests
32+
- PIRuntime
33+
- PerfSmoke
34+
35+
concurrency:
36+
group: vergil-ci-${{ github.ref }}
37+
cancel-in-progress: true
38+
39+
jobs:
40+
vergil-ci:
41+
name: Vergil CI
42+
runs-on:
43+
- self-hosted
44+
- windows
45+
timeout-minutes: 240
46+
env:
47+
UE_5_7_ROOT: ${{ vars.UE_5_7_ROOT }}
48+
defaults:
49+
run:
50+
shell: pwsh
51+
steps:
52+
- name: Check out repository
53+
uses: actions/checkout@v4
54+
with:
55+
submodules: recursive
56+
57+
- name: Show selected lane
58+
run: |
59+
$lane = "${{ github.event.inputs.lane }}"
60+
if ([string]::IsNullOrWhiteSpace($lane)) {
61+
$lane = 'All'
62+
}
63+
64+
Write-Host "Selected lane: $lane"
65+
66+
- name: Run Vergil CI
67+
run: |
68+
$lane = "${{ github.event.inputs.lane }}"
69+
if ([string]::IsNullOrWhiteSpace($lane)) {
70+
$lane = 'All'
71+
}
72+
73+
if ($lane -in @('All', 'Build')) {
74+
powershell -ExecutionPolicy Bypass -File .\Plugins\Vergil\Tools\Invoke-VergilCILane.ps1 -Lane $lane
75+
}
76+
else {
77+
powershell -ExecutionPolicy Bypass -File .\Plugins\Vergil\Tools\Invoke-VergilCILane.ps1 -Lane Build
78+
powershell -ExecutionPolicy Bypass -File .\Plugins\Vergil\Tools\Invoke-VergilCILane.ps1 -Lane $lane
79+
}
80+
81+
- name: Upload Vergil CI artifacts
82+
if: always()
83+
uses: actions/upload-artifact@v4
84+
with:
85+
name: vergil-ci-${{ github.run_id }}-${{ github.run_attempt }}
86+
if-no-files-found: ignore
87+
path: |
88+
Saved/Logs/VergilCI
89+
Saved/Vergil/GoldenAssets

Plugins/Vergil/README.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,26 @@ Useful variants:
3737

3838
The runner writes a headless automation log and prints a compile/apply/test summary from that log.
3939

40+
## CI lanes
41+
42+
Repo-level GitHub Actions coverage now lives in `.github/workflows/vergil-ci.yml` and targets a self-hosted Windows runner with Unreal Engine 5.7 installed. The workflow uses `UE_5_7_ROOT` if it is set as a runner environment variable or GitHub repo variable; otherwise it falls back to `C:\Program Files\Epic Games\UE_5.7`.
43+
44+
The same CI entrypoints are available locally:
45+
46+
```powershell
47+
powershell -ExecutionPolicy Bypass -File .\Plugins\Vergil\Tools\Invoke-VergilCILane.ps1 -Lane All
48+
```
49+
50+
Useful variants:
51+
52+
- Build only: `powershell -ExecutionPolicy Bypass -File .\Plugins\Vergil\Tools\Invoke-VergilCILane.ps1 -Lane Build`
53+
- Full scaffold suite only: `powershell -ExecutionPolicy Bypass -File .\Plugins\Vergil\Tools\Invoke-VergilCILane.ps1 -Lane HeadlessAutomation`
54+
- Persisted golden regressions only: `powershell -ExecutionPolicy Bypass -File .\Plugins\Vergil\Tools\Invoke-VergilCILane.ps1 -Lane GoldenTests`
55+
- PIE runtime validation only: `powershell -ExecutionPolicy Bypass -File .\Plugins\Vergil\Tools\Invoke-VergilCILane.ps1 -Lane PIRuntime`
56+
- Perf smoke only: `powershell -ExecutionPolicy Bypass -File .\Plugins\Vergil\Tools\Invoke-VergilCILane.ps1 -Lane PerfSmoke`
57+
58+
The `PerfSmoke` lane currently time-bounds representative persisted and PIE regressions instead of large-graph benchmarks. `VGR-9003` is the follow-up ticket that will replace that smoke guard with dedicated large-graph benchmark coverage.
59+
4060
## Versioning and migration
4161

4262
- See [VERSIONING.md](VERSIONING.md) for the current semantic-versioning policy, schema-migration policy, and release-update checklist.
@@ -81,7 +101,7 @@ The runner writes a headless automation log and prints a compile/apply/test summ
81101
## Current baseline
82102

83103
- Milestone 0 is complete.
84-
- `VGR-1001`, `VGR-1002`, `VGR-1003`, `VGR-1004`, `VGR-1005`, `VGR-1006`, `VGR-1007`, `VGR-1008`, `VGR-1009`, `VGR-1010`, `VGR-2001`, `VGR-2002`, `VGR-2003`, `VGR-2004`, `VGR-3001`, `VGR-3002`, `VGR-3003`, `VGR-3004`, `VGR-3005`, `VGR-3006`, `VGR-3007`, `VGR-3008`, `VGR-4001`, `VGR-4002`, `VGR-4003`, `VGR-4004`, `VGR-4005`, `VGR-4006`, `VGR-4007`, `VGR-4008`, `VGR-4009`, `VGR-5001`, `VGR-5002`, `VGR-5003`, `VGR-5004`, `VGR-5005`, `VGR-5006`, `VGR-5007`, `VGR-5008`, `VGR-6001`, `VGR-6002`, `VGR-6003`, `VGR-6004`, `VGR-6005`, `VGR-6006`, `VGR-6007`, `VGR-6008`, `VGR-7001`, `VGR-7002`, `VGR-7003`, `VGR-7004`, `VGR-7005`, `VGR-7006`, `VGR-7007`, `VGR-8001`, `VGR-8002`, `VGR-8003`, `VGR-8004`, `VGR-8005`, `VGR-8006`, `VGR-8007`, `VGR-8008`, `VGR-9002`, `VGR-9004`, `VGR-9005`, `VGR-9006`, `VGR-9007`, `VGR-9008`, and `VGR-9010` are complete.
104+
- `VGR-1001`, `VGR-1002`, `VGR-1003`, `VGR-1004`, `VGR-1005`, `VGR-1006`, `VGR-1007`, `VGR-1008`, `VGR-1009`, `VGR-1010`, `VGR-2001`, `VGR-2002`, `VGR-2003`, `VGR-2004`, `VGR-3001`, `VGR-3002`, `VGR-3003`, `VGR-3004`, `VGR-3005`, `VGR-3006`, `VGR-3007`, `VGR-3008`, `VGR-4001`, `VGR-4002`, `VGR-4003`, `VGR-4004`, `VGR-4005`, `VGR-4006`, `VGR-4007`, `VGR-4008`, `VGR-4009`, `VGR-5001`, `VGR-5002`, `VGR-5003`, `VGR-5004`, `VGR-5005`, `VGR-5006`, `VGR-5007`, `VGR-5008`, `VGR-6001`, `VGR-6002`, `VGR-6003`, `VGR-6004`, `VGR-6005`, `VGR-6006`, `VGR-6007`, `VGR-6008`, `VGR-7001`, `VGR-7002`, `VGR-7003`, `VGR-7004`, `VGR-7005`, `VGR-7006`, `VGR-7007`, `VGR-8001`, `VGR-8002`, `VGR-8003`, `VGR-8004`, `VGR-8005`, `VGR-8006`, `VGR-8007`, `VGR-8008`, `VGR-9002`, `VGR-9004`, `VGR-9005`, `VGR-9006`, `VGR-9007`, `VGR-9008`, `VGR-9009`, and `VGR-9010` are complete.
85105
- Document-authored Blueprint metadata now has structural validation, deterministic command planning, editor execution, and headless automation coverage.
86106
- Document-authored member variables now have structural validation, deterministic command planning, editor execution, and headless automation coverage.
87107
- Document-authored function and macro definitions now have structural validation plus deterministic command planning and editor execution for graph/signature creation and updates.
@@ -105,6 +125,7 @@ The runner writes a headless automation log and prints a compile/apply/test summ
105125
- Whole-Blueprint authoring now also has persisted source-control diff coverage: `Vergil.Scaffold.SourceControlDiff` compares two persisted revisions of the same `/Game/Tests/...` Blueprint package through a checked-in text diff fixture while cross-checking the changed review lines against `Vergil.DocumentDiff`'s modified canonical paths.
106126
- Whole-Blueprint authoring now also has persisted failed-apply recovery coverage: `Vergil.Scaffold.CompileApplyRecoveryRoundtrip` forces a late apply failure after a compiled update plan has already mutated a real `/Game/Tests/...` Blueprint package, then saves and reloads the recovered asset to prove the rolled-back baseline state survives the restart boundary cleanly.
107127
- The scaffold now also has dedicated PIE runtime validation suites for real play-world behavior, covering latent event flow (`Delay`, function-name timers, and dispatcher signaling) plus runtime world mutation (`AddComponentByClass` and `SpawnActor`) under the same headless `Vergil.Scaffold` runner.
128+
- Repo-level GitHub Actions CI now exists for a self-hosted Windows Unreal runner, with explicit `Build`, `HeadlessAutomation`, `GoldenTests`, `PIRuntime`, and `PerfSmoke` lanes that reuse checked-in PowerShell entrypoints instead of duplicating commands inside workflow YAML.
108129
- Vergil now has an explicit semantic-versioning and migration policy document, and the supported-contract manifest now reports the plugin semantic version, plugin descriptor version, and supported schema migration paths directly from code-backed helpers.
109130
- Schema migration helpers now exist for older documents, and the compiler now runs schema migration as its first pass so supported legacy documents upgrade automatically before validation and planning.
110131
- Every supported legacy schema starting version now also has headless end-to-end execution coverage, proving representative legacy documents survive migration, compile, and apply through the current `UE_5.7` pipeline.

Plugins/Vergil/ROADMAP.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ Tickets:
596596
- [x] `VGR-9006` Add crash/recovery tests around compile/apply
597597
- [x] `VGR-9007` Add semantic versioning and migration docs
598598
- [x] `VGR-9008` Add extension docs for custom handlers
599-
- `VGR-9009` Add CI pipelines for build, headless automation, golden tests, and perf smoke
599+
- [x] `VGR-9009` Add CI pipelines for build, headless automation, golden tests, and perf smoke
600600
- [x] `VGR-9010` Add runtime-safe automation fixtures
601601

602602
Acceptance criteria:
@@ -644,6 +644,12 @@ Session note for `VGR-9008` (2026-03-07):
644644
- `README.md` and `SUPPORTED_DESCRIPTOR_CONTRACTS.md` now link that guide back to the code-backed node-support matrix and the existing node-lowering contracts, so extension work has one explicit checklist covering manifest updates, earlier-pass normalization, executor integration, diagnostics, and automation.
645645
- `Vergil.Scaffold.SupportedNodeContractDocs` re-verified cleanly after the doc updates in this workspace.
646646

647+
Session note for `VGR-9009` (2026-03-07):
648+
649+
- Repo-level GitHub Actions coverage now exists in `.github/workflows/vergil-ci.yml`, targeting a self-hosted Windows Unreal runner and reusing checked-in PowerShell entrypoints instead of hardcoding engine commands directly into the workflow.
650+
- `Plugins/Vergil/Tools/Invoke-VergilProjectBuild.ps1` now builds the `TensAiExampleEditor` target through `UnrealBuildTool.exe`, `Invoke-VergilCILane.ps1` exposes explicit `Build`, `HeadlessAutomation`, `GoldenTests`, `PIRuntime`, `PerfSmoke`, and `All` lanes, and the workflow uploads `Saved/Logs/VergilCI` plus any saved golden-artifact mismatches on every run.
651+
- The current `PerfSmoke` lane time-bounds representative persisted and PIE regressions (`GoldenAssetSnapshot`, `SourceControlDiff`, `CompileApplyRecoveryRoundtrip`, and `PIERuntime`) as a release-hardening guard until `VGR-9003` lands dedicated large-graph benchmarks.
652+
647653
Session note for `VGR-9004` (2026-03-07):
648654

649655
- `Vergil.Scaffold.LegacySchemaExecutionCoverage` now iterates every supported legacy starting schema and proves representative legacy documents survive migration, compile, and apply through the current `UE_5.7` editor pipeline.
@@ -670,8 +676,7 @@ If those are weak, later coverage work will turn into one-off patches.
670676
## Recommended Next Sprint
671677
Best next sprint from the current baseline:
672678

673-
1. `VGR-9009`
674-
2. `VGR-9003`
679+
1. `VGR-9003`
675680

676681
This keeps pressure on the next highest-value release-hardening work now that the agent layer can separate read-only planning from explicit replayed apply, inspection tooling is in place, the code-backed support manifest is exposed, version/migration policy is explicit, and whole-asset authoring now has persisted save/reload/native-compile roundtrip coverage plus restart-boundary failed-apply recovery coverage and checked-in golden snapshot and reviewed source-control diff fixtures on the supported milestone-4 surface.
677682

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
param(
2+
[ValidateSet('All', 'Build', 'HeadlessAutomation', 'GoldenTests', 'PIRuntime', 'PerfSmoke')]
3+
[string]$Lane = 'All',
4+
[string]$EngineRoot = '',
5+
[string]$UnrealBuildToolPath = '',
6+
[string]$EditorCmdPath = '',
7+
[string]$ProjectPath = '',
8+
[int]$PerfSmokeMaxTotalSeconds = 900,
9+
[int]$PerfSmokeMaxPerScenarioSeconds = 360
10+
)
11+
12+
Set-StrictMode -Version Latest
13+
$ErrorActionPreference = 'Stop'
14+
15+
. (Join-Path $PSScriptRoot 'VergilCICommon.ps1')
16+
17+
$projectRoot = Get-VergilProjectRoot
18+
$resolvedProjectPath = Resolve-VergilProjectPath -ProjectPath $ProjectPath
19+
$resolvedEditorCmdPath = Resolve-VergilEditorCmdPath -EditorCmdPath $EditorCmdPath -EngineRoot $EngineRoot
20+
$resolvedUnrealBuildToolPath = Resolve-VergilUnrealBuildToolPath -UnrealBuildToolPath $UnrealBuildToolPath -EngineRoot $EngineRoot
21+
$automationScriptPath = Join-Path $PSScriptRoot 'Invoke-VergilScaffoldAutomation.ps1'
22+
$buildScriptPath = Join-Path $PSScriptRoot 'Invoke-VergilProjectBuild.ps1'
23+
$perfSmokeScriptPath = Join-Path $PSScriptRoot 'Invoke-VergilPerfSmoke.ps1'
24+
25+
function Invoke-VergilAutomationLane {
26+
param(
27+
[Parameter(Mandatory = $true)]
28+
[string]$LaneName,
29+
30+
[Parameter(Mandatory = $true)]
31+
[string]$TestFilter,
32+
33+
[string]$LogFileName = 'Automation.log'
34+
)
35+
36+
$outputDirectory = Get-VergilCILaneOutputDirectory -Lane $LaneName -ProjectRoot $projectRoot
37+
Reset-VergilDirectory -Path $outputDirectory
38+
39+
Invoke-VergilPowerShellFile `
40+
-ScriptPath $automationScriptPath `
41+
-WorkingDirectory $projectRoot `
42+
-Arguments @(
43+
'-EditorCmdPath', $resolvedEditorCmdPath,
44+
'-ProjectPath', $resolvedProjectPath,
45+
'-TestFilter', $TestFilter,
46+
'-LogPath', (Join-Path $outputDirectory $LogFileName)
47+
)
48+
}
49+
50+
switch ($Lane) {
51+
'All' {
52+
$buildOutputDirectory = Get-VergilCILaneOutputDirectory -Lane 'Build' -ProjectRoot $projectRoot
53+
Reset-VergilDirectory -Path $buildOutputDirectory
54+
Invoke-VergilPowerShellFile `
55+
-ScriptPath $buildScriptPath `
56+
-WorkingDirectory $projectRoot `
57+
-Arguments @(
58+
'-ProjectPath', $resolvedProjectPath,
59+
'-UnrealBuildToolPath', $resolvedUnrealBuildToolPath,
60+
'-LogPath', (Join-Path $buildOutputDirectory 'UnrealBuildTool.log')
61+
)
62+
63+
Invoke-VergilAutomationLane -LaneName 'HeadlessAutomation' -TestFilter 'Vergil.Scaffold' -LogFileName 'VergilAutomation_HeadlessAutomation.log'
64+
Invoke-VergilAutomationLane -LaneName 'GoldenTests' -TestFilter 'Vergil.Scaffold.GoldenAssetSnapshot' -LogFileName 'GoldenAssetSnapshot.log'
65+
66+
$goldenOutputDirectory = Get-VergilCILaneOutputDirectory -Lane 'GoldenTests' -ProjectRoot $projectRoot
67+
Invoke-VergilPowerShellFile `
68+
-ScriptPath $automationScriptPath `
69+
-WorkingDirectory $projectRoot `
70+
-Arguments @(
71+
'-EditorCmdPath', $resolvedEditorCmdPath,
72+
'-ProjectPath', $resolvedProjectPath,
73+
'-TestFilter', 'Vergil.Scaffold.SourceControlDiff',
74+
'-LogPath', (Join-Path $goldenOutputDirectory 'SourceControlDiff.log')
75+
)
76+
77+
Invoke-VergilAutomationLane -LaneName 'PIRuntime' -TestFilter 'Vergil.Scaffold.PIERuntime' -LogFileName 'PIERuntime.log'
78+
Invoke-VergilPowerShellFile `
79+
-ScriptPath $perfSmokeScriptPath `
80+
-WorkingDirectory $projectRoot `
81+
-Arguments @(
82+
'-EditorCmdPath', $resolvedEditorCmdPath,
83+
'-ProjectPath', $resolvedProjectPath,
84+
'-MaxTotalSeconds', $PerfSmokeMaxTotalSeconds,
85+
'-MaxPerScenarioSeconds', $PerfSmokeMaxPerScenarioSeconds
86+
)
87+
}
88+
'Build' {
89+
$buildOutputDirectory = Get-VergilCILaneOutputDirectory -Lane 'Build' -ProjectRoot $projectRoot
90+
Reset-VergilDirectory -Path $buildOutputDirectory
91+
Invoke-VergilPowerShellFile `
92+
-ScriptPath $buildScriptPath `
93+
-WorkingDirectory $projectRoot `
94+
-Arguments @(
95+
'-ProjectPath', $resolvedProjectPath,
96+
'-UnrealBuildToolPath', $resolvedUnrealBuildToolPath,
97+
'-LogPath', (Join-Path $buildOutputDirectory 'UnrealBuildTool.log')
98+
)
99+
}
100+
'HeadlessAutomation' {
101+
Invoke-VergilAutomationLane -LaneName 'HeadlessAutomation' -TestFilter 'Vergil.Scaffold' -LogFileName 'VergilAutomation_HeadlessAutomation.log'
102+
}
103+
'GoldenTests' {
104+
$outputDirectory = Get-VergilCILaneOutputDirectory -Lane 'GoldenTests' -ProjectRoot $projectRoot
105+
Reset-VergilDirectory -Path $outputDirectory
106+
107+
Invoke-VergilPowerShellFile `
108+
-ScriptPath $automationScriptPath `
109+
-WorkingDirectory $projectRoot `
110+
-Arguments @(
111+
'-EditorCmdPath', $resolvedEditorCmdPath,
112+
'-ProjectPath', $resolvedProjectPath,
113+
'-TestFilter', 'Vergil.Scaffold.GoldenAssetSnapshot',
114+
'-LogPath', (Join-Path $outputDirectory 'GoldenAssetSnapshot.log')
115+
)
116+
117+
Invoke-VergilPowerShellFile `
118+
-ScriptPath $automationScriptPath `
119+
-WorkingDirectory $projectRoot `
120+
-Arguments @(
121+
'-EditorCmdPath', $resolvedEditorCmdPath,
122+
'-ProjectPath', $resolvedProjectPath,
123+
'-TestFilter', 'Vergil.Scaffold.SourceControlDiff',
124+
'-LogPath', (Join-Path $outputDirectory 'SourceControlDiff.log')
125+
)
126+
}
127+
'PIRuntime' {
128+
Invoke-VergilAutomationLane -LaneName 'PIRuntime' -TestFilter 'Vergil.Scaffold.PIERuntime' -LogFileName 'PIERuntime.log'
129+
}
130+
'PerfSmoke' {
131+
Invoke-VergilPowerShellFile `
132+
-ScriptPath $perfSmokeScriptPath `
133+
-WorkingDirectory $projectRoot `
134+
-Arguments @(
135+
'-EditorCmdPath', $resolvedEditorCmdPath,
136+
'-ProjectPath', $resolvedProjectPath,
137+
'-MaxTotalSeconds', $PerfSmokeMaxTotalSeconds,
138+
'-MaxPerScenarioSeconds', $PerfSmokeMaxPerScenarioSeconds
139+
)
140+
}
141+
}

0 commit comments

Comments
 (0)