Merge portability onboarding fix: pack the whole public closure; time the headless run from its command
NU1101 since69bd45ff: pack-nuget.ps1 packed a hand list that missed GameCult.Math; it now follows ProjectReferences from the public roots. ubuntu timeout since2dc7c122: the headless deadline started at connect. Green on both OSes: run 36727267703 (ubuntu passed twice). Follow-up (Eve): pack-dotnet-surface.ps1 ignores the version parameters the verify script passes, so the local feed reuses published versions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
82f976b933
3 changed files with 72 additions and 29 deletions
|
|
@ -11,9 +11,12 @@ CultNet, and CultCache ownership boundaries.
|
|||
powershell -ExecutionPolicy Bypass -File .\scripts\pack-nuget.ps1
|
||||
```
|
||||
|
||||
The command creates a local feed under `artifacts/nuget`, verifies that every
|
||||
internal dependency package exists at the same version, then restores and runs
|
||||
a clean .NET consumer using only `PackageReference Include="GameCult.Mesh"`.
|
||||
The command packs `GameCult.Mesh`, `GameCult.Networking.WebSockets`, and every
|
||||
project they reach through `ProjectReference` (including `GameCult.Math`, which
|
||||
carries its own version) into a local feed under `artifacts/nuget`. It verifies
|
||||
that each of those packages exists at the identity MSBuild reports for it, then
|
||||
restores and runs a clean .NET consumer using only
|
||||
`PackageReference Include="GameCult.Mesh"`.
|
||||
|
||||
The Unity distribution remains `org.gamecult.cultlib`, built by
|
||||
`scripts/build-unity-package.ps1`, because Unity consumes the managed assembly
|
||||
|
|
|
|||
|
|
@ -244,11 +244,13 @@ static async Task RunHeadlessAsync(Args arguments)
|
|||
Console.WriteLine("HEADLESS_READY " + JsonSerializer.Serialize(new { count = initial.Count }));
|
||||
}
|
||||
if (initial.Count >= arguments.ExpectedCount) completion.TrySetResult();
|
||||
var commandReceived = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
var commandTask = Task.Run(async () =>
|
||||
{
|
||||
var line = await Console.In.ReadLineAsync();
|
||||
if (line == null || !line.StartsWith("INVOKE ", StringComparison.Ordinal))
|
||||
throw new InvalidOperationException("The headless sample expected 'INVOKE <idempotency-key>' on stdin.");
|
||||
commandReceived.TrySetResult();
|
||||
var commandId = line["INVOKE ".Length..].Trim();
|
||||
var result = await mesh.InvokeAsync<IncrementRequest, IncrementReceipt>(
|
||||
target,
|
||||
|
|
@ -268,6 +270,9 @@ static async Task RunHeadlessAsync(Args arguments)
|
|||
Console.WriteLine("HEADLESS_NETWORK_BENCHMARK " + JsonSerializer.Serialize(
|
||||
await MeasureOperationSessionAsync(mesh, target, arguments)));
|
||||
});
|
||||
// How long the caller takes to send the command is the caller's to bound
|
||||
// (closing stdin ends the wait); this process bounds only its own work.
|
||||
await Task.WhenAny(commandReceived.Task, commandTask);
|
||||
await Task.WhenAll(completion.Task, commandTask).WaitAsync(TimeSpan.FromSeconds(45));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,55 +13,90 @@ $outputRoot = if ([IO.Path]::IsPathRooted($OutputDirectory)) {
|
|||
Join-Path $repoRoot $OutputDirectory
|
||||
}
|
||||
|
||||
$projects = @(
|
||||
"src\GameCult.Logging\GameCult.Logging.csproj",
|
||||
"src\GameCult.Caching\GameCult.Caching.csproj",
|
||||
"src\GameCult.Caching.MessagePack\GameCult.Caching.MessagePack.csproj",
|
||||
"src\GameCult.Networking\GameCult.Networking.csproj",
|
||||
"src\GameCult.Networking.WebSockets\GameCult.Networking.WebSockets.csproj",
|
||||
"src\GameCult.Mesh\GameCult.Mesh.csproj"
|
||||
# The public packages a consumer references directly. Everything they reach
|
||||
# through ProjectReference is part of the published closure and is packed too,
|
||||
# so a new internal dependency cannot leave the feed short of a package.
|
||||
$rootProjects = @(
|
||||
"src\GameCult.Mesh\GameCult.Mesh.csproj",
|
||||
"src\GameCult.Networking.WebSockets\GameCult.Networking.WebSockets.csproj"
|
||||
)
|
||||
|
||||
# MSBuild paths use backslashes; normalize them so the closure walk also
|
||||
# resolves on Linux and macOS.
|
||||
function Resolve-ProjectPath([string] $path) {
|
||||
return [IO.Path]::GetFullPath($path.Replace('\', [string][IO.Path]::DirectorySeparatorChar))
|
||||
}
|
||||
|
||||
function Get-ProjectReferenceClosure([string[]] $roots) {
|
||||
$seen = [System.Collections.Generic.List[string]]::new()
|
||||
$pending = [System.Collections.Generic.Queue[string]]::new()
|
||||
foreach ($root in $roots) { $pending.Enqueue((Resolve-ProjectPath $root)) }
|
||||
while ($pending.Count -gt 0) {
|
||||
$project = $pending.Dequeue()
|
||||
if ($seen.Contains($project)) { continue }
|
||||
if (-not (Test-Path -LiteralPath $project -PathType Leaf)) {
|
||||
throw "Project in the NuGet closure was not found: $project"
|
||||
}
|
||||
$seen.Add($project)
|
||||
# Every ProjectReference counts, whatever its Condition: a reference
|
||||
# present for any target framework ships in the package's dependencies.
|
||||
foreach ($reference in ([xml](Get-Content -LiteralPath $project -Raw)).SelectNodes("//*[local-name()='ProjectReference']")) {
|
||||
$include = $reference.GetAttribute("Include").Trim()
|
||||
if ($include -match '\$\(') {
|
||||
throw "Cannot resolve the MSBuild property in ProjectReference '$include' from $project"
|
||||
}
|
||||
$pending.Enqueue((Resolve-ProjectPath (Join-Path (Split-Path -Parent $project) $include)))
|
||||
}
|
||||
}
|
||||
return $seen
|
||||
}
|
||||
|
||||
$projects = Get-ProjectReferenceClosure ($rootProjects | ForEach-Object { Join-Path $repoRoot $_ })
|
||||
|
||||
if (Test-Path -LiteralPath $outputRoot) {
|
||||
Remove-Item -LiteralPath $outputRoot -Recurse -Force
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $outputRoot | Out-Null
|
||||
|
||||
$versionArguments = @()
|
||||
if (-not [string]::IsNullOrWhiteSpace($PackageVersion)) {
|
||||
$versionArguments += "-p:CultLibPackageVersion=$PackageVersion"
|
||||
}
|
||||
|
||||
$expectedPackages = @()
|
||||
foreach ($project in $projects) {
|
||||
$packArguments = @(
|
||||
"pack", (Join-Path $repoRoot $project), "-c", $Configuration, "-o", $outputRoot,
|
||||
"--nologo", "--verbosity", "quiet", "-p:NoWarn=1591%3BCS8632"
|
||||
)
|
||||
if (-not [string]::IsNullOrWhiteSpace($PackageVersion)) {
|
||||
$packArguments += "-p:CultLibPackageVersion=$PackageVersion"
|
||||
# The package identity comes from MSBuild itself: GameCult.Math, for one,
|
||||
# carries its own PackageId and version rather than the CultLib version.
|
||||
$identity = (& dotnet msbuild $project -nologo "-getProperty:PackageId" "-getProperty:Version" @versionArguments) |
|
||||
Out-String | ConvertFrom-Json
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Could not read the package identity of $project"
|
||||
}
|
||||
$expectedPackages += "$($identity.Properties.PackageId).$($identity.Properties.Version).nupkg"
|
||||
|
||||
$packArguments = @(
|
||||
"pack", $project, "-c", $Configuration, "-o", $outputRoot,
|
||||
"--nologo", "--verbosity", "quiet", "-p:NoWarn=1591%3BCS8632"
|
||||
) + $versionArguments
|
||||
& dotnet @packArguments
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "NuGet pack failed for $project with exit code $LASTEXITCODE"
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($package in $expectedPackages) {
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $outputRoot $package))) {
|
||||
throw "NuGet dependency closure is missing $package"
|
||||
}
|
||||
}
|
||||
|
||||
$meshPackage = Get-ChildItem -LiteralPath $outputRoot -Filter "GameCult.Mesh.*.nupkg" |
|
||||
Where-Object { $_.Name -notlike "*.snupkg" } |
|
||||
Select-Object -First 1
|
||||
if (-not $meshPackage) {
|
||||
throw "GameCult.Mesh package was not produced."
|
||||
}
|
||||
|
||||
$version = [regex]::Match($meshPackage.Name, '^GameCult\.Mesh\.(.+)\.nupkg$').Groups[1].Value
|
||||
$expectedPackages = @(
|
||||
"GameCult.Logging.$version.nupkg",
|
||||
"GameCult.Caching.$version.nupkg",
|
||||
"GameCult.Caching.MessagePack.$version.nupkg",
|
||||
"GameCult.Networking.$version.nupkg",
|
||||
"GameCult.Networking.WebSockets.$version.nupkg",
|
||||
"GameCult.Mesh.$version.nupkg"
|
||||
)
|
||||
foreach ($package in $expectedPackages) {
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $outputRoot $package))) {
|
||||
throw "NuGet dependency closure is missing $package"
|
||||
}
|
||||
}
|
||||
|
||||
if (-not $SkipConsumerSmoke) {
|
||||
$smokeRoot = Join-Path $repoRoot "artifacts\nuget-consumer-smoke"
|
||||
|
|
|
|||
Loading…
Reference in a new issue