Build, test, and package ILSpy on Linux and macOS in CI#3768
Merged
Conversation
Member
Author
|
First-run checklist validated against run 27337878106 (all 4 jobs green):
Downloaded-artifact inspection (Ubuntu 24.04): launcher is 🤖 Generated with Claude Code |
With the move to Avalonia the app runs on Linux and macOS, so the distribution tooling grows beyond Windows: publish.ps1 gains a -Platform parameter (the default keeps the Windows behavior unchanged), and new BuildTools scripts package the self-contained publish outputs into a zip, deb, and rpm on Linux and a zipped ILSpy.app on macOS. Nothing is signed at package time; signing happens offline on the release manager machine. Info-ZIP zip is used instead of Compress-Archive because the latter drops unix mode bits. deb/rpm are built with dpkg-deb/rpmbuild directly (both preinstalled on GitHub ubuntu runners) following sourcegit-scm/sourcegit's proven model, including the libicu OR-chain in the deb control file and disabled auto-requires for the self-contained rpm payload. Pre-release versions map '-' to '~' so both package managers sort them before the release. The macOS bundle's Info.plist now carries placeholders stamped at package time; on Windows the spec/control/desktop files are kept LF via .gitattributes because Linux tools consume them verbatim. Assisted-by: Claude:claude-fable-5:Claude Code
Two new jobs run alongside the Windows matrix: each builds the desktop solution filter on its own OS, runs the headless UI tests there, and packages Release self-contained bundles (zip/deb/rpm on Linux, zipped ILSpy.app on macOS) via the BuildTools packaging scripts. Decompiler tests, NuGet packing, and the Windows installers stay in the Windows job, and neither new job checks out the ILSpy-tests submodule, since that only feeds the decompiler tests. The Windows job is untouched so existing required-status-check names keep working. Assisted-by: Claude:claude-fable-5:Claude Code
The macOS CI artifact is an unsigned zipped ILSpy.app; the signed distribution channel is a dmg the release manager creates offline with create-dmg.ps1 (codesign and notarization stay optional parameters, so no signing secrets ever live in CI). Distribution via Homebrew uses a tap repository holding only a cask file that points at the dmg attached to the GitHub release; the cask template and per-release update steps are documented next to the scripts. Assisted-by: Claude:claude-fable-5:Claude Code
The first CI run on a macOS runner surfaced that these tests encoded the Windows/Linux menu shape, while MainMenu.Attach intentionally diverges on macOS: TranslateGesturesForMacOS rewrites Control to Meta so shortcuts follow the Cmd-key convention, and PromoteHelpToMacAppMenu moves the Help items into the application menu and drops the _Help top-level. The tests now assert the macOS-correct expectations on macOS, including that the Help content lands in the app menu rather than vanishing. Assisted-by: Claude:claude-fable-5:Claude Code
1ff8d5b to
51af1dd
Compare
Member
|
The decompiler test suite is platform-aware: on non-Windows,
Tester.SupportedOnCurrentPlatform keeps only the dotnet-hosted Roslyn
configs and [Platform("Win")] gates the fixtures that truly need
Windows (legacy csc, mcs, 32-bit, roundtrip), so running the suite on
the Linux and macOS jobs gives real coverage of the non-Windows
decompilation paths instead of leaving them untested. Both jobs now
also check out the ILSpy-tests submodule, because the TargetNet40
configs compile against its legacy reference assemblies even when the
compiled output is never executed.
Assisted-by: Claude:claude-fable-5:Claude Code
The Linux and macOS jobs were near-identical copies; their shared head (checkout, SDK setup, version, restore, build, both test steps, test-log upload) drifts the moment one is edited and the other is not. Collapse them into a single platform-parameterized matrix job, and hoist the .NET SDK version/quality and the staging directory to workflow-level env so an SDK bump is a one-line change across all jobs. The Windows job keeps its id (Build) so internal references stay stable, but gains a display name to read as "Desktop (Windows)" alongside the matrix "Desktop (linux)/(macos)" checks. Branch-protection required checks must be repointed to the new check names. Assisted-by: Claude:claude-opus-4-8:Claude Code
siegfriedpammer
approved these changes
Jun 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
With the move to Avalonia, ILSpy runs on Linux and macOS, but CI only built Windows artifacts. This adds parallel Linux and macOS jobs so every platform gets compiled, UI-tested, and packaged on its native OS, per the requirements gathered in NewBuildPipeline.md:
ILSpy.Tests) and the decompiler tests run on each platform (on non-Windows the suite self-restricts to the dotnet-hosted Roslyn configs viaTester.SupportedOnCurrentPlatform/[Platform("Win")]). NuGet packing, MSI, VSIX, andILSpy.Tests.Windowsstay Windows-only.What
Publishing (
publish.ps1): new-Platform windows|linux|macosparameter; default is the existing Windows behavior verbatim. Linux/macOS publish self-containedlinux-x64/osx-arm64bundles. On macOS, ILSpy.ReadyToRun is published before ILSpy because theBuildMacAppBundletarget snapshots the publish directory intoILSpy.appwhen ILSpy publishes.Linux packaging (
BuildTools/package-linux.ps1, modeled on sourcegit-scm/sourcegit):.debvia dpkg-deb, with the libicu OR-chain in Depends and a.desktopfile + 256x256 icon (extracted from the existing .ico).rpmvia rpmbuild, withAutoReqProv: noso the self-contained payload does not generate bogus auto-requires-to~(11.0.0.8948-preview1->11.0.0.8948~preview1), legal in both formats and sorts before the final releasemacOS packaging (
BuildTools/package-macos.ps1): stamps the version placeholders in the bundle Info.plist (replacing the stale hardcoded 10.1.0) and zipsILSpy.appwith symlinks and exec bits preserved.Offline release tooling (never called from CI):
BuildTools/packaging/macos/create-dmg.ps1builds the dmg with optional codesign/notarization parameters, andBuildTools/packaging/homebrew/ilspy.rbis the cask template for a futureicsharpcode/homebrew-ilspytap. Zero signing secrets in any CI; the tap holds only the cask file.Per the "simplicity wins" rule, Flatpak, PPA, OBS, and AppImage were considered and dropped; deb/rpm are distributed as GitHub release attachments only.
Verification done
dpkg -I/dpkg -cmetadata and payload correct;apt-get install --simulateresolves all deb dependencies;rpm -qpi/-qpR/-qplcorrect; plist stamped to11.0.0.8948/11.0.0..gitattributesLF rules for the Linux-consumed files.First-run checklist (things only CI can prove)
rpmbuildpresent on ubuntu-latest (script has a tool guard with an actionable message; add an apt-get step only if missing)sudo apt-get install -y fontconfig fonts-dejavu-core)🤖 Generated with Claude Code