-
Notifications
You must be signed in to change notification settings - Fork 0
243 lines (223 loc) · 10.8 KB
/
Copy pathci.yml
File metadata and controls
243 lines (223 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
name: CI
on:
push:
branches: [main]
pull_request:
merge_group:
workflow_dispatch:
jobs:
# F-22 package isolation (fork-safe, no Unity license): prove every shipped package.json is a
# self-consistent UPM graph - all five packages versioned in lockstep and every internal
# com.neoxider.* dependency pin matches the pinned package's actual version. Catches a half-done
# version bump (one of A-01/A-02's failure classes) before it reaches a consumer.
package-graph:
name: Package graph (lockstep + deps)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Verify package versions are in lockstep and internal deps resolve
shell: bash
run: |
python3 - <<'PY'
import json, glob, sys
pkgs = {}
for path in glob.glob('Assets/*/package.json'):
with open(path, encoding='utf-8') as f:
data = json.load(f)
pkgs[data['name']] = (data.get('version', ''), data.get('dependencies', {}))
versions = {v[0] for v in pkgs.values()}
ok = True
if len(versions) != 1:
print(f"::error::package versions are not in lockstep: {versions}")
ok = False
for name, (ver, deps) in pkgs.items():
for dep, dver in deps.items():
if dep.startswith('com.neoxider.') and dep in pkgs and dver != pkgs[dep][0]:
print(f"::error::{name} pins {dep}@{dver} but that package is {pkgs[dep][0]}")
ok = False
print("packages:", {n: v[0] for n, v in pkgs.items()})
sys.exit(0 if ok else 1)
PY
# F-12 trusted merge-queue gate: on the merge queue the repository secrets ARE available, so a
# missing UNITY_LICENSE is a misconfiguration, not a forked PR - FAIL loudly instead of skipping,
# so an unlicensed runner can never merge an uncompiled/untested wave (the A-01/A-02 class).
merge-queue-gate:
name: Merge-queue license gate
runs-on: ubuntu-latest
if: github.event_name == 'merge_group'
steps:
- name: Require UNITY_LICENSE on the merge queue
shell: bash
run: |
if [ -z "${{ secrets.UNITY_LICENSE }}" ]; then
echo "::error::UNITY_LICENSE is unavailable on the merge queue; the EditMode/PlayMode Unity"
echo "::error::jobs would skip and let an uncompiled change merge. Configure the secret."
exit 1
fi
echo "UNITY_LICENSE present - Unity jobs will run and gate this merge."
# Secret-free analyzer guard. Uses plain dotnet (no Unity license), so it also runs
# on forked PRs where the Unity jobs are skipped. Building + testing the Roslyn
# analyzer project guards the committed CoreAI.UnityAsyncAnalyzers.dll against drift
# and proves rule CAIU001 (ban ConfigureAwait(false) in CoreAiUnity code) still fires.
analyzer:
name: Roslyn analyzer (dotnet)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
- name: Build analyzer + tests
run: dotnet build tools/CoreAI.Tools.sln -c Release --nologo
- name: Run analyzer tests (verifies CAIU001 fires)
run: dotnet test tools/CoreAI.Tools.sln -c Release --no-build --nologo
editmode-tests:
name: EditMode (${{ matrix.name }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Explicit include list (no cross-product). Three configurations must stay green:
# lua - default project with the bundled Lua-CSharp runtime.
# no-lua - COREAI_NO_LUA build so every Lua feature compiles out.
# no-llm - COREAI_NO_LLM build so the layer under #if !COREAI_NO_LLM compiles out.
include:
- name: lua
lua: lua
no_llm: false
- name: no-lua
lua: no-lua
no_llm: false
- name: no-llm
lua: lua
no_llm: true
steps:
- uses: actions/checkout@v4
- name: Check UNITY_LICENSE availability for this run
id: unity-license-check
shell: bash
run: |
if [ -z "${{ secrets.UNITY_LICENSE }}" ]; then
echo "::warning title=Unity EditMode tests skipped::UNITY_LICENSE is unavailable; EditMode (${{ matrix.name }}) tests were SKIPPED."
echo "- ⚠️ EditMode (${{ matrix.name }}) tests: **SKIPPED** due to missing \`UNITY_LICENSE\`." >> "$GITHUB_STEP_SUMMARY"
if [ "$GITHUB_REPOSITORY" = "NeoXider/CoreAI" ]; then
echo "::error::UNITY_LICENSE is required for Unity tests in NeoXider/CoreAI."
exit 1
fi
echo "run_tests=false" >> "$GITHUB_OUTPUT"
else
echo "run_tests=true" >> "$GITHUB_OUTPUT"
fi
- name: Define COREAI_NO_LUA
if: matrix.lua == 'no-lua'
run: |
# The Lua-CSharp runtime ships bundled inside the CoreAI Mods package (Lua.dll), so there
# is no package to remove — this job proves the project compiles with every Lua feature
# compiled out via the COREAI_NO_LUA define (documented opt-out in
# Assets/CoreAI/Docs/LUA_SANDBOX_SECURITY.md).
# Append the define to every per-platform scriptingDefineSymbols entry.
sed -i 's/: DOTWEEN$/: DOTWEEN;COREAI_NO_LUA/' ProjectSettings/ProjectSettings.asset
grep -q 'COREAI_NO_LUA' ProjectSettings/ProjectSettings.asset || { echo 'COREAI_NO_LUA injection failed'; exit 1; }
- name: Define COREAI_NO_LLM
if: matrix.no_llm
run: |
# Compile out the LLM layer (real code lives under #if !COREAI_NO_LLM) so the
# no-LLM build can't silently break. Mirrors the COREAI_NO_LUA injection above.
sed -i 's/: DOTWEEN$/: DOTWEEN;COREAI_NO_LLM/' ProjectSettings/ProjectSettings.asset
grep -q 'COREAI_NO_LLM' ProjectSettings/ProjectSettings.asset || { echo 'COREAI_NO_LLM injection failed'; exit 1; }
- uses: actions/cache@v4
with:
path: Library
key: library-${{ matrix.name }}-${{ hashFiles('Packages/packages-lock.json', 'ProjectSettings/ProjectVersion.txt') }}
restore-keys: |
library-${{ matrix.name }}-
- name: Run EditMode tests
if: steps.unity-license-check.outputs.run_tests == 'true'
uses: game-ci/unity-test-runner@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
testMode: editmode
artifactsPath: artifacts-${{ matrix.name }}
checkName: EditMode results (${{ matrix.name }})
githubToken: ${{ secrets.GITHUB_TOKEN }}
- name: Enforce Lua sandbox escape-test coverage
if: matrix.lua == 'lua' && matrix.no_llm == false && steps.unity-license-check.outputs.run_tests == 'true'
run: |
# The sandbox isolation suite must actually have run — a green build with
# 0 executed escape tests (filter typo, asmdef regression, define drift)
# must fail, not pass silently.
count=$(grep -o 'LuaCsSecureSandboxEditModeTests' artifacts-${{ matrix.name }}/editmode-results.xml | wc -l)
echo "LuaCsSecureSandboxEditModeTests occurrences in results: $count"
if [ "$count" -lt 3 ]; then
echo "::error::Lua sandbox escape tests did not run (expected the LuaCsSecureSandboxEditModeTests fixture in EditMode results)."
exit 1
fi
- uses: actions/upload-artifact@v4
if: always() && steps.unity-license-check.outputs.run_tests == 'true'
with:
name: test-results-${{ matrix.name }}
path: artifacts-${{ matrix.name }}
# Deterministic, no-LLM PlayMode suite (main-thread marshaling, streaming/non-streaming
# parity, stop/cancel). Filtered to the FastNoLlm test assembly so the live LLM
# benchmark / LlmVerification fixtures (which need a model server) never run here.
playmode-fastnollm:
name: PlayMode (FastNoLlm)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check UNITY_LICENSE availability for this run
id: unity-license-check
shell: bash
run: |
if [ -z "${{ secrets.UNITY_LICENSE }}" ]; then
echo "::warning title=Unity PlayMode tests skipped::UNITY_LICENSE is unavailable; PlayMode (FastNoLlm) tests were SKIPPED."
echo "- ⚠️ PlayMode (FastNoLlm) tests: **SKIPPED** due to missing \`UNITY_LICENSE\`." >> "$GITHUB_STEP_SUMMARY"
if [ "$GITHUB_REPOSITORY" = "NeoXider/CoreAI" ]; then
echo "::error::UNITY_LICENSE is required for Unity tests in NeoXider/CoreAI."
exit 1
fi
echo "run_tests=false" >> "$GITHUB_OUTPUT"
else
echo "run_tests=true" >> "$GITHUB_OUTPUT"
fi
- uses: actions/cache@v4
with:
path: Library
key: library-playmode-${{ hashFiles('Packages/packages-lock.json', 'ProjectSettings/ProjectVersion.txt') }}
restore-keys: |
library-playmode-
- name: Run PlayMode tests (FastNoLlm assembly only)
if: steps.unity-license-check.outputs.run_tests == 'true'
uses: game-ci/unity-test-runner@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
testMode: playmode
# The deterministic suite has no NUnit [Category]; isolate it by assembly name.
# The LlmVerification / benchmark fixtures live in separate assemblies and are
# excluded by not naming them here.
customParameters: -assemblyNames "CoreAI.Tests.PlayMode.FastNoLlm"
artifactsPath: artifacts-playmode
checkName: PlayMode results (FastNoLlm)
githubToken: ${{ secrets.GITHUB_TOKEN }}
- name: Enforce FastNoLlm PlayMode suite actually ran
if: steps.unity-license-check.outputs.run_tests == 'true'
run: |
# A green build with 0 executed FastNoLlm tests (filter typo, asmdef rename,
# define drift) must fail, not pass silently.
count=$(grep -o 'PlayModeTests' artifacts-playmode/playmode-results.xml | wc -l)
echo "PlayMode FastNoLlm test-case occurrences in results: $count"
if [ "$count" -lt 10 ]; then
echo "::error::FastNoLlm PlayMode suite did not run (expected the CoreAI.Tests.PlayMode.FastNoLlm fixtures in PlayMode results)."
exit 1
fi
- uses: actions/upload-artifact@v4
if: always() && steps.unity-license-check.outputs.run_tests == 'true'
with:
name: test-results-playmode
path: artifacts-playmode