tools/Build contains the Reaparr desktop build CLI. It wraps the project-specific desktop publish, package, CI package, and run workflows behind a single Spectre.Console command application named reaparr-build.
The build tool is intended to be run from anywhere inside the Reaparr repository. At startup it walks up from the current working directory until it finds Reaparr.sln; that directory becomes the repository root used for all relative paths and default outputs.
Run the project with dotnet run and pass build-tool arguments after --:
dotnet run --project tools/Build -- desktop <command> [options]When published as a standalone tool or executable, the configured application name is:
reaparr-build desktop <command> [options]Parsing is strict and command names are case-insensitive. Unknown commands or options fail instead of being ignored.
All commands live under the desktop branch and share the same option set.
| Command | Alias | Purpose |
|---|---|---|
desktop publish |
none | Generate frontend assets, restore the AppHost for a RID, publish the desktop AppHost, and copy frontend output into the published wwwroot. |
desktop package |
desktop pack |
Run the full publish workflow and then create Velopack artifacts for the RID. |
desktop ci-package |
none | CI-focused package workflow. It assumes frontend assets were generated earlier, forces --skip-frontend, keeps restore/package enabled, then publishes and packages. |
desktop run |
none | Publish or package, then launch the desktop app when supported. |
Every desktop command validates these two options before publishing:
--version <VERSION>--informational-version <VERSION>
If either value is missing or blank, the command throws an error before running dotnet publish.
These values are passed to the AppHost publish as MSBuild properties:
-p:Version=<VERSION>
-p:InformationalVersion=<VERSION>
--rid must be one of the RIDs known by DesktopRuntimeCatalog. Matching is case-insensitive.
| RID | Publish profile | Main executable |
|---|---|---|
linux-x64 |
Desktop-linux-x64 |
Reaparr.AppHost |
linux-arm64 |
Desktop-linux-arm64 |
Reaparr.AppHost |
win-x64 |
Desktop-win-x64 |
Reaparr.AppHost.exe |
win-arm64 |
Desktop-win-arm64 |
Reaparr.AppHost.exe |
osx-x64 |
Desktop-osx-x64 |
Reaparr.AppHost |
osx-arm64 |
Desktop-osx-arm64 |
Reaparr.AppHost |
Unsupported RIDs fail with a message listing the supported values.
Selects the desktop runtime identifier. This controls:
- the AppHost publish profile, via
-p:PublishProfile=<profile> - the publish output directory
- the Velopack
--runtimevalue - the expected main executable name
- the default artifact directory
- the default Velopack channel name
- launch behavior for Linux and Windows outputs
This option is effectively required because the workflow immediately resolves it through DesktopRuntimeCatalog.Get(...). An empty or unsupported value fails before publishing.
Example:
dotnet run --project tools/Build -- desktop publish --rid linux-x64 --version 0.0.1 --informational-version 0.0.1-localSets the package/application version. The value is passed to dotnet publish as -p:Version=<VERSION> and to Velopack as --packVersion <VERSION>.
This option is required for all desktop commands because desktop package, desktop ci-package, and desktop run all include the publish workflow, and desktop publish validates it directly.
Example:
--version 1.2.3Sets the informational version passed to dotnet publish as -p:InformationalVersion=<VERSION>. It is also used to infer the default Velopack channel when --channel is not supplied.
Default channel inference:
- if the informational version contains
devusing case-insensitive matching, the channel is<rid>-dev - otherwise, the channel is
<rid>-stable
Examples:
--informational-version 1.2.3+abc123 # default channel: <rid>-stable
--informational-version 1.2.3-dev.4 # default channel: <rid>-devOverrides the Velopack channel. When omitted, the build tool derives a RID-specific channel from --informational-version:
- stable release:
<rid>-stable - dev release:
<rid>-dev
Examples:
--channel linux-x64-stable
--channel nightlyThe channel is passed to vpk pack as --channel <CHANNEL>.
Skips frontend generation. When this flag is not set, the publish workflow:
- verifies
bunis available, unless--dry-runis set - installs frontend dependencies with
bun install --frozen-lockfilewhensrc/AppHost/ClientApp/bun.lockexists andsrc/AppHost/ClientApp/node_modulesdoes not exist - runs
bun run generate --fail-on-errorinsrc/AppHost/ClientApp
Important: skipping frontend generation does not skip the later frontend copy step. Unless --dry-run is set, the workflow still copies frontend public assets into the published wwwroot. Use --frontend-public-dir to point at pre-generated assets when using --skip-frontend.
desktop ci-package always forces this option to true, regardless of the command-line value.
Skips the explicit RID-specific restore step. When not skipped, publish runs:
dotnet restore src/AppHost/AppHost.csproj --runtime <rid>The later publish command always includes --no-restore, so use --skip-restore only when the RID-specific restore has already been performed or the build environment otherwise guarantees restored assets.
desktop ci-package forces restore back on (SkipRestore = false).
Affects desktop run. When set, desktop run publishes only and launches the published output directly. It does not run Velopack packaging.
For Windows RIDs, desktop run --skip-package also exports convenience artifacts after publish:
- copies the published executable into the artifact directory
- copies the full publish directory into
<artifact-dir>/publish
This option has no effect on desktop publish, because that command never packages. It is not honored by desktop package or desktop ci-package; those commands call the package workflow directly after publish.
Logs the commands that would be run without executing external commands or performing file-copy side effects that require build outputs.
Dry-run behavior includes:
- skips checking that
dotnet,bun, andvpkexist RunCommandAsync(...)logs the command and returns without executing itExecuteCommandAsync(...)logs the command and returns exit code0- frontend output is not copied into the publish directory
- artifact cleanup is skipped
- launch is skipped by
desktop runafter publish/package planning
Dry-run still performs argument validation and still builds the command argument lists, making it useful for checking what the workflow would do.
Overrides the directory copied into the published wwwroot. The default is:
src/AppHost/ClientApp/.output/public
Relative paths are resolved from the repository root, not from the shell working directory.
This option is especially important for CI and --skip-frontend, where frontend generation may happen in a separate job or earlier step. The source directory must exist unless --dry-run is set.
Example:
--frontend-public-dir .tmp/frontend-publicOverrides the directory used for Velopack artifacts. The default is RID-specific:
.artifacts/<rid>
Relative paths are resolved from the repository root.
The directory is used as:
- the Velopack
--outputDir - the search location for Linux AppImage launch mode
- the Windows export location for
desktop run --skip-package
Example:
--artifact-dir ReleasesControls cleanup of the artifact directory before packaging.
Default behavior is to clear the artifact directory before running vpk pack. With this flag set, the tool creates the directory if needed but preserves existing files.
This only applies when packaging is actually executed and --dry-run is not set.
Use this when a release process intentionally accumulates artifacts in the same output directory. Avoid it when you need a clean package output.
Controls how desktop run launches Linux builds after packaging. The default is:
published
Supported values for Linux packaged runs are:
| Value | Behavior |
|---|---|
published |
Launch the executable from the publish directory. |
packaged |
Find the newest *.AppImage in the artifact directory, run chmod +x, and execute the AppImage. |
Validation of --launch-mode is only strict for Linux RIDs when packaging was not skipped. In that case, any value other than published or packaged fails.
For non-Linux RIDs, and for Linux runs with --skip-package, the workflow launches the published executable path and logs the requested launch mode.
Example:
dotnet run --project tools/Build -- desktop run --rid linux-x64 --version 1.2.3 --informational-version 1.2.3-dev.1 --launch-mode packageddesktop publish performs these steps:
- validate
--versionand--informational-version - require
dotnet, unless--dry-runis set - generate frontend assets unless
--skip-frontendis set - restore AppHost for the selected RID unless
--skip-restoreis set - publish AppHost with the selected publish profile
- copy frontend public output into
<publish-dir>/wwwroot, unless--dry-runis set
The publish command run by the tool is:
dotnet publish src/AppHost/AppHost.csproj \
-p:PublishProfile=<profile> \
-p:Version=<version> \
-p:InformationalVersion=<informational-version> \
-p:CSharpier_Bypass=true \
--no-restoreDefault publish directory:
src/AppHost/bin/Publish/Desktop/<rid>
desktop package and desktop ci-package run the publish workflow, then the package workflow.
When --dry-run is not set, package first requires vpk on PATH. It then clears the artifact directory unless --preserve-existing-artifacts is set.
Velopack is invoked as:
vpk pack \
--packId Reaparr \
--packTitle Reaparr \
--packVersion <version> \
--packDir <publish-dir> \
--mainExe <main-executable> \
--runtime <rid> \
--channel <channel> \
--outputDir <artifact-dir>desktop ci-package is a specialized wrapper around the package workflow. It copies the supplied settings but forces:
SkipFrontend = true
SkipRestore = false
SkipPackage = false
Use it when CI has already generated frontend output, but each RID job still needs to run restore, publish, and Velopack packaging.
Typical CI command:
dotnet run --project tools/Build -- desktop ci-package \
--rid linux-x64 \
--version 0.0.1 \
--informational-version 0.0.1-dev.1 \
--frontend-public-dir .tmp/frontend-public \
--artifact-dir Releasesdesktop run performs publish/package work and then launches the output unless --dry-run is set.
Default behavior:
- publish
- package
- launch published executable
With --skip-package:
- publish
- for Windows RIDs, export executable and full publish directory to the artifact directory
- launch published executable
Linux packaged launch:
--launch-mode packagedFor Linux RIDs, when packaging is not skipped and launch mode is packaged, the tool finds the newest *.AppImage in the artifact directory, marks it executable with chmod +x, and launches it.
Windows launch on non-Windows hosts:
- if
wineis available, the tool launches the published.exethroughwine - if
wineis not available, the tool logs the publish/artifact locations, warns that the Windows build was not launched, and exits successfully with0
Publish Linux x64:
dotnet run --project tools/Build -- desktop publish \
--rid linux-x64 \
--version 0.0.1 \
--informational-version 0.0.1-localPackage macOS ARM64 without modifying artifacts:
dotnet run --project tools/Build -- desktop package \
--rid osx-arm64 \
--version 0.0.1 \
--informational-version 0.0.1-local \
--dry-runUse the pack alias:
dotnet run --project tools/Build -- desktop pack \
--rid win-x64 \
--version 1.0.0 \
--informational-version 1.0.0 \
--channel win-x64-stableRun Windows x64 from a non-Windows host with Wine when available:
dotnet run --project tools/Build -- desktop run \
--rid win-x64 \
--version 0.0.1 \
--informational-version 0.0.1-localRun Linux AppImage after packaging:
dotnet run --project tools/Build -- desktop run \
--rid linux-x64 \
--version 0.0.1 \
--informational-version 0.0.1-dev.1 \
--launch-mode packagedPackage in CI from pre-generated frontend assets:
dotnet run --project tools/Build -- desktop ci-package \
--rid linux-x64 \
--version 0.0.1 \
--informational-version 0.0.1-dev.1 \
--frontend-public-dir .tmp/frontend-public \
--artifact-dir ReleasesDepending on options and command, the build tool expects these executables on PATH:
| Tool | Required when |
|---|---|
dotnet |
Any non-dry-run publish workflow. |
bun |
Frontend generation is not skipped and this is not a dry run. |
vpk |
Packaging is performed and this is not a dry run. |
chmod |
Linux desktop run --launch-mode packaged executes an AppImage. |
wine |
Optional for launching Windows builds from non-Windows hosts. If missing, the run command logs a warning and exits 0. |
All relative override paths are resolved from the repository root.
| Purpose | Default path |
|---|---|
| AppHost project | src/AppHost/AppHost.csproj |
| Client app directory | src/AppHost/ClientApp |
| Frontend public output | src/AppHost/ClientApp/.output/public |
| Publish output | src/AppHost/bin/Publish/Desktop/<rid> |
| Artifact output | .artifacts/<rid> |
- Frontend output is copied recursively into
<publish-dir>/wwwroot, overwriting files with the same names. - Packaging clears the artifact directory by default before invoking Velopack.
--preserve-existing-artifactsdisables artifact cleanup.- Windows
desktop run --skip-packageexports the published executable and a fullpublishdirectory under the artifact directory. --dry-runavoids command execution, frontend copying, artifact cleanup, and launch execution.
Use one of the supported RIDs listed above. The RID controls publish profiles and executable names, so arbitrary .NET RIDs are not accepted.
Pass both --version and --informational-version. They are required even for dry-runs.
The publish workflow could not find the frontend public directory to copy. Either run without --skip-frontend so the tool generates it, or pass --frontend-public-dir pointing at pre-generated frontend output.
Install the missing tool or update PATH. Use --dry-run to inspect commands without requiring external tools.
desktop run --launch-mode packaged looks for *.AppImage directly inside the artifact directory. Confirm packaging succeeded and that --artifact-dir points to the directory containing the AppImage.