# Packaging & distribution

`RVZSharp` is published as a NuGet package. This page documents what the package contains,
how to build it, how to publish it, and the quality gates that protect the API.

## Package facts

| | |
|---|---|
| Package ID | `RVZSharp` |
| Version | `1.1.0` (SemVer; bumped per release) |
| Target frameworks | `net8.0`, `net9.0`, `net10.0` |
| License | GPL-2.0-or-later (`PackageLicenseExpression`) |
| Dependencies | `LZMA-SDK`, `SharpZipLib`, `ZstdSharp.Port` (all pure managed) |
| Symbols | embedded portable PDBs inside the assemblies (no `.snupkg`) |
| SBOM | SPDX 2.2 manifest embedded at `_manifest/spdx_2.2/manifest.spdx.json` (Release packs) |
| Reproducible | deterministic builds (`ContinuousIntegrationBuild` for release packs) |

## What's inside the package

```
lib/net8.0/RVZSharp.dll        assemblies per target framework
lib/net8.0/RVZSharp.xml        XML API documentation (IntelliSense)
lib/net9.0/…
lib/net10.0/…
README.md                      package readme (shown on nuget.org)
LICENSE                        GPL-2.0-or-later text
THIRD-PARTY-NOTICES.md         MIT notice for the vendored LZMA decoder
_manifest/spdx_2.2/…           SPDX 2.2 SBOM (+ .sha256) generated by Microsoft.Sbom.Targets
```

## Building the package

```bash
dotnet pack RVZSharp/RVZSharp.csproj -c Release
# output: RVZSharp/bin/Release/RVZSharp.<version>.nupkg
```

For a deterministic release build (reproducible SourceLink paths):

```bash
dotnet pack RVZSharp/RVZSharp.csproj -c Release -p:ContinuousIntegrationBuild=true
```

## Quality gates

1. **Zero warnings** — `TreatWarningsAsErrors` is on; the pack fails on any analyzer
   warning.
2. **Package validation** — `EnablePackageValidation` checks at pack time that the
   `net8.0`/`net9.0`/`net10.0` assets are compatible with the package's supported
   frameworks, and `PackageValidationBaselineVersion` (currently `1.0.0`) diffs the public
   API against the last published release (API-compat analysis). A breaking change fails
   `dotnet pack`; intentional breaks must be suppressed in a `CompatibilitySuppressions.xml`
   with a justification.
3. **Tests on every framework** — `dotnet test CSharp_RVZSharp.sln -c Release` runs the
   fast suite (609 tests) on `net8.0`, `net9.0` and `net10.0`; the real-file slow suite
   (`dotnet test RVZSharp.Slow.Tests -c Release`) runs on machines with the games mounted.
4. **Consumer smoke test** — before publishing, a fresh project consuming only the nupkg
   (from a local feed) must compile and run on .NET 8 and .NET 10, converting and decoding
   a disc image byte-exactly. This catches packaging mistakes (missing files, wrong
   dependency graph) that unit tests cannot.
5. **Native AOT / trimming** — the library sets `IsAotCompatible`/`IsTrimmable` and the CLI
   sets `EnableAotAnalyzer`/`EnableTrimAnalyzer`, so trim/AOT warnings fail the build on all
   frameworks. Release smoke test: `dotnet publish RVZSharp.Cli -r win-x64
   -p:PublishTrimmed=true` (and `-p:PublishAot=true`) must produce warning-free binaries
   that round-trip a disc image byte-exactly.
6. **CI** — `.github/workflows/ci.yml` builds and runs the fast suite on all three
   frameworks (coverage artifact), packs with API validation + SBOM, and publishes the
   framework-dependent single-file CLI (win-x64) that is smoke-tested before upload.
   `.github/workflows/release.yml` builds the release bundles (below),
   `.github/workflows/wiki.yml` mirrors `docs/` to the wiki and
   `.github/workflows/pages.yml` deploys `docs/` to GitHub Pages. Dependabot
   (`.github/dependabot.yml`) keeps NuGet and GitHub Actions dependencies current.

## Release process

Releases are built by `.github/workflows/release.yml`. Push a tag `v<version>`
(`v1.1.0`, `v1.2.0-beta.1`, … — valid SemVer after the leading `v`):

