Contributing / building from source

June 18, 2026 · View on GitHub

Developer docs for building, packaging and releasing Downloader Desktop. End users don't need any of this — see the README for install instructions.

Prerequisites

Get the source

git clone https://github.com/bezzad/Downloader.Desktop.git
cd Downloader.Desktop/src

All commands below run from the src/ folder (where Downloader.Desktop.sln lives).

Run & test

dotnet restore
dotnet build
dotnet run  --project Downloader.Desktop/Downloader.Desktop.csproj
dotnet test

Platform notes:

  • Linux: needs a desktop session (X11/Wayland). Running from an IDE debugger (e.g. Rider) can group the taskbar entry under the IDE host — run the built binary directly for the real taskbar icon.
  • Windows: an unsigned build may trigger SmartScreen — choose More info → Run anyway (sign builds for distribution).

Publish a self-contained build

# one command, outputs to dist/ (self-contained single file, no end-user dependencies)
./scripts/publish.sh linux-x64 win-x64 osx-arm64 osx-x64

Or per RID:

dotnet publish Downloader.Desktop/Downloader.Desktop.csproj -c Release -r linux-x64 \
  --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -o publish/linux-x64

Common RIDs: linux-x64, win-x64, osx-x64, osx-arm64.

Releasing a new version

The version is automatic: bump VersionPrefix (major.minor) in Downloader.Desktop.csproj when you want; build/revision come from the build time. To cut a release:

git checkout main && git pull
dotnet test
git tag v1.0.0          # match major.minor to VersionPrefix
git push origin v1.0.0  # triggers .github/workflows/release.yml

release.yml builds self-contained executables for win-x64, linux-x64, osx-x64, osx-arm64, creates the GitHub Release for the tag, and attaches the archives. dotnet-desktop.yml runs the test suite on every push/PR. To re-run a release, delete the tag (git tag -d v1.0.0 && git push origin :refs/tags/v1.0.0) and the GitHub Release, then re-tag.

Package listings

  • winget: templates + steps in packaging/winget/ (submit via wingetcreate to microsoft/winget-pkgs).
  • Homebrew: cask in Casks/downloader.rb — publish via a homebrew-tap repo.
  • Linux installer: scripts/install.sh (curl | bash) installs the latest release + a .desktop entry and icon.

macOS .app bundle + signing

The macOS release ships a proper Downloader.app bundle (Spotlight-visible, launches detached) — both release.yml and ./scripts/publish.sh osx-arm64 produce it by wrapping the plain net10.0 self-contained binary via scripts/make-macos-app.sh. No extra workload is required. (The native macOS TFM path — -p:IsMacBuild=true, targeting net10.0-macos — is separate and needs dotnet workload install macos + a recent Xcode; it is not used by the release.)

# Wrap an existing publish output into Downloader.app (done automatically by publish.sh/release.yml):
scripts/make-macos-app.sh publish/osx-arm64 publish/osx-arm64-app 1.2.3
# Distribute outside the App Store: sign the bundle with a Developer ID certificate
codesign --force --options runtime --sign "Developer ID Application: Behzad Khosravifar (XXXX)" \
  "publish/osx-arm64-app/Downloader.app"

Avalonia macOS packaging guide

Architecture

See CLAUDE.md and the project skill in .claude/skills/downloader-desktop/ for the full architecture, conventions and per-feature notes.