Type Unity dependency ownership
This commit is contained in:
parent
f12a1d1e98
commit
9dc4e0ef81
16 changed files with 341 additions and 25 deletions
|
|
@ -557,8 +557,9 @@ Recently cut:
|
||||||
`testables` manifest mutation. The lifecycle smoke validates the runner
|
`testables` manifest mutation. The lifecycle smoke validates the runner
|
||||||
script and Aetheria project, parity compares the contract against the ledger,
|
script and Aetheria project, parity compares the contract against the ledger,
|
||||||
and the runtime-owner consumer smoke asserts exported test fields. This is
|
and the runtime-owner consumer smoke asserts exported test fields. This is
|
||||||
Unity package consumption proof for CultLib-produced assemblies; CultLib
|
Unity package consumption proof for CultLib-produced assemblies; the test
|
||||||
remains the owner of the .NET/NuGet dependency story.
|
contract names those CultLib-owned NuGet/precompiled assembly inputs
|
||||||
|
explicitly. CultLib remains the owner of the .NET/NuGet dependency story.
|
||||||
- EveUnity release lifecycle now carries a structured UPM release contract.
|
- EveUnity release lifecycle now carries a structured UPM release contract.
|
||||||
`packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json`
|
`packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json`
|
||||||
names the package root, version source, tag pattern, artifact kind, and
|
names the package root, version source, tag pattern, artifact kind, and
|
||||||
|
|
@ -566,10 +567,11 @@ Recently cut:
|
||||||
`tools/eveunity/eveunity-release-contract.mjs` now builds a
|
`tools/eveunity/eveunity-release-contract.mjs` now builds a
|
||||||
`gamecult.eve.runtime_release_request.v1` request from the runtime capability
|
`gamecult.eve.runtime_release_request.v1` request from the runtime capability
|
||||||
manifest and UPM package manifest, deriving the `eveunity-uitoolkit-v{version}`
|
manifest and UPM package manifest, deriving the `eveunity-uitoolkit-v{version}`
|
||||||
tag, package dependency set, and UPM artifact path. The lifecycle smoke
|
tag, package dependency set, required package dependency owners, and UPM
|
||||||
validates that contract against the package manifest, parity compares it
|
artifact path. The lifecycle smoke validates that contract against the package
|
||||||
against the ledger, and the runtime-owner consumer smoke asserts the exported
|
manifest, parity compares it against the ledger, and the runtime-owner
|
||||||
release fields without pretending the tagged EveUnity release already exists.
|
consumer smoke asserts the exported release fields without pretending the
|
||||||
|
tagged EveUnity release already exists.
|
||||||
- EveUnity capture lifecycle now carries a structured pending capture contract.
|
- EveUnity capture lifecycle now carries a structured pending capture contract.
|
||||||
`captureContract` names the Unity UI Toolkit runtime, capture kind, PNG
|
`captureContract` names the Unity UI Toolkit runtime, capture kind, PNG
|
||||||
artifact pattern, conformance attachment point, required Aetheria provider
|
artifact pattern, conformance attachment point, required Aetheria provider
|
||||||
|
|
@ -852,11 +854,12 @@ Recently cut:
|
||||||
`GameCult.Eve.UnityUIToolkit.Tests` in Unity batchmode, writes XML and log
|
`GameCult.Eve.UnityUIToolkit.Tests` in Unity batchmode, writes XML and log
|
||||||
artifacts under `artifacts/aetheria-unity-editmode`, and restores
|
artifacts under `artifacts/aetheria-unity-editmode`, and restores
|
||||||
`Packages/manifest.json` before returning. The Brokkr/CultMesh DLL references
|
`Packages/manifest.json` before returning. The Brokkr/CultMesh DLL references
|
||||||
in the Unity test asmdef are Unity package assembly plumbing: CultLib already
|
in the Unity test asmdef are now mirrored by
|
||||||
owns the .NET/NuGet dependency story. EveUnity's split blocker is proving how
|
`testContract.managedAssemblyDependencies`, with CultLib named as the
|
||||||
the Unity package resolves those assemblies after it leaves Eve incubation,
|
NuGet/package owner. EveUnity's split blocker is proving how the Unity package
|
||||||
not inventing a dependency system for CultLib. This proves the incubating test
|
resolves those assemblies after it leaves Eve incubation, not inventing a
|
||||||
lifecycle; it does not make Eve own Unity's final runtime lifecycle.
|
dependency system for CultLib. This proves the incubating test lifecycle; it
|
||||||
|
does not make Eve own Unity's final runtime lifecycle.
|
||||||
- Aetheria now has repeatable Unity package consumer-build evidence for the
|
- Aetheria now has repeatable Unity package consumer-build evidence for the
|
||||||
Eve UI Toolkit runtime. `scripts/run-aetheria-unity-package-smoke.ps1`
|
Eve UI Toolkit runtime. `scripts/run-aetheria-unity-package-smoke.ps1`
|
||||||
verifies Aetheria's Unity `Packages/manifest.json` consumes Eve's surface and
|
verifies Aetheria's Unity `Packages/manifest.json` consumes Eve's surface and
|
||||||
|
|
|
||||||
|
|
@ -404,10 +404,12 @@ exist. The current lifecycle proof is deliberately split:
|
||||||
|
|
||||||
- release: `tools/eveunity/eveunity-release-contract.mjs` builds a typed
|
- release: `tools/eveunity/eveunity-release-contract.mjs` builds a typed
|
||||||
`gamecult.eve.runtime_release_request.v1` request from the runtime capability
|
`gamecult.eve.runtime_release_request.v1` request from the runtime capability
|
||||||
manifest and UPM package manifest; the tagged EveUnity release remains a
|
manifest and UPM package manifest, including required package dependency
|
||||||
split blocker;
|
owners; the tagged EveUnity release remains a split blocker;
|
||||||
- test: package-owned EditMode tests run through Aetheria in Unity batchmode,
|
- test: package-owned EditMode tests run through Aetheria in Unity batchmode,
|
||||||
and Aetheria can build the package through Unity's generated project;
|
and Aetheria can build the package through Unity's generated project. The
|
||||||
|
test contract names CultLib-owned NuGet/precompiled assembly inputs without
|
||||||
|
making EveUnity own CultLib package distribution;
|
||||||
- capture: `tools/eveunity/eveunity-capture-contract.mjs` builds a typed
|
- capture: `tools/eveunity/eveunity-capture-contract.mjs` builds a typed
|
||||||
`gamecult.eve.runtime_capture_request.v1` request from the runtime capability
|
`gamecult.eve.runtime_capture_request.v1` request from the runtime capability
|
||||||
manifest and Aetheria provider advertisement; Unity editor or batchmode PNG
|
manifest and Aetheria provider advertisement; Unity editor or batchmode PNG
|
||||||
|
|
|
||||||
|
|
@ -50,16 +50,18 @@ its version from `package.json`, and uses tag pattern
|
||||||
`tools/eveunity/eveunity-release-contract.mjs` builds a
|
`tools/eveunity/eveunity-release-contract.mjs` builds a
|
||||||
`gamecult.eve.runtime_release_request.v1` request from the runtime capability
|
`gamecult.eve.runtime_release_request.v1` request from the runtime capability
|
||||||
manifest and UPM package manifest. The request names the package version, tag,
|
manifest and UPM package manifest. The request names the package version, tag,
|
||||||
artifact path, dependency set, and target `GameCult/EveUnity` repository without
|
artifact path, dependency set, required package dependency owners, and target
|
||||||
pretending Eve has published the tagged release.
|
`GameCult/EveUnity` repository without pretending Eve has published the tagged
|
||||||
|
release.
|
||||||
|
|
||||||
The test stage declares the Unity EditMode runner contract: the current runner
|
The test stage declares the Unity EditMode runner contract: the current runner
|
||||||
is `scripts/run-aetheria-unity-editmode-tests.ps1`, runs
|
is `scripts/run-aetheria-unity-editmode-tests.ps1`, runs
|
||||||
`GameCult.Eve.UnityUIToolkit.Tests` on `EditMode`, writes XML and log artifacts
|
`GameCult.Eve.UnityUIToolkit.Tests` on `EditMode`, writes XML and log artifacts
|
||||||
under `artifacts/aetheria-unity-editmode/{stamp}`, and temporarily adds
|
under `artifacts/aetheria-unity-editmode/{stamp}`, and temporarily adds
|
||||||
`org.gamecult.eve.unity-uitoolkit` to Aetheria's Unity `testables`. This is a
|
`org.gamecult.eve.unity-uitoolkit` to Aetheria's Unity `testables`. This is a
|
||||||
Unity package consumption proof. CultLib still owns the .NET/NuGet dependency
|
Unity package consumption proof. The test contract names the CultLib-owned
|
||||||
story for its assemblies.
|
NuGet/precompiled assembly inputs Unity needs during incubation; CultLib still
|
||||||
|
owns the .NET/NuGet dependency story for its assemblies.
|
||||||
|
|
||||||
The capture stage declares `gamecult.eve.runtime_capture_request.v1` request
|
The capture stage declares `gamecult.eve.runtime_capture_request.v1` request
|
||||||
construction through `tools/eveunity/eveunity-capture-contract.mjs`. That
|
construction through `tools/eveunity/eveunity-capture-contract.mjs`. That
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,16 @@
|
||||||
"requestSchema": "gamecult.eve.runtime_release_request.v1",
|
"requestSchema": "gamecult.eve.runtime_release_request.v1",
|
||||||
"requestBuilder": "tools/eveunity/eveunity-release-contract.mjs",
|
"requestBuilder": "tools/eveunity/eveunity-release-contract.mjs",
|
||||||
"artifactPattern": "artifacts/eveunity-uitoolkit-release/{version}/org.gamecult.eve.unity-uitoolkit-{version}.tgz",
|
"artifactPattern": "artifacts/eveunity-uitoolkit-release/{version}/org.gamecult.eve.unity-uitoolkit-{version}.tgz",
|
||||||
"publishProof": "Tagged UPM release is created from the EveUnity repo and references this package root."
|
"publishProof": "Tagged UPM release is created from the EveUnity repo and references this package root.",
|
||||||
|
"requiredPackageDependencies": [
|
||||||
|
{
|
||||||
|
"packageName": "org.gamecult.eve.surface",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"packageManager": "upm",
|
||||||
|
"ownerRepo": "Eve",
|
||||||
|
"purpose": "Shared Unity DTOs for gamecult.eve.surface.v1 documents."
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"evidencePaths": [
|
"evidencePaths": [
|
||||||
"packages/org.gamecult.eve.unity-uitoolkit/package.json",
|
"packages/org.gamecult.eve.unity-uitoolkit/package.json",
|
||||||
|
|
@ -110,7 +119,30 @@
|
||||||
"testPlatform": "EditMode",
|
"testPlatform": "EditMode",
|
||||||
"resultsArtifact": "artifacts/aetheria-unity-editmode/{stamp}/unity-editmode-results.xml",
|
"resultsArtifact": "artifacts/aetheria-unity-editmode/{stamp}/unity-editmode-results.xml",
|
||||||
"logArtifact": "artifacts/aetheria-unity-editmode/{stamp}/unity-editmode.log",
|
"logArtifact": "artifacts/aetheria-unity-editmode/{stamp}/unity-editmode.log",
|
||||||
"manifestMutation": "Temporarily adds org.gamecult.eve.unity-uitoolkit to Aetheria Packages/manifest.json testables and restores the manifest after the run."
|
"manifestMutation": "Temporarily adds org.gamecult.eve.unity-uitoolkit to Aetheria Packages/manifest.json testables and restores the manifest after the run.",
|
||||||
|
"managedAssemblyDependencies": [
|
||||||
|
{
|
||||||
|
"assemblyName": "GameCult.Caching.dll",
|
||||||
|
"packageId": "GameCult.Caching",
|
||||||
|
"packageManager": "nuget",
|
||||||
|
"ownerRepo": "CultLib",
|
||||||
|
"resolutionMode": "Unity precompiled reference during EveUnity incubation."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assemblyName": "GameCult.Mesh.dll",
|
||||||
|
"packageId": "GameCult.Mesh",
|
||||||
|
"packageManager": "nuget",
|
||||||
|
"ownerRepo": "CultLib",
|
||||||
|
"resolutionMode": "Unity precompiled reference during EveUnity incubation."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assemblyName": "MessagePack.Annotations.dll",
|
||||||
|
"packageId": "MessagePack.Annotations",
|
||||||
|
"packageManager": "nuget",
|
||||||
|
"ownerRepo": "CultLib",
|
||||||
|
"resolutionMode": "Unity precompiled reference during EveUnity incubation."
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"evidencePaths": [
|
"evidencePaths": [
|
||||||
"packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef",
|
"packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef",
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,16 @@
|
||||||
"requestSchema": "gamecult.eve.runtime_release_request.v1",
|
"requestSchema": "gamecult.eve.runtime_release_request.v1",
|
||||||
"requestBuilder": "tools/eveunity/eveunity-release-contract.mjs",
|
"requestBuilder": "tools/eveunity/eveunity-release-contract.mjs",
|
||||||
"artifactPattern": "artifacts/eveunity-scene-release/{version}/org.gamecult.eve.unity-scene-{version}.tgz",
|
"artifactPattern": "artifacts/eveunity-scene-release/{version}/org.gamecult.eve.unity-scene-{version}.tgz",
|
||||||
"publishProof": "Tagged Unity scene package release is created from the EveUnity repo and references the provider-agnostic scene package root."
|
"publishProof": "Tagged Unity scene package release is created from the EveUnity repo and references the provider-agnostic scene package root.",
|
||||||
|
"requiredPackageDependencies": [
|
||||||
|
{
|
||||||
|
"packageName": "org.gamecult.eve.surface",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"packageManager": "upm",
|
||||||
|
"ownerRepo": "Eve",
|
||||||
|
"purpose": "Shared Unity DTOs for gamecult.eve.surface.v1 documents."
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"evidencePaths": [
|
"evidencePaths": [
|
||||||
"runtimes/incubating/eve-unity-scene/package.json",
|
"runtimes/incubating/eve-unity-scene/package.json",
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,40 @@
|
||||||
"publishProof": {
|
"publishProof": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"requiredPackageDependencies": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"packageName",
|
||||||
|
"version",
|
||||||
|
"packageManager",
|
||||||
|
"ownerRepo"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"packageName": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"packageManager": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"ownerRepo": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"purpose": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": true
|
"additionalProperties": true
|
||||||
|
|
@ -352,6 +386,42 @@
|
||||||
"manifestMutation": {
|
"manifestMutation": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"managedAssemblyDependencies": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"assemblyName",
|
||||||
|
"packageId",
|
||||||
|
"packageManager",
|
||||||
|
"ownerRepo",
|
||||||
|
"resolutionMode"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"assemblyName": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"packageId": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"packageManager": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"ownerRepo": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"resolutionMode": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": true
|
"additionalProperties": true
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,40 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"requiredPackageDependencies": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"packageName",
|
||||||
|
"version",
|
||||||
|
"packageManager",
|
||||||
|
"ownerRepo"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"packageName": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"packageManager": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"ownerRepo": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"purpose": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"type": "object"
|
"type": "object"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,16 @@ if (-not $packageManifest.version) {
|
||||||
if ($packageManifest.dependencies."org.gamecult.eve.surface" -ne "0.1.0") {
|
if ($packageManifest.dependencies."org.gamecult.eve.surface" -ne "0.1.0") {
|
||||||
throw "EveUnity package manifest missing org.gamecult.eve.surface dependency"
|
throw "EveUnity package manifest missing org.gamecult.eve.surface dependency"
|
||||||
}
|
}
|
||||||
|
$surfaceDependency = @($releaseContract.requiredPackageDependencies) | Where-Object { $_.packageName -eq "org.gamecult.eve.surface" } | Select-Object -First 1
|
||||||
|
if (-not $surfaceDependency) {
|
||||||
|
throw "EveUnity release contract missing required org.gamecult.eve.surface dependency contract"
|
||||||
|
}
|
||||||
|
if ($surfaceDependency.ownerRepo -ne "Eve" -or $surfaceDependency.packageManager -ne "upm") {
|
||||||
|
throw "EveUnity release contract has unexpected surface dependency owner/package manager: $($surfaceDependency.ownerRepo)/$($surfaceDependency.packageManager)"
|
||||||
|
}
|
||||||
|
if ($surfaceDependency.version -ne $packageManifest.dependencies."org.gamecult.eve.surface") {
|
||||||
|
throw "EveUnity release contract surface dependency version mismatch: $($surfaceDependency.version)"
|
||||||
|
}
|
||||||
|
|
||||||
& (Join-Path $projectRoot "scripts\run-eveunity-release-contract-smoke.ps1")
|
& (Join-Path $projectRoot "scripts\run-eveunity-release-contract-smoke.ps1")
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
|
@ -155,6 +165,25 @@ if ($testContract.testAssembly -ne "GameCult.Eve.UnityUIToolkit.Tests") {
|
||||||
if ($testContract.testPlatform -ne "EditMode") {
|
if ($testContract.testPlatform -ne "EditMode") {
|
||||||
throw "EveUnity test contract has unexpected test platform: $($testContract.testPlatform)"
|
throw "EveUnity test contract has unexpected test platform: $($testContract.testPlatform)"
|
||||||
}
|
}
|
||||||
|
$managedDependencies = @($testContract.managedAssemblyDependencies)
|
||||||
|
$testAssemblyDefinitionPath = Join-Path $projectRoot "packages\org.gamecult.eve.unity-uitoolkit\Tests\Editor\GameCult.Eve.UnityUIToolkit.Tests.asmdef"
|
||||||
|
$testAssemblyDefinition = Get-Content -LiteralPath $testAssemblyDefinitionPath -Raw | ConvertFrom-Json
|
||||||
|
foreach ($dependency in @(
|
||||||
|
@{ assemblyName = "GameCult.Caching.dll"; packageId = "GameCult.Caching" },
|
||||||
|
@{ assemblyName = "GameCult.Mesh.dll"; packageId = "GameCult.Mesh" },
|
||||||
|
@{ assemblyName = "MessagePack.Annotations.dll"; packageId = "MessagePack.Annotations" }
|
||||||
|
)) {
|
||||||
|
$record = $managedDependencies | Where-Object { $_.assemblyName -eq $dependency.assemblyName } | Select-Object -First 1
|
||||||
|
if (-not $record) {
|
||||||
|
throw "EveUnity test contract missing managed assembly dependency: $($dependency.assemblyName)"
|
||||||
|
}
|
||||||
|
if ($record.packageId -ne $dependency.packageId -or $record.packageManager -ne "nuget" -or $record.ownerRepo -ne "CultLib") {
|
||||||
|
throw "EveUnity test contract has unexpected managed dependency record for $($dependency.assemblyName)"
|
||||||
|
}
|
||||||
|
if (-not (@($testAssemblyDefinition.precompiledReferences) -contains $dependency.assemblyName)) {
|
||||||
|
throw "EveUnity test asmdef missing managed dependency precompiled reference: $($dependency.assemblyName)"
|
||||||
|
}
|
||||||
|
}
|
||||||
foreach ($pathProperty in @("runnerScript", "consumerProject")) {
|
foreach ($pathProperty in @("runnerScript", "consumerProject")) {
|
||||||
$candidate = $testContract.$pathProperty
|
$candidate = $testContract.$pathProperty
|
||||||
if (-not $candidate) {
|
if (-not $candidate) {
|
||||||
|
|
|
||||||
|
|
@ -91,5 +91,15 @@ if ($request.artifactPath -ne $expectedArtifactPath) {
|
||||||
if ($request.dependencies."org.gamecult.eve.surface" -ne $packageManifest.dependencies."org.gamecult.eve.surface") {
|
if ($request.dependencies."org.gamecult.eve.surface" -ne $packageManifest.dependencies."org.gamecult.eve.surface") {
|
||||||
throw "EveUnity release request lost org.gamecult.eve.surface dependency"
|
throw "EveUnity release request lost org.gamecult.eve.surface dependency"
|
||||||
}
|
}
|
||||||
|
$surfaceDependency = @($request.requiredPackageDependencies) | Where-Object { $_.packageName -eq "org.gamecult.eve.surface" } | Select-Object -First 1
|
||||||
|
if (-not $surfaceDependency) {
|
||||||
|
throw "EveUnity release request missing required org.gamecult.eve.surface dependency contract"
|
||||||
|
}
|
||||||
|
if ($surfaceDependency.ownerRepo -ne "Eve" -or $surfaceDependency.packageManager -ne "upm") {
|
||||||
|
throw "EveUnity release request has unexpected surface dependency owner/package manager: $($surfaceDependency.ownerRepo)/$($surfaceDependency.packageManager)"
|
||||||
|
}
|
||||||
|
if ($surfaceDependency.version -ne $packageManifest.dependencies."org.gamecult.eve.surface") {
|
||||||
|
throw "EveUnity release request surface dependency version mismatch: $($surfaceDependency.version)"
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host "EveUnity release contract smoke passed: $absoluteOutputPath"
|
Write-Host "EveUnity release contract smoke passed: $absoluteOutputPath"
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,17 @@ foreach ($pathProperty in @("requestBuilder")) {
|
||||||
throw "EveUnity scene release contract $pathProperty does not exist: $relativePath"
|
throw "EveUnity scene release contract $pathProperty does not exist: $relativePath"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$packageManifest = Get-Content -LiteralPath (Join-Path $projectRoot $releaseContract.versionSource) -Raw | ConvertFrom-Json
|
||||||
|
$surfaceDependency = @($releaseContract.requiredPackageDependencies) | Where-Object { $_.packageName -eq "org.gamecult.eve.surface" } | Select-Object -First 1
|
||||||
|
if (-not $surfaceDependency) {
|
||||||
|
throw "EveUnity scene release contract missing required org.gamecult.eve.surface dependency contract"
|
||||||
|
}
|
||||||
|
if ($surfaceDependency.ownerRepo -ne "Eve" -or $surfaceDependency.packageManager -ne "upm") {
|
||||||
|
throw "EveUnity scene release contract has unexpected surface dependency owner/package manager: $($surfaceDependency.ownerRepo)/$($surfaceDependency.packageManager)"
|
||||||
|
}
|
||||||
|
if ($surfaceDependency.version -ne $packageManifest.dependencies."org.gamecult.eve.surface") {
|
||||||
|
throw "EveUnity scene release contract surface dependency version mismatch: $($surfaceDependency.version)"
|
||||||
|
}
|
||||||
|
|
||||||
$testContract = $manifest.lifecycle.test.testContract
|
$testContract = $manifest.lifecycle.test.testContract
|
||||||
foreach ($field in @("ownerRepo", "runnerKind", "runnerScript", "consumerProject", "packageName", "testAssembly", "testPlatform", "resultsArtifact", "logArtifact", "manifestMutation")) {
|
foreach ($field in @("ownerRepo", "runnerKind", "runnerScript", "consumerProject", "packageName", "testAssembly", "testPlatform", "resultsArtifact", "logArtifact", "manifestMutation")) {
|
||||||
|
|
|
||||||
|
|
@ -94,5 +94,15 @@ if ($request.artifactPath -ne $expectedArtifactPath) {
|
||||||
if ($request.dependencies."org.gamecult.eve.surface" -ne $packageManifest.dependencies."org.gamecult.eve.surface") {
|
if ($request.dependencies."org.gamecult.eve.surface" -ne $packageManifest.dependencies."org.gamecult.eve.surface") {
|
||||||
throw "EveUnity scene release request lost org.gamecult.eve.surface dependency"
|
throw "EveUnity scene release request lost org.gamecult.eve.surface dependency"
|
||||||
}
|
}
|
||||||
|
$surfaceDependency = @($request.requiredPackageDependencies) | Where-Object { $_.packageName -eq "org.gamecult.eve.surface" } | Select-Object -First 1
|
||||||
|
if (-not $surfaceDependency) {
|
||||||
|
throw "EveUnity scene release request missing required org.gamecult.eve.surface dependency contract"
|
||||||
|
}
|
||||||
|
if ($surfaceDependency.ownerRepo -ne "Eve" -or $surfaceDependency.packageManager -ne "upm") {
|
||||||
|
throw "EveUnity scene release request has unexpected surface dependency owner/package manager: $($surfaceDependency.ownerRepo)/$($surfaceDependency.packageManager)"
|
||||||
|
}
|
||||||
|
if ($surfaceDependency.version -ne $packageManifest.dependencies."org.gamecult.eve.surface") {
|
||||||
|
throw "EveUnity scene release request surface dependency version mismatch: $($surfaceDependency.version)"
|
||||||
|
}
|
||||||
|
|
||||||
Write-Host "EveUnity scene release contract smoke passed: $absoluteOutputPath"
|
Write-Host "EveUnity scene release contract smoke passed: $absoluteOutputPath"
|
||||||
|
|
|
||||||
|
|
@ -165,11 +165,17 @@ node $consumerScript $consumerExport `
|
||||||
--expect-runtime-lifecycle-field "unity-uitoolkit:release:releaseContract.artifactPattern:artifacts/eveunity-uitoolkit-release/{version}/org.gamecult.eve.unity-uitoolkit-{version}.tgz" `
|
--expect-runtime-lifecycle-field "unity-uitoolkit:release:releaseContract.artifactPattern:artifacts/eveunity-uitoolkit-release/{version}/org.gamecult.eve.unity-uitoolkit-{version}.tgz" `
|
||||||
--expect-runtime-lifecycle-field "unity-uitoolkit:release:releaseContract.tagPattern:eveunity-uitoolkit-v{version}" `
|
--expect-runtime-lifecycle-field "unity-uitoolkit:release:releaseContract.tagPattern:eveunity-uitoolkit-v{version}" `
|
||||||
--expect-runtime-lifecycle-field "unity-uitoolkit:release:releaseContract.packageRoot:packages/org.gamecult.eve.unity-uitoolkit" `
|
--expect-runtime-lifecycle-field "unity-uitoolkit:release:releaseContract.packageRoot:packages/org.gamecult.eve.unity-uitoolkit" `
|
||||||
|
--expect-runtime-lifecycle-field "unity-uitoolkit:release:releaseContract.requiredPackageDependencies.0.packageName:org.gamecult.eve.surface" `
|
||||||
|
--expect-runtime-lifecycle-field "unity-uitoolkit:release:releaseContract.requiredPackageDependencies.0.ownerRepo:Eve" `
|
||||||
|
--expect-runtime-lifecycle-field "unity-uitoolkit:release:releaseContract.requiredPackageDependencies.0.packageManager:upm" `
|
||||||
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.runnerKind:unity-editmode-batchmode" `
|
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.runnerKind:unity-editmode-batchmode" `
|
||||||
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.runnerScript:scripts/run-aetheria-unity-editmode-tests.ps1" `
|
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.runnerScript:scripts/run-aetheria-unity-editmode-tests.ps1" `
|
||||||
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.packageName:org.gamecult.eve.unity-uitoolkit" `
|
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.packageName:org.gamecult.eve.unity-uitoolkit" `
|
||||||
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.testAssembly:GameCult.Eve.UnityUIToolkit.Tests" `
|
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.testAssembly:GameCult.Eve.UnityUIToolkit.Tests" `
|
||||||
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.testPlatform:EditMode" `
|
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.testPlatform:EditMode" `
|
||||||
|
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.managedAssemblyDependencies.0.ownerRepo:CultLib" `
|
||||||
|
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.managedAssemblyDependencies.0.packageManager:nuget" `
|
||||||
|
--expect-runtime-lifecycle-field "unity-uitoolkit:test:testContract.managedAssemblyDependencies.1.packageId:GameCult.Mesh" `
|
||||||
--expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.captureKind:unity-editor-or-batchmode-png" `
|
--expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.captureKind:unity-editor-or-batchmode-png" `
|
||||||
--expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.artifactKind:png" `
|
--expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.artifactKind:png" `
|
||||||
--expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.targetId:unity-uitoolkit" `
|
--expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.targetId:unity-uitoolkit" `
|
||||||
|
|
@ -192,6 +198,9 @@ node $consumerScript $consumerExport `
|
||||||
--expect-runtime-lifecycle-field "unity-scene:release:releaseContract.requestSchema:gamecult.eve.runtime_release_request.v1" `
|
--expect-runtime-lifecycle-field "unity-scene:release:releaseContract.requestSchema:gamecult.eve.runtime_release_request.v1" `
|
||||||
--expect-runtime-lifecycle-field "unity-scene:release:releaseContract.requestBuilder:tools/eveunity/eveunity-release-contract.mjs" `
|
--expect-runtime-lifecycle-field "unity-scene:release:releaseContract.requestBuilder:tools/eveunity/eveunity-release-contract.mjs" `
|
||||||
--expect-runtime-lifecycle-field "unity-scene:release:releaseContract.artifactPattern:artifacts/eveunity-scene-release/{version}/org.gamecult.eve.unity-scene-{version}.tgz" `
|
--expect-runtime-lifecycle-field "unity-scene:release:releaseContract.artifactPattern:artifacts/eveunity-scene-release/{version}/org.gamecult.eve.unity-scene-{version}.tgz" `
|
||||||
|
--expect-runtime-lifecycle-field "unity-scene:release:releaseContract.requiredPackageDependencies.0.packageName:org.gamecult.eve.surface" `
|
||||||
|
--expect-runtime-lifecycle-field "unity-scene:release:releaseContract.requiredPackageDependencies.0.ownerRepo:Eve" `
|
||||||
|
--expect-runtime-lifecycle-field "unity-scene:release:releaseContract.requiredPackageDependencies.0.packageManager:upm" `
|
||||||
--expect-runtime-lifecycle-field "unity-scene:test:testContract.runnerKind:unity-scene-provider-shell-smoke" `
|
--expect-runtime-lifecycle-field "unity-scene:test:testContract.runnerKind:unity-scene-provider-shell-smoke" `
|
||||||
--expect-runtime-lifecycle-field "unity-scene:test:testContract.runnerScript:scripts/run-eveunity-scene-provider-shell-smoke.ps1" `
|
--expect-runtime-lifecycle-field "unity-scene:test:testContract.runnerScript:scripts/run-eveunity-scene-provider-shell-smoke.ps1" `
|
||||||
--expect-runtime-lifecycle-field "unity-scene:capture:captureContract.captureKind:unity-scene-frame-png" `
|
--expect-runtime-lifecycle-field "unity-scene:capture:captureContract.captureKind:unity-scene-frame-png" `
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,21 @@ export function buildUnityReleaseRequest({
|
||||||
if (!packageManifest.version || !/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(packageManifest.version)) {
|
if (!packageManifest.version || !/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(packageManifest.version)) {
|
||||||
throw new Error(`package version is not a SemVer-compatible release version: ${packageManifest.version || "missing"}`);
|
throw new Error(`package version is not a SemVer-compatible release version: ${packageManifest.version || "missing"}`);
|
||||||
}
|
}
|
||||||
if (packageManifest.dependencies && !packageManifest.dependencies["org.gamecult.eve.surface"]) {
|
const requiredPackageDependencies = Array.isArray(releaseContract.requiredPackageDependencies)
|
||||||
throw new Error("Unity UI Toolkit package must depend on org.gamecult.eve.surface");
|
? releaseContract.requiredPackageDependencies
|
||||||
|
: [{ packageName: "org.gamecult.eve.surface", version: "0.1.0", packageManager: "upm", ownerRepo: "Eve" }];
|
||||||
|
const packageDependencies = packageManifest.dependencies || {};
|
||||||
|
for (const dependency of requiredPackageDependencies) {
|
||||||
|
if (!dependency?.packageName) {
|
||||||
|
throw new Error("release contract requiredPackageDependencies entries must name packageName");
|
||||||
|
}
|
||||||
|
const actualVersion = packageDependencies[dependency.packageName];
|
||||||
|
if (!actualVersion) {
|
||||||
|
throw new Error(`${releaseContract.packageName} must depend on ${dependency.packageName}`);
|
||||||
|
}
|
||||||
|
if (dependency.version && actualVersion !== dependency.version) {
|
||||||
|
throw new Error(`${releaseContract.packageName} dependency ${dependency.packageName} expected ${dependency.version} got ${actualVersion}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagName = replaceVersion(releaseContract.tagPattern, packageManifest.version);
|
const tagName = replaceVersion(releaseContract.tagPattern, packageManifest.version);
|
||||||
|
|
@ -54,6 +67,7 @@ export function buildUnityReleaseRequest({
|
||||||
sourceCapabilityManifestPath: capabilityManifestPath,
|
sourceCapabilityManifestPath: capabilityManifestPath,
|
||||||
sourcePackageManifestPath: packageManifestPath,
|
sourcePackageManifestPath: packageManifestPath,
|
||||||
dependencies: packageManifest.dependencies || {},
|
dependencies: packageManifest.dependencies || {},
|
||||||
|
requiredPackageDependencies,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,15 @@ test("builds EveUnity UPM release request from package manifest", () => {
|
||||||
assert.equal(request.artifactKind, "upm-package");
|
assert.equal(request.artifactKind, "upm-package");
|
||||||
assert.equal(request.artifactPath, "artifacts/eveunity-uitoolkit-release/0.1.0/org.gamecult.eve.unity-uitoolkit-0.1.0.tgz");
|
assert.equal(request.artifactPath, "artifacts/eveunity-uitoolkit-release/0.1.0/org.gamecult.eve.unity-uitoolkit-0.1.0.tgz");
|
||||||
assert.equal(request.dependencies["org.gamecult.eve.surface"], "0.1.0");
|
assert.equal(request.dependencies["org.gamecult.eve.surface"], "0.1.0");
|
||||||
|
assert.deepEqual(request.requiredPackageDependencies, [
|
||||||
|
{
|
||||||
|
packageName: "org.gamecult.eve.surface",
|
||||||
|
version: "0.1.0",
|
||||||
|
packageManager: "upm",
|
||||||
|
ownerRepo: "Eve",
|
||||||
|
purpose: "Shared Unity DTOs for gamecult.eve.surface.v1 documents.",
|
||||||
|
},
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("rejects package manifests that do not match the release contract package", () => {
|
test("rejects package manifests that do not match the release contract package", () => {
|
||||||
|
|
@ -38,6 +47,15 @@ test("rejects package manifests that do not match the release contract package",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("rejects package manifests missing required UPM dependencies", () => {
|
||||||
|
const missingDependencyPackage = { ...packageManifest, dependencies: {} };
|
||||||
|
|
||||||
|
assert.throws(
|
||||||
|
() => buildUnityReleaseRequest({ capabilityManifest, packageManifest: missingDependencyPackage }),
|
||||||
|
/must depend on org\.gamecult\.eve\.surface/,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("builds EveUnity scene UPM release request from package manifest", () => {
|
test("builds EveUnity scene UPM release request from package manifest", () => {
|
||||||
const request = buildUnityReleaseRequest({
|
const request = buildUnityReleaseRequest({
|
||||||
capabilityManifest: sceneCapabilityManifest,
|
capabilityManifest: sceneCapabilityManifest,
|
||||||
|
|
@ -57,4 +75,5 @@ test("builds EveUnity scene UPM release request from package manifest", () => {
|
||||||
assert.equal(request.artifactKind, "upm-package");
|
assert.equal(request.artifactKind, "upm-package");
|
||||||
assert.equal(request.artifactPath, "artifacts/eveunity-scene-release/0.1.0/org.gamecult.eve.unity-scene-0.1.0.tgz");
|
assert.equal(request.artifactPath, "artifacts/eveunity-scene-release/0.1.0/org.gamecult.eve.unity-scene-0.1.0.tgz");
|
||||||
assert.equal(request.dependencies["org.gamecult.eve.surface"], "0.1.0");
|
assert.equal(request.dependencies["org.gamecult.eve.surface"], "0.1.0");
|
||||||
|
assert.equal(request.requiredPackageDependencies[0].ownerRepo, "Eve");
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1569,7 +1569,16 @@
|
||||||
"requestSchema": "gamecult.eve.runtime_release_request.v1",
|
"requestSchema": "gamecult.eve.runtime_release_request.v1",
|
||||||
"requestBuilder": "tools/eveunity/eveunity-release-contract.mjs",
|
"requestBuilder": "tools/eveunity/eveunity-release-contract.mjs",
|
||||||
"artifactPattern": "artifacts/eveunity-uitoolkit-release/{version}/org.gamecult.eve.unity-uitoolkit-{version}.tgz",
|
"artifactPattern": "artifacts/eveunity-uitoolkit-release/{version}/org.gamecult.eve.unity-uitoolkit-{version}.tgz",
|
||||||
"publishProof": "Tagged UPM release is created from the EveUnity repo and references this package root."
|
"publishProof": "Tagged UPM release is created from the EveUnity repo and references this package root.",
|
||||||
|
"requiredPackageDependencies": [
|
||||||
|
{
|
||||||
|
"packageName": "org.gamecult.eve.surface",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"packageManager": "upm",
|
||||||
|
"ownerRepo": "Eve",
|
||||||
|
"purpose": "Shared Unity DTOs for gamecult.eve.surface.v1 documents."
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"evidencePaths": [
|
"evidencePaths": [
|
||||||
"packages/org.gamecult.eve.unity-uitoolkit/package.json",
|
"packages/org.gamecult.eve.unity-uitoolkit/package.json",
|
||||||
|
|
@ -1598,7 +1607,30 @@
|
||||||
"testPlatform": "EditMode",
|
"testPlatform": "EditMode",
|
||||||
"resultsArtifact": "artifacts/aetheria-unity-editmode/{stamp}/unity-editmode-results.xml",
|
"resultsArtifact": "artifacts/aetheria-unity-editmode/{stamp}/unity-editmode-results.xml",
|
||||||
"logArtifact": "artifacts/aetheria-unity-editmode/{stamp}/unity-editmode.log",
|
"logArtifact": "artifacts/aetheria-unity-editmode/{stamp}/unity-editmode.log",
|
||||||
"manifestMutation": "Temporarily adds org.gamecult.eve.unity-uitoolkit to Aetheria Packages/manifest.json testables and restores the manifest after the run."
|
"manifestMutation": "Temporarily adds org.gamecult.eve.unity-uitoolkit to Aetheria Packages/manifest.json testables and restores the manifest after the run.",
|
||||||
|
"managedAssemblyDependencies": [
|
||||||
|
{
|
||||||
|
"assemblyName": "GameCult.Caching.dll",
|
||||||
|
"packageId": "GameCult.Caching",
|
||||||
|
"packageManager": "nuget",
|
||||||
|
"ownerRepo": "CultLib",
|
||||||
|
"resolutionMode": "Unity precompiled reference during EveUnity incubation."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assemblyName": "GameCult.Mesh.dll",
|
||||||
|
"packageId": "GameCult.Mesh",
|
||||||
|
"packageManager": "nuget",
|
||||||
|
"ownerRepo": "CultLib",
|
||||||
|
"resolutionMode": "Unity precompiled reference during EveUnity incubation."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"assemblyName": "MessagePack.Annotations.dll",
|
||||||
|
"packageId": "MessagePack.Annotations",
|
||||||
|
"packageManager": "nuget",
|
||||||
|
"ownerRepo": "CultLib",
|
||||||
|
"resolutionMode": "Unity precompiled reference during EveUnity incubation."
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"evidencePaths": [
|
"evidencePaths": [
|
||||||
"packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef",
|
"packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/GameCult.Eve.UnityUIToolkit.Tests.asmdef",
|
||||||
|
|
|
||||||
|
|
@ -1541,6 +1541,12 @@ function compareTestContract(expected, actual, label) {
|
||||||
if (actual.consumerProject && !existsSync(actual.consumerProject)) {
|
if (actual.consumerProject && !existsSync(actual.consumerProject)) {
|
||||||
errors.push(`${label}.consumerProject:${actual.consumerProject}:missing`);
|
errors.push(`${label}.consumerProject:${actual.consumerProject}:missing`);
|
||||||
}
|
}
|
||||||
|
errors.push(...compareDependencyRecords(
|
||||||
|
expected.managedAssemblyDependencies || [],
|
||||||
|
actual.managedAssemblyDependencies || [],
|
||||||
|
`${label}.managedAssemblyDependencies`,
|
||||||
|
["assemblyName", "packageId", "packageManager", "ownerRepo", "resolutionMode"],
|
||||||
|
));
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1561,6 +1567,30 @@ function compareReleaseContract(expected, actual, label) {
|
||||||
errors.push(`${label}.${key}:${actual[key]}:missing`);
|
errors.push(`${label}.${key}:${actual[key]}:missing`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
errors.push(...compareDependencyRecords(
|
||||||
|
expected.requiredPackageDependencies || [],
|
||||||
|
actual.requiredPackageDependencies || [],
|
||||||
|
`${label}.requiredPackageDependencies`,
|
||||||
|
["packageName", "version", "packageManager", "ownerRepo", "purpose"],
|
||||||
|
));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareDependencyRecords(expectedRecords, actualRecords, label, keys) {
|
||||||
|
const errors = [];
|
||||||
|
for (const expected of expectedRecords) {
|
||||||
|
const identityKey = expected.packageName ? "packageName" : "assemblyName";
|
||||||
|
const actual = actualRecords.find(candidate => candidate?.[identityKey] === expected[identityKey]);
|
||||||
|
if (!actual) {
|
||||||
|
errors.push(`${label}:${expected[identityKey]}:missing`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const key of keys) {
|
||||||
|
if ((expected[key] || "") !== (actual[key] || "")) {
|
||||||
|
errors.push(`${label}:${expected[identityKey]}.${key}:expected ${expected[key] || ""} got ${actual[key] || ""}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue