Novolis Docs
novolis-governance / completed-plans/nuget_versioning_scheme_43388e4a.plan.md

Implement NuGet versioning (`2026.APIBREAK.FEATURE`)

dotnetgovernancenovolis

name: NuGet versioning scheme overview: Migrate the Novolis platform from committed 4-part 0.0.1.{build} versioning to the SDKYEAR.APIBREAK.FEATURE + CI BUILD scheme in nuget-versioning.md, with 2026.1.* floating cross-repo dependencies, while keeping GitHub Release–triggered stable publishes and cutting over at 2026.1.0. todos:

  • id: governance-version-model

content: Add build/version.json + version.props sync, rewrite Novolis.Version.targets, floating-version MSBuild props status: completed

  • id: workflows-read-pack

content: Add read-version + dotnet-pack-versioned actions; update merge/release/raylib workflows; remove build bump commits status: completed

  • id: normalize-deps

content: Update normalize-package-versions.ps1 and stack Directory.Packages.props to 2026.1.* + CentralPackageFloatingVersionsEnabled status: completed

  • id: repo-rollout

content: Batch-migrate all packable novolis-* repos (2026.1.0 cutover, remove .novolis/version.props) status: completed

  • id: docs-validate

content: Update release-policy.md and validate PR/merge/release/local pack flows status: completed isProject: false


Current vs target

AreaTodayTarget (per nuget-versioning.md)
Version source`.novolis/version.props` (NovolisVersionMajor/Minor/Patch/Build)`build/version.json` (sdkYear, apiBreak, feature) — not eng/ (ambiguous)
Merge publish4-part 0.0.1.{build}; commits build bump2026.1.0-ci.{run} to GitHub Packages; BUILD = `github.run_number` only (no commit)
Stable publishTag from GitHub Release → dotnet-release-publish.ymlSame trigger (your choice); pack `2026.1.0` (3-part, no build)
Cross-repo depsFixed 0.0.1.1 in stack `Directory.Packages.props`Floating `2026.1.*` (from build/version.json)
MSBuildNovolis.Version.targets maps 4-part propsCI-injected Version / PackageVersion / assembly metadata per doc
flowchart LR
  subgraph intent [Human intent]
    VJ[build/version.json]
  end
  subgraph ci [CI on main]
    MR[merge.yml]
    RW[dotnet-merge-publish]
    PACK["pack PackageVersion=2026.1.0-ci.N"]
    GPR[GitHub Packages]
  end
  subgraph stable [Stable release]
    REL[GitHub Release published]
    RR[dotnet-release-publish]
    NUG[nuget.org 2026.1.0]
  end
  VJ --> RW
  MR --> RW --> PACK --> GPR
  VJ --> RR
  REL --> RR --> NUG

Scope: Implement in existing `novolis-workflows` + `novolis-governance`, then roll out to ~20 packable novolis-* repos. Doc references frankhaugen/shared-workflows; do not create a new repo—extend Novolis-Platform/novolis-workflows already in use.


Phase 1 — Version model and MSBuild (novolis-governance)

1.1 Add build/version.json template

