From 923a099d2f52afa27d7da47daf078a9a3221ca3a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 9 Jul 2026 01:32:53 +0100 Subject: [PATCH] Add plugin owner conformance consumer proof --- docs/eve-migration-roadmap.md | 8 +++ scripts/run-parity-harness.ps1 | 1 + ...lugin-owner-conformance-consumer-smoke.ps1 | 59 +++++++++++++++++++ tools/conformance/consume-export.mjs | 46 ++++++++++++++- tools/parity/run-parity.mjs | 5 ++ 5 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 scripts/run-plugin-owner-conformance-consumer-smoke.ps1 diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index f688113..54e1037 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -280,6 +280,14 @@ Exit criteria: Recently cut: +- Plugin owner conformance consumption now has a smoke runner at + `scripts/run-plugin-owner-conformance-consumer-smoke.ps1`. The generic + conformance consumer can assert plugin ABI operations and capabilities with + `--expect-plugin-operation` and `--expect-plugin-capability`, and the export + now carries plugin manifest paths, advertisement paths, split targets, + graduation triggers, and optional plugin dependencies. Sai and Norn can prove + the plugin contract from the conformance export without reading Eve's parity + manifest. - EveUnity now has a split lifecycle smoke at `scripts/run-eveunity-lifecycle-smoke.ps1`. It validates the existing `packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json` diff --git a/scripts/run-parity-harness.ps1 b/scripts/run-parity-harness.ps1 index e8f17dd..4e6212c 100644 --- a/scripts/run-parity-harness.ps1 +++ b/scripts/run-parity-harness.ps1 @@ -15,6 +15,7 @@ Push-Location $projectRoot try { node .\tools\parity\run-parity.mjs & (Join-Path $PSScriptRoot "run-conformance-consumer-smoke.ps1") + & (Join-Path $PSScriptRoot "run-plugin-owner-conformance-consumer-smoke.ps1") } finally { Pop-Location } diff --git a/scripts/run-plugin-owner-conformance-consumer-smoke.ps1 b/scripts/run-plugin-owner-conformance-consumer-smoke.ps1 new file mode 100644 index 0000000..699de11 --- /dev/null +++ b/scripts/run-plugin-owner-conformance-consumer-smoke.ps1 @@ -0,0 +1,59 @@ +param( + [string] $ExportDirectory = "artifacts\conformance\latest", + [string] $ConsumerDirectory = "artifacts\plugin-owner-conformance-consumer-smoke" +) + +$ErrorActionPreference = "Stop" + +$projectRoot = Split-Path -Parent $PSScriptRoot +$sourceExport = if ([System.IO.Path]::IsPathRooted($ExportDirectory)) { + $ExportDirectory +} else { + Join-Path $projectRoot $ExportDirectory +} +$consumerRoot = if ([System.IO.Path]::IsPathRooted($ConsumerDirectory)) { + $ConsumerDirectory +} else { + Join-Path $projectRoot $ConsumerDirectory +} +$consumerExport = Join-Path $consumerRoot "export" + +if (-not (Test-Path $sourceExport)) { + throw "Conformance export not found: $sourceExport" +} + +if (Test-Path $consumerExport) { + Remove-Item -LiteralPath $consumerExport -Recurse -Force +} +New-Item -ItemType Directory -Force -Path $consumerRoot | Out-Null +Copy-Item -LiteralPath $sourceExport -Destination $consumerExport -Recurse + +$consumerScript = Join-Path $projectRoot "tools\conformance\consume-export.mjs" + +node $consumerScript $consumerExport ` + --expect-pack plugin ` + --expect-fixture sai-vn ` + --expect-plugin sai.vn ` + --expect-plugin norn.graph ` + --expect-plugin-operation sai.vn:describe ` + --expect-plugin-operation sai.vn:validate ` + --expect-plugin-operation sai.vn:project ` + --expect-plugin-operation sai.vn:lower ` + --expect-plugin-operation sai.vn:measure ` + --expect-plugin-operation sai.vn:apply ` + --expect-plugin-operation norn.graph:describe ` + --expect-plugin-operation norn.graph:validate ` + --expect-plugin-operation norn.graph:project ` + --expect-plugin-operation norn.graph:lower ` + --expect-plugin-operation norn.graph:measure ` + --expect-plugin-operation norn.graph:apply ` + --expect-plugin-capability sai.vn:vn.stage ` + --expect-plugin-capability sai.vn:story.choose ` + --expect-plugin-capability sai.vn:story.continue ` + --expect-plugin-capability sai.vn:story.jump ` + --expect-plugin-capability norn.graph:embed.norn ` + --expect-plugin-capability norn.graph:graph.node.activate + +if ($LASTEXITCODE -ne 0) { + throw "Plugin owner conformance consumer smoke failed with exit code $LASTEXITCODE" +} diff --git a/tools/conformance/consume-export.mjs b/tools/conformance/consume-export.mjs index 136ec5d..f4c2ef1 100644 --- a/tools/conformance/consume-export.mjs +++ b/tools/conformance/consume-export.mjs @@ -16,6 +16,8 @@ if (!exportDirectory) { " --expect-runtime ", " --expect-scenario ", " --expect-split-target ", + " --expect-plugin-operation ", + " --expect-plugin-capability ", ].join("\n")); process.exit(2); } @@ -95,6 +97,26 @@ function validateIndex(index, directory, expectations, errors) { for (const expectedPlugin of expectations.plugins) { if (!plugins.some(plugin => plugin.pluginId === expectedPlugin)) errors.push(`plugins:${expectedPlugin}:missing`); } + for (const expectation of expectations.pluginOperations) { + const plugin = plugins.find(candidate => candidate.pluginId === expectation.pluginId); + if (!plugin) { + errors.push(`plugins:${expectation.pluginId}:missing`); + continue; + } + if (!Array.isArray(plugin.abiOperations) || !plugin.abiOperations.includes(expectation.operation)) { + errors.push(`plugins:${expectation.pluginId}:operation:${expectation.operation}:missing`); + } + } + for (const expectation of expectations.pluginCapabilities) { + const plugin = plugins.find(candidate => candidate.pluginId === expectation.pluginId); + if (!plugin) { + errors.push(`plugins:${expectation.pluginId}:missing`); + continue; + } + if (!Array.isArray(plugin.capabilities) || !plugin.capabilities.includes(expectation.capability)) { + errors.push(`plugins:${expectation.pluginId}:capability:${expectation.capability}:missing`); + } + } for (const expectedProvider of expectations.providers) { if (!providers.some(provider => provider.providerId === expectedProvider)) errors.push(`providers:${expectedProvider}:missing`); } @@ -132,6 +154,8 @@ function parseArguments(args) { packs: [], fixtures: [], plugins: [], + pluginOperations: [], + pluginCapabilities: [], providers: [], runtimes: [], scenarios: [], @@ -145,6 +169,8 @@ function parseArguments(args) { ["--expect-runtime", expectations.runtimes], ["--expect-scenario", expectations.scenarios], ["--expect-split-target", expectations.splitTargets], + ["--expect-plugin-operation", expectations.pluginOperations], + ["--expect-plugin-capability", expectations.pluginCapabilities], ]); for (let index = 1; index < args.length; index += 1) { @@ -159,9 +185,27 @@ function parseArguments(args) { console.error(`Missing value for option: ${option}`); process.exit(2); } - target.push(value); + if (option === "--expect-plugin-operation") { + target.push(parsePluginExpectation(value, "operation")); + } else if (option === "--expect-plugin-capability") { + target.push(parsePluginExpectation(value, "capability")); + } else { + target.push(value); + } index += 1; } return { exportDirectory: exportPath, expectations }; } + +function parsePluginExpectation(value, field) { + const separator = value.indexOf(":"); + if (separator <= 0 || separator === value.length - 1) { + console.error(`Expected plugin ${field} in form, got: ${value}`); + process.exit(2); + } + return { + pluginId: value.slice(0, separator), + [field]: value.slice(separator + 1), + }; +} diff --git a/tools/parity/run-parity.mjs b/tools/parity/run-parity.mjs index 2dfdc8a..01244e0 100644 --- a/tools/parity/run-parity.mjs +++ b/tools/parity/run-parity.mjs @@ -1341,8 +1341,13 @@ function buildConformanceExport(report) { pluginId: plugin.pluginId, status: plugin.status, ownerRepo: plugin.ownerRepo, + splitTarget: plugin.splitTarget, + graduationTrigger: plugin.graduationTrigger, + manifestPath: plugin.manifestPath, + advertisementPath: plugin.advertisementPath, abiFixturePath: plugin.abiFixturePath, abiOperations: plugin.abiOperations || [], + optionalPlugins: plugin.optionalPlugins || [], capabilities: plugin.capabilities, })), providers: (report.providers || []).map(provider => ({