test(opcua): bind saturation artifacts to gateway 1.0 and module identities
v2-ci / unit-tests (pull_request) Has been cancelled

This commit is contained in:
Joseph Doherty
2026-11-10 14:57:45 -05:00
parent d7891372f2
commit 04d5513af2
3 changed files with 45 additions and 0 deletions
@@ -0,0 +1,26 @@
// Read-only managed metadata extraction; does not load or execute product assemblies.
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text.Json;
var root = Path.GetFullPath(args[0]);
var modules = new List<object>();
foreach (var directory in args.Skip(1))
{
foreach (var path in Directory.EnumerateFiles(directory, "*.dll", SearchOption.AllDirectories).Order())
{
try
{
using var file = File.OpenRead(path);
using var pe = new PEReader(file);
if (!pe.HasMetadata) continue;
var metadata = pe.GetMetadataReader();
if (!metadata.IsAssembly) continue;
var assembly = metadata.GetAssemblyDefinition();
modules.Add(new { file = Path.GetRelativePath(root, path).Replace(Path.DirectorySeparatorChar, '/'),
assembly = metadata.GetString(assembly.Name), version = assembly.Version.ToString(),
mvid = metadata.GetGuid(metadata.GetModuleDefinition().Mvid).ToString("D") });
}
catch (BadImageFormatException) { /* Native DLLs have no CLR module identity. */ }
}
}
Console.WriteLine(JsonSerializer.Serialize(modules));
@@ -79,6 +79,9 @@ publishes fresh, separate app/observer artifacts for `linux-arm64`, self-contain
.NET **10.0.14**, without trimming or single-file publishing. It never starts the
experiment. The bundle records source revision, source hashes, exact publish
command, fixed protocol, image digest and SHA-256 of every app/observer/tool file.
It requires the landed HistorianGateway Client/Contracts 1.0.0 dependency graph
and records assembly versions/module MVIDs by reading PE metadata without loading
or executing product assemblies.
```bash
python3 scripts/validation/opcua-lifecycle-memory/prepare_saturation.py \
@@ -49,12 +49,28 @@ def main():
if not included or any(value["version"] != "10.0.14" for value in included):
raise RuntimeError("Published participant is not the prospectively fixed self-contained runtime")
dependencies = json.loads((root / "app/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests.deps.json").read_text())["libraries"]
gateway_libraries = [name for name in dependencies if name.startswith("ZB.MOM.WW.HistorianGateway.")]
if not gateway_libraries or any(name.rsplit("/", 1)[-1] != "1.0.0" for name in gateway_libraries):
raise RuntimeError("The reviewed candidate requires the landed HistorianGateway 1.0.0 graph")
metadata = root / "module-info-build"
metadata.mkdir()
(metadata / "module-info.csproj").write_text(
'<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType>'
'<TargetFramework>net10.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings>'
'<EnableDefaultCompileItems>false</EnableDefaultCompileItems></PropertyGroup>'
'<ItemGroup><Compile Include="' + str(source / "ArtifactModuleInfo.cs") + '" /></ItemGroup></Project>')
with (metadata / "build.log").open("w") as log:
subprocess.run(["dotnet", "build", str(metadata / "module-info.csproj"), "-c", "Release", "-m:2"],
stdout=log, stderr=subprocess.STDOUT, check=True)
modules = json.loads(subprocess.check_output(["dotnet", str(metadata / "bin/Release/net10.0/module-info.dll"),
str(root), str(root / "app"), str(root / "observer")], text=True))
receipt = {"protocol": "sdk2-saturation-v1", "sourceRevision": revision, "image": options.image,
"rid": "linux-arm64", "runtimeVersion": "10.0.14", "selfContained": True,
"canonicalCycles": 384, "completedRestarts": 96, "settlingCycles": 1, "maximumPhaseSeconds": 1800,
"windows": [[321, 352], [353, 384]], "observerBufferMiB": 16, "runtimeKeywords": "0x18", "rundown": False,
"acceptance": False, "preparedUtc": datetime.now(timezone.utc).isoformat(), "publishCommand": command,
"sdkLibraries": [name for name in dependencies if name.startswith("OPCFoundation.")],
"gatewayLibraries": gateway_libraries, "managedModules": modules,
"testSourceSha256": sha(project.parent / "Sdk2LifecycleSoakTests.cs"),
"observerSourceSha256": sha(source / "SaturationObserver.cs"), "files": manifest}
receipt_path = root / "receipt.json"