Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 24 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ name: release

# Release-only publish for FS.GG.Game (ADR-0022 / P5). Tag-triggered; NEVER a required check and
# NEVER on push/PR (mirrors the sibling repos' release cadence). Publishes the coherent set
# FS.GG.Game.Core + FS.GG.Game.Render + FS.GG.Game.Harness to the org GitHub Packages feed
# FS.GG.Game.Core + FS.GG.Game.Render + FS.GG.Game.Harness + FS.GG.Game.Physics.Box2D to the org GitHub Packages feed
# (nuget.pkg.github.com/FS-GG), then dual-publishes the BYTE-IDENTICAL set to public nuget.org via
# Trusted Publishing / OIDC (ADR-0012 §1–§5 / ADR-0013 — no stored key). Gated on a full headless
# build+test first: a set is published only after `verify` is green (publish-before-flip, FR-007).
#
# The set shares ONE version (the repo-wide <Version> in Directory.Build.local.props); the packages
# move together, so every release re-publishes all three at the same version. Game.Render depends on
# move together, so every release re-publishes all four at the same version. Game.Render depends on
# Game.Core@same-version + FS.GG.UI.Scene (itself dual-published, ADR-0012/0013); Game.Harness
# depends on Game.Core@same-version + the BCL only (WI-6, Game#425). FS.GG.Game.Harness joined the
# set at 0.6.0 — it is packable and in the slnx as of #423 but landed after the v0.5.1 release, so no
Expand Down Expand Up @@ -54,6 +54,13 @@ jobs:
with:
dotnet-version: '10.0.x'

# Cheap exact-roster/version/order checks run before restore or the full release suite.
- name: Preflight 0.17.0 release contract and mutation controls
shell: bash
run: |
tests/release/test-game-0-17-release.sh --source-only
tests/release/test-game-0-17-release-selftest.sh

# COLD, via the shared action (#135 / FS-GG/.github#429) — and this is the job where a warm
# restore was worst. There is no `cache: true` here to blame: --locked-mode validates the
# committed contentHash against whatever copy of the package is already in the global-packages
Expand All @@ -71,12 +78,6 @@ jobs:
- name: Test (full headless suite)
run: dotnet test FS.GG.Game.slnx -c Debug --no-restore

- name: Verify 0.16.0 release contract and inversion
shell: bash
run: |
tests/release/test-game-0-16-release.sh --source-only
tests/release/test-game-0-16-release-selftest.sh

publish-packages:
name: Publish FS.GG.Game.* (org feed + nuget.org, release-only)
runs-on: ubuntu-latest
Expand Down Expand Up @@ -147,9 +148,9 @@ jobs:
with:
target: FS.GG.Game.slnx

# Pack the coherent set. FS.GG.Game.Core, FS.GG.Game.Render and FS.GG.Game.Harness are
# IsPackable=true (the three test projects are IsPackable=false), so packing the whole slnx
# yields exactly those three packages, all at $VER — and each ProjectReference to Game.Core
# Pack the coherent set. FS.GG.Game.Core, FS.GG.Game.Render, FS.GG.Game.Harness and FS.GG.Game.Physics.Box2D are
# IsPackable=true (all test and example projects are IsPackable=false), so packing the whole slnx
# yields exactly those four packages, all at $VER — and each ProjectReference to Game.Core
# (Render's and Harness's) is emitted as a package dependency at the same $VER (coherent set).
#
# `dotnet pack` restores implicitly, and that is left alone deliberately: it re-resolves with
Expand All @@ -167,7 +168,6 @@ jobs:
-p:Version="$VER" \
-p:RepositoryCommit="$GITHUB_SHA" \
-p:EnablePackageValidation=true \
-p:PackageValidationBaselineVersion=0.15.0 \
-o artifacts/packages
(cd artifacts/packages && sha256sum -- *.nupkg | sort -k2 > SHA256SUMS)
echo "Packed:"; ls -1 artifacts/packages/*.nupkg
Expand Down Expand Up @@ -216,11 +216,15 @@ jobs:
set -euo pipefail
(cd artifacts/packages && sha256sum --check SHA256SUMS)
count="$(find artifacts/packages -maxdepth 1 -type f -name '*.nupkg' ! -name '*.snupkg' | wc -l)"
[ "$count" = 3 ] || { echo "::error::release custody must contain exactly three primary packages; found $count"; exit 1; }
[ "$count" = 4 ] || { echo "::error::release custody must contain exactly four primary packages; found $count"; exit 1; }

- name: Verify coherent package set, metadata, and clean Journey consumer
shell: bash
run: tests/release/test-game-0-16-release.sh --packages artifacts/packages
run: tests/release/test-game-0-17-release.sh --packages artifacts/packages

- name: Qualify package-only Box2D candidate from retained preparation
shell: bash
run: tests/release/test-box2d-package-consumer.sh --packages artifacts/packages

- name: Retain original coherent package bytes before publication
if: inputs.source_run_id == ''
Expand Down Expand Up @@ -311,10 +315,15 @@ jobs:
if observed != expected: raise SystemExit(f'{feed} payload differs for {original.name}')
row['feeds'][feed]={'archiveSha256':sha256(remote.read_bytes()).hexdigest(),'payloadMembers':len(observed),'payloadMatch':True}
rows.append(row)
if len(rows) != 3: raise SystemExit(f'expected three primary package readbacks, got {len(rows)}')
if len(rows) != 4: raise SystemExit(f'expected four primary package readbacks, got {len(rows)}')
report.write_text(json.dumps({'schema':'fsgg.game.release-readback/v1','packages':rows},indent=2)+'\n')
PY

- name: Qualify fresh public package-only Box2D consumer
if: steps.ver.outputs.push == 'true'
shell: bash
run: tests/release/test-box2d-package-consumer.sh --public

- name: Retain dual-feed readback evidence
if: steps.ver.outputs.push == 'true'
uses: actions/upload-artifact@v7
Expand Down
15 changes: 9 additions & 6 deletions Directory.Build.local.props
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,15 @@

<PropertyGroup Label="Package">
<!-- The release tag is authoritative: release.yml packs with -p:Version="$VER" from the tag,
which overrides this. Keep it equal to the newest released version anyway, so a local
`dotnet pack` and the feed cannot disagree. -->
<!-- 0.16.0 is a MINOR cut: the coherent set adds portable replay, planning/rules, and
authoritative network-admission surfaces while preserving the 0.15.0 API. Core, Render,
and Harness keep one scalar and publish together through release.yml. -->
<Version>0.16.0</Version>
which overrides this. This is the selected source release scalar;
native both-feed readback separately establishes its published status. -->
<!-- 0.17.0 adds optional Box2D physics and Core portal topology. Core, Render, Harness and
Physics.Box2D keep one scalar and publish together through release.yml. -->
<Version>0.17.0</Version>
<!-- Existing packages compare against their actual published predecessor. Physics.Box2D has
no predecessor on its first release: enable current-package validation, without asking
NuGet for a nonexistent baseline package. Add its baseline after first publication. -->
<PackageValidationBaselineVersion Condition="'$(MSBuildProjectName)' == 'FS.GG.Game.Core' Or '$(MSBuildProjectName)' == 'FS.GG.Game.Render' Or '$(MSBuildProjectName)' == 'FS.GG.Game.Harness'">0.16.0</PackageValidationBaselineVersion>
<Authors>FS.GG Contributors</Authors>
<Company>FS.GG Contributors</Company>
<RepositoryType>git</RepositoryType>
Expand Down
67 changes: 64 additions & 3 deletions docs/roadmaps/game-box2d-physics.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,13 @@ adoption must preview and preserve user-owned files and record rollback/removal

- [ ] `.1` Runtime and headless example: locally qualified; native source delivery pending.
- [ ] `.2` Retained snapshots, interpolation and pinned input replay: locally qualified; native source delivery pending.
- [ ] `.3` Public package and fresh installed example: publication/installed support pending.
- [ ] `.3` Public package and fresh installed example: successor source and package-only candidate consumer locally qualified; native source/publication/public installed proof pending.
- [ ] `.4` Explicit consumer adoption: not selected.

The optional project is `src/Game.Physics.Box2D/FS.GG.Game.Physics.Box2D.fsproj`.
Its package identity is `FS.GG.Game.Physics.Box2D` and it inherits Game's coherent
version convention (currently 0.16.0); a future release must select a coherent version
and add the package to release obligations before publication. No package has been published.
version convention (selected successor 0.17.0); the producer release source prepares all four Game packages
at that scalar. Source preparation does not establish publication.

The dependency is [published Box2D.NET 3.1.654](https://www.nuget.org/packages/Box2D.NET/3.1.654),
whose package metadata records upstream revision
Expand Down Expand Up @@ -157,3 +157,64 @@ registered type/member baselines (27 types, 52 members); whole-solution qualific
and the native source delivery gates remain the integrator's obligation. No public
package, installed consumer, portal transfer, browser or wider-platform acceptance
is established by these local results.

## 0.17.0 release source and preflight

The selected source successor is stable `0.17.0`, with Core, Render, Harness and the new
Physics.Box2D adapter forming one coherent release set. Existing packages use their published
`0.16.0` API baseline. The first adapter package has no predecessor baseline; current-package
validation and the exact package/API/dependency checks still apply. Both feed collision checks
must be refreshed before the release owner selects publication.

The existing `release.yml` route retains its verify dependency, exact-source version guard,
one coherent preparation, original custody bytes, GitHub Packages before nuget.org/OIDC,
byte comparison/readback, and authenticated retained-byte recovery. Successor fixtures check
four exact package identities, metadata, adapter .fsi/API entries, Core's FSharp.Core-only
runtime boundary and the adapter's absence of rendering dependencies. Candidate and public
smokes copy independent source into a fresh directory outside the checkout and restore only
PackageReferences; neither sibling projects nor repository imports/linked source qualify them.
The smoke executes the same complete 240-tick falling/joint/sensor and exact replay scene.
Candidate artifacts establish preflight only; the public-only fresh restore after genuine
both-feed readback is required before `.3` can close.

Pipeline-preflight selection is the existing-path static check plus focused mutation controls,
placed before release restore/build/tests. The changes alter scalar/roster and first-release
baseline selection while preserving the existing effect order and recovery route, so a custom
state model is deferred: its maintenance would duplicate unchanged controls. Investment is
bounded to the successor release fixture and isolated consumer; no new release framework or
registry authority is introduced. Source controls accepted the good workflow and rejected
substituted first/second/both push commands, a wrong scalar, foreign API baseline, missing
adapter custody, and missing/foreign/wrong-version artifacts before consumer launch. The
measured local source-only mutation run took about 0.22 seconds with no restore or downloads.
No billed runner savings or native publication acceptance are inferred from that measurement.
Local candidate preflight passed on source `2eaca0271431a1239f9e1ba2640005687b3bd63d`:
one coherent preparation produced exactly the four `0.17.0` packages, with existing-three
API compatibility against `0.16.0` and first-release adapter validation. The source-bound
metadata/API/dependency checks and independent Journey consumer passed. A fresh copied
Box2D package-only consumer cold-restored the candidate packages into an empty cache,
proved an exact four-package dependency closure (adapter, Core, Box2D.NET and FSharp.Core),
and ran the real 240-tick falling/joint/sensor scene with exact replay. It reported
.NET 10.0.12 / Arch Linux / X64, final falling height `0.39992859959602356` m and maximum
joint length error `0.00008517808537522598` m, with sensor enter/exit and floor contact.
The original candidate bytes and consumer inputs were retained; no package was published.
This candidate evidence binds the named source, independently of later documentation edits.
Native source delivery, both-feed publication/readback and public-only installed qualification
remain pending.

## Portal transfer source join for the selected release

The isolated successor joins the release source with the qualified P2 adapter/real headless
portal scene (`10f63b469ec8b63635a8f4c5fe6e66f11010b3c9`). Source acceptance of that candidate
remains separate from its native merge readback. The package-only consumer now copies that
scene into its own `PortalScene.fs` and compiles it only against restored package APIs, alongside
the existing falling-body scene. It requires one live traveller after the rotated transfer,
transformed pose/momentum, one step per local world/tick, one traversal, no repeated transfer and
exact fresh-world replay. The package metadata gate also requires the packed `AreaRuntime.fsi`.

The earlier immutable `2eaca027` preparation and its four artifacts remain historical; they do
not contain or qualify the expanded area-transfer API. The joined successor needs a fresh
version-consistent restore (including the new portal example lock), existing-three API/package
validation, one fresh four-package preparation and independent candidate smoke for both scenes.
Public installed support still requires genuine publication, both-feed readback and the public-only
consumer; product/template opt-in remains separate. No old candidate is substituted for that
fresh obligation.
2 changes: 1 addition & 1 deletion examples/Box2D.Headless/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"type": "Project",
"dependencies": {
"Box2D.NET": "[3.1.654, )",
"FS.GG.Game.Core": "[0.16.0, )",
"FS.GG.Game.Core": "[0.17.0, )",
"FSharp.Core": "[10.1.302, )"
}
},
Expand Down
2 changes: 1 addition & 1 deletion examples/Box2D.Portals/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"type": "Project",
"dependencies": {
"Box2D.NET": "[3.1.654, )",
"FS.GG.Game.Core": "[0.16.0, )",
"FS.GG.Game.Core": "[0.17.0, )",
"FSharp.Core": "[10.1.302, )"
}
},
Expand Down
2 changes: 1 addition & 1 deletion src/Playtest.Cli/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"fs.gg.game.harness": {
"type": "Project",
"dependencies": {
"FS.GG.Game.Core": "[0.16.0, )",
"FS.GG.Game.Core": "[0.17.0, )",
"FSharp.Core": "[10.1.302, )"
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/ReferenceGame/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"fs.gg.game.harness": {
"type": "Project",
"dependencies": {
"FS.GG.Game.Core": "[0.16.0, )",
"FS.GG.Game.Core": "[0.17.0, )",
"FSharp.Core": "[10.1.302, )"
}
}
Expand Down
4 changes: 2 additions & 2 deletions tests/Game.Harness.Tests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,14 @@
"fs.gg.game.harness": {
"type": "Project",
"dependencies": {
"FS.GG.Game.Core": "[0.16.0, )",
"FS.GG.Game.Core": "[0.17.0, )",
"FSharp.Core": "[10.1.302, )"
}
},
"fs.gg.game.reference": {
"type": "Project",
"dependencies": {
"FS.GG.Game.Harness": "[0.16.0, )",
"FS.GG.Game.Harness": "[0.17.0, )",
"FSharp.Core": "[10.1.302, )"
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Game.Physics.Box2D.Tests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
"type": "Project",
"dependencies": {
"Box2D.NET": "[3.1.654, )",
"FS.GG.Game.Core": "[0.16.0, )",
"FS.GG.Game.Core": "[0.17.0, )",
"FSharp.Core": "[10.1.302, )"
}
},
Expand Down
2 changes: 1 addition & 1 deletion tests/Game.Render.Tests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@
"fs.gg.game.render": {
"type": "Project",
"dependencies": {
"FS.GG.Game.Core": "[0.16.0, )",
"FS.GG.Game.Core": "[0.17.0, )",
"FS.GG.UI.KeyboardInput": "[0.31.0, )",
"FS.GG.UI.Scene": "[0.31.0, )",
"FSharp.Core": "[10.1.302, )"
Expand Down
6 changes: 3 additions & 3 deletions tests/Playtest.Cli.Tests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,21 +118,21 @@
"fs.gg.game.harness": {
"type": "Project",
"dependencies": {
"FS.GG.Game.Core": "[0.16.0, )",
"FS.GG.Game.Core": "[0.17.0, )",
"FSharp.Core": "[10.1.302, )"
}
},
"fs.gg.game.reference": {
"type": "Project",
"dependencies": {
"FS.GG.Game.Harness": "[0.16.0, )",
"FS.GG.Game.Harness": "[0.17.0, )",
"FSharp.Core": "[10.1.302, )"
}
},
"fsgg-playtest": {
"type": "Project",
"dependencies": {
"FS.GG.Game.Harness": "[0.16.0, )",
"FS.GG.Game.Harness": "[0.17.0, )",
"FSharp.Core": "[10.1.302, )"
}
}
Expand Down
17 changes: 17 additions & 0 deletions tests/release/Box2D.PackageConsumer/Consumer.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<IsPackable>false</IsPackable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
<DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference>
<DisableImplicitLibraryPacksFolder>true</DisableImplicitLibraryPacksFolder>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
<ItemGroup><Compile Include="Scene.fs" /><Compile Include="PortalScene.fs" /><Compile Include="Program.fs" /></ItemGroup>
<ItemGroup>
<PackageReference Include="FS.GG.Game.Physics.Box2D" Version="[0.17.0]" />
<PackageReference Include="FSharp.Core" Version="[10.1.302]" />
</ItemGroup>
</Project>
Loading
Loading