Naming: use repo-root build/ for version intent—not eng/ (ambiguous between engineering and English). This coexists with existing per-repo build/*Packaging.props (e.g. physics, rendering); version files are build/version.json and generated build/version.props.

Per-repo file (initial cutover `2026.1.0`):

{
  "sdkYear": 2026,
  "apiBreak": 1,
  "feature": 0,
  "dotnetBaseline": "net10.0",
  "publicPackage": true
}

1.2 Replace .novolis/version.props import chain

  • Update each repo `Directory.Build.props`: import build/version.props (generated MSBuild projection) instead of .novolis/version.props.
  • Remove .novolis/version.props after migration (update `merge.yml` paths-ignore to ignore build/version.json only if release bumps are committed manually—not for CI build bumps).

Recommended: commit `build/version.json` (source of truth) plus a thin `build/version.props` generated by script (MSBuild-friendly) to avoid requiring System.Text.Json in every build:

<NovolisSdkYear>2026</NovolisSdkYear>
<NovolisApiBreak>1</NovolisApiBreak>
<NovolisFeature>0</NovolisFeature>
<NovolisStableVersion>2026.1.0</NovolisStableVersion>
<NovolisPackageFloatVersion>2026.1.*</NovolisPackageFloatVersion>

Add `novolis-governance/scripts/sync-version-props.ps1` to regenerate build/version.props from JSON (used by release bump PRs and local dev).

1.3 Rewrite `Novolis.Version.targets`

  • Local / non-CI: if $(PackageVersion) unset, set stable $(NovolisStableVersion) for packable projects.
  • Do not set 4-part Version from committed build segment.
  • When CI passes -p:PackageVersion=... (see Phase 2), targets should not override it.
  • Apply assembly metadata from doc when CI properties present:
PropertyCI value
AssemblyVersion{sdkYear}.{apiBreak}.0.0
FileVersion{sdkYear}.{apiBreak}.{feature}.{run}
InformationalVersion{stable}+build.{run}.sha.{shortSha}
  • Add ContinuousIntegrationBuild when GITHUB_ACTIONS=true (already partially done in stack repos).

1.4 Central floating Novolis dependencies

Add to governance `Directory.Build.props` fragment (or template applied by configure-package-publishing.ps1):

<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageFloatingVersionsEnabled>true</CentralPackageFloatingVersionsEnabled>

Update `normalize-package-versions.ps1`:

  • Replace Novolis.* pins 0.0.1.1 → $(NovolisPackageFloatVersion) or literal 2026.1.* synced from build/version.json.
  • Apply to: novolis-physics, novolis-simulation, novolis-rendering, novolis-raylib, novolis-testing, and any other repo with PackageVersion Include="Novolis.*".
  • Update dogfooding from Version="*" to Version="2026.1.*" for consistency (still floating, but aligned to platform line).

GPR / `-ci` note: NuGet floating 2026.1.* resolves stable versions only. Until 2026.1.0 exists on a feed, stack CI may need the first stable publish or temporary 2026.1.*-* on the GitHub Packages source. Validate in novolis-math → novolis-physics merge order after cutover; adjust `nuget.config` only if restore fails (document in `release-policy.md`).

1.5 Deprecate repo *Packaging.props fallbacks

Remove or narrow 0.0.1.1 / 0.1.0-local fallbacks in:

Governance targets become the single version path.


Phase 2 — CI actions and workflows (novolis-workflows)

2.1 New composite: read-version

Replace `resolve-version` logic:

Inputs: version-file (default build/version.json), optional prerelease-label (ci), build-number (github.run_number).

Outputs:

  • stableVersion → 2026.1.0
  • packageVersion → 2026.1.0-ci.382 (merge) or 2026.1.0 (release)
  • assemblyVersion, fileVersion, informationalVersion strings for -p: passthrough

Implementation: jq in bash (ubuntu-latest) or small inline Node—keep dependency-free if possible.

2.2 New composite: dotnet-pack-versioned

Wrap `pack`:

dotnet pack "$slnx" -c Release -o artifacts/packages \
  -p:Version="$PACKAGE_VERSION" \
  -p:PackageVersion="$PACKAGE_VERSION" \
  -p:AssemblyVersion="$ASSEMBLY_VERSION" \
  -p:FileVersion="$FILE_VERSION" \
  -p:InformationalVersion="$INFO_VERSION"

2.3 Update `dotnet-merge-publish.yml`

read-version (ci) → dotnet-build → dotnet-pack-versioned → publish-github-packages

Remove: `bump-build-version` and `commit-version-bump` from merge path.

Remove contents: write from merge jobs where only used for version commits (keep packages: write).

2.4 Update `dotnet-release-publish.yml`

Keep GitHub Release published trigger in per-repo `release.yml`.

  • `resolve-release-version`: validate tag matches v{sdkYear}.{apiBreak}.{feature} (e.g. v2026.1.0).
  • Pack with 3-part PackageVersion only (no -ci, no 4th segment).
  • Optional: fail if tag’s feature ≠ build/version.json (prevents accidental drift).

Feature bump: remains manual edit to build/version.json before creating the GitHub Release (doc’s workflow_dispatch bump can be a later enhancement; not required for your chosen trigger).

2.5 Raylib path

Update `dotnet-raylib-merge-publish.yml` and `raylib-pack-publish` to use read-version + versioned pack; drop commit-version-bump there too.

2.6 Workflow inputs (optional, low priority)

Add version-file: build/version.json to reusable workflow_call inputs for forward compatibility; default suffices for all Novolis repos.


Phase 3 — Per-repo rollout (batch via governance scripts)

Update `configure-package-publishing.ps1` and `apply-pr-merge-release-workflows.ps1` to:

  1. Create build/version.json + build/version.props at `2026.1.0`
  2. Delete .novolis/version.props
  3. Fix Directory.Build.props import path
  4. Normalize Directory.Packages.props Novolis refs → 2026.1.*
  5. Enable CentralPackageFloatingVersionsEnabled where missing
  6. Fix paths-ignore in merge workflows

Repos (~20): math, physics, simulation, rendering, raylib, commands, avalonia, aspire, analyzers, codegen, machinelearning, markup, messaging, security, storage, transports, wirefish, testing, templates, template-dotnet, install, smoketest.

Order suggestion: novolis-workflows + novolis-governance first → novolis-math (leaf) → physics → simulation → rendering → raylib → remaining leaves.


Phase 4 — Docs, registry, and validation

ItemAction
nuget-versioning.mdReplace eng/ with build/ throughout; add Novolis-specific note: workflows live in novolis-workflows, release trigger = GitHub Release
release-policy.mdRewrite for new scheme; remove “CI bumps build in git”
novolis-physics/docs/VERSIONING.mdPoint to platform doc or archive
`novolis-registry/packages/*.json`Update metadata versions to 2026.1.0 when publishing
Installer `Resolve-Version.ps1`Read build/version.json if it consumes package version

Validation checklist

  1. PR: build + test only; no publish; no version file changes.
  2. Merge math: GPR receives Novolis.Math.* at 2026.1.0-ci.{run}; git tree unchanged.
  3. Merge physics: restore Novolis.Math.Geometry at 2026.1.* succeeds against GPR (or documented prerelease workaround).
  4. Release math: create GitHub Release v2026.1.0 → nuget.org gets 2026.1.0; assembly/file/info versions match doc.
  5. Local: dotnet pack /p:NovolisLocalPack=true produces 2026.1.0 without CI env.

Explicit non-goals (this pass)

  • workflow_dispatch release with auto-bump PR (doc’s dotnet-nuget-release.yml); manual build/version.json + GitHub Release is enough per your choice.
  • New frankhaugen/shared-workflows repo.
  • eng/ folder anywhere in Novolis repos (use build/ instead).
  • Auto-increment feature from commit analysis (human-owned build/version.json only).

Risk summary

RiskMitigation
Floating 2026.1.* ignores -ci packagesPublish first stable 2026.1.0 to GPR or use 2026.1.*-* temporarily on GPR-only consumers
Breaking all existing 0.0.1.x consumersExpected platform cutover; document in release notes
Parallel merge workflows pin different ci buildsAcceptable with floating; consumers get latest matching stable/ci per NuGet rules
Raylib native pack path divergesDedicated workflow update in Phase 2.5