1. **CLI bundles** — the framework-dependent single-file CLI is published for six
   runtimes, smoke-tested on a native runner (`--help` must exit 0) and zipped, each
   zip holding the executable plus `LICENSE`, `README.md`, `WhatsNew.md` and
   `THIRD-PARTY-NOTICES.md`:

   | Asset | Runner |
   |---|---|
   | `rvzsharp_v<version>_win-x64.zip` | Windows Server 2025 (x64) |
   | `rvzsharp_v<version>_win-arm64.zip` | Windows 11 (arm64) |
   | `rvzsharp_v<version>_linux-x64.zip` | Ubuntu 24.04 (x64) |
   | `rvzsharp_v<version>_linux-arm64.zip` | Ubuntu 24.04 (arm64) |
   | `rvzsharp_v<version>_osx-x64.zip` | macOS 15 (Intel) |
   | `rvzsharp_v<version>_osx-arm64.zip` | macOS 26 (Apple Silicon) |

   The bundles are framework-dependent: the .NET 10 runtime is **not** embedded, so
   target machines install it (see
   [Getting started](getting-started.md#install-the-cli)). On Linux and macOS the zip
   is built with `zip(1)` so the executable bit survives extraction.
2. **NuGet package** — `dotnet pack` with API-compat validation and the embedded
   SBOM (`RVZSharp.<version>.nupkg`; the PDBs are embedded, so no symbols package).
3. **GitHub Release** — created with the six zips, `SHA256SUMS.txt` and both
   packages. The notes come from `docs/release-notes-<version>.md` when that file
   exists (`release-notes-1.0.0.md` shows the format); otherwise a fallback body
   is used. Tags containing `-` are marked as pre-releases.
4. **nuget.org** — stable versions are pushed automatically when the
   `NUGET_API_KEY` repository secret is set (nuget.org → API Keys → repository
   secret); without it the workflow logs a warning and the GitHub Release remains
   the only published artifact.

Run the workflow manually (`workflow_dispatch` with a `version` input) for a dry
run: everything builds and uploads as run artifacts for inspection, but no GitHub
Release is created and nothing is pushed to nuget.org.

## Wiki and Pages automation

`docs/` is the single source of the documentation and is published twice by CI:

- **GitHub Pages** — `.github/workflows/pages.yml` builds `docs/` with Jekyll on
  every `master` push touching `docs/` (plus manual runs). `_config.yml` selects
  the Cayman theme, `index.md` renders this folder's `README.md` as the home
  page, `_data/nav.yml` + `_layouts/default.html` render the side menu, and
  internal links use the `.md` form (`[CLI](usage-cli.md)`), which Jekyll converts
  to `.html`.
- **GitHub Wiki** — `.github/workflows/wiki.yml` mirrors `docs/` into the
  repository wiki on the same trigger: `README.md` → `Home.md`, `format/*.md` →
  `format/…` subpages, `_Sidebar.md` kept as-is (the wiki sidebar), Jekyll-only
  files (`index.md`, `_config.yml`, `_data/`, `_layouts/`) dropped, and relative
  `page.md` links rewritten to the extensionless wiki form (`[CLI](usage-cli)`;
  anchors and absolute URLs untouched). The wiki is fully generated — edit
  `docs/`, never the wiki pages (manual edits are overwritten on the next sync).

One-time setup: enable Wikis (Settings → General → Features) and save a personal
access token with the `repo` scope (or a fine-grained token with Contents
read/write on this repository) as the `WIKI_TOKEN` repository secret; without it
the workflow fails with setup instructions.

## Publishing to nuget.org

Pushing a stable `v<version>` tag does this automatically (see [Release
process](#release-process) — the `NUGET_API_KEY` secret must be set). The manual
equivalent is:

```bash
# 1. Build the package.
dotnet pack RVZSharp/RVZSharp.csproj -c Release

# 2. Push (the API key comes from nuget.org → API Keys).
dotnet nuget push RVZSharp/bin/Release/RVZSharp.1.1.0.nupkg \
    --source https://api.nuget.org/v3/index.json \
    --api-key <NUGET_API_KEY>
```

After publishing, verify the package page: readme rendering, license, dependencies, the
`lib/` folder list for all three target frameworks, and the embedded
`_manifest/spdx_2.2` SBOM.

## Versioning policy

- The package version follows SemVer. The public API is diffed against the last published
  release at pack time (`PackageValidationBaselineVersion`), so breaking changes must be
  deliberate and suppressed with a justification.
- Public API changes are intentional and reviewed: the reader/writer surface
  (`Blob.Open`, `RvzReader`, `RvzWriter.Write`) is stable; format-specific structs
  (`WiaDisc`, `WiaPartEntry`, …) may grow fields.

## Local development feed

To test the package without publishing:

```xml
<!-- nuget.config -->
<configuration>
  <packageSources>
    <clear />
    <add key="local" value="D:\path\to\RVZSharp\bin\Release" />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
</configuration>
```

```bash
dotnet add package RVZSharp --version 1.1.0
```

## The CLI

The command-line tool (`RVZSharp.Cli`) is **not** packaged as a NuGet tool — it is a
reference implementation and smoke-test surface for the library. It targets `net10.0`
and ships as framework-dependent single-file binaries (one executable per runtime; the
.NET 10 runtime is a prerequisite on the target machine, it is not embedded):

```bash
dotnet build CSharp_RVZSharp.sln -c Release
dotnet run --project RVZSharp.Cli -c Release -- header -i game.rvz

# Release bundles (all six RIDs), framework-dependent single-file:
dotnet publish RVZSharp.Cli -c Release -f net10.0 -r win-x64 --self-contained false \
    -p:PublishSingleFile=true
```

End users do not need the SDK: each GitHub Release (see [Release
process](#release-process)) ships `rvzsharp_v<version>_<rid>.zip` bundles
(`win-x64`, `win-arm64`, `linux-x64`, `linux-arm64`, `osx-x64`, `osx-arm64`) — unzip,
install the [.NET 10 Runtime](https://dotnet.microsoft.com/download/dotnet/10.0) and run
`RVZSharp` (`RVZSharp.exe` on Windows); see [Getting
started](getting-started.md#install-the-cli).
