You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Offer IT-published .pkg, .dmg and .zip apps in the macOS Software Catalog - #5603
A macOS "Installer packages" manager (pkg), fed by the catalog itself. A catalog in the machine-wide SoftwareCatalog.json declares a pkgFeed: a folder or mounted share read directly, or an https folder read through the index.json that unigetui pkg-index <folder> writes. Every package in the feed joins that catalog.
Formats:.pkg, .dmg holding an app or a .pkg, and .zip holding an app. Identity, version, architectures and icon are read from the file (xar table of contents and payload, a read-only mounted image, or the zip entries).
Detection: by the package receipt, or by the app in place under its bundle id, so apps already deployed by Jamf or by hand are recognized and updates are offered.
Install and update: a private staged copy is checked against the index hash, its identity and Gatekeeper (Developer ID signed and notarized, unless the catalog sets allowUnsignedPackages), then installed as root behind the macOS administrator prompt. .dmg/.zip apps are swapped into /Applications so that a failure keeps the previous version. Uninstall is refused (new CanUninstall capability).
Without a password:sudo unigetui pkg-helper install-daemon sets up a root LaunchDaemon so standard users can install too. A client sends only catalog id, package id and version over a Unix socket. The helper resolves and checks the package itself, and refuses anything the catalog does not publish or that a standard user could change (catalog, feed, package file, UniGetUI itself). Without it, installs fall back to the prompt.
Catalog Editor: a Package feed dialog sets a catalog's feed and previews what it publishes and which files it skips, with the reason.
Shared changes: InstalledPackagesKeepTheirSource and Sources.CanBeEdited capabilities (replacing Chocolatey and manager-id special cases), and an unpinned catalog entry of a NuGet-based manager now asks the feed for its latest version instead of Version='', so its icon is found.
Docs: docs/PKG_FEEDS.md for administrators, docs/CLI.md, and a section in AGENTS.md.
Testing
UniGetUI.PackageEngine.Tests on macOS: all 99 pkg-feed tests and the NuGet tests pass, including real pkgbuild, productbuild, hdiutil and ditto fixtures, an install over the helper socket, and the refusal cases. The rest of the suite fails the same 107 tests as main on macOS (Windows-only areas).
Tried in the app on macOS with real vendor files: Zoom (.pkg), GitKraken and ChatGPT (.dmg, ChatGPT installed), and the Devolutions Mac downloads (.dmg and .pkg, detected from the existing installs with updates offered), plus the Package feed dialog.
dotnet format whitespace and dotnet format style (--verify-no-changes) pass, and the osx-arm64 NativeAOT publish adds no trim or AOT warnings.
Not run locally: the Windows-only UniGetUI.Tests (catalog model, Catalog Editor and identity tests) and the Chocolatey change, which this PR's CI covers. The package helper has not yet run as a real LaunchDaemon on a Mac.
- Stage packages and mount disk images in folders created at once with
mode 0700, with no shared parent another user could own.
- Trust a path only when each of its components, and each link it goes
through, is protected, and when no access control entry lets a
standard user change it.
- Give the feed index download one overall deadline, stop a package
download that stalls, and stop a canceled tool before reporting it.
- Let the helper drop a client that stops reading instead of holding
every later install.
- Honor catalog version pins, skip pinned builds that cannot run on the
Mac, and find an update whichever feed offers it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[P2] Without a receipt, p.Installed is the app's bundle version, but p.Item.Version is the installer component's version. A component at 1.0 containing app 10.2 therefore produces a 10.1 → 1.0 update for installed app 10.1, which UpgradablePackagesLoader.IsPackageValid rejects as a downgrade. Keep update comparisons in the bundle-version domain while retaining the separate installer version for feed selection.
…second review
- Behind the administrator prompt, run UniGetUI itself as root
(pkg-helper install-item) to stage, check and install the package, so
the user's own processes cannot swap it after the checks. A build
standard users could replace keeps checking before the prompt.
- Authorize installs and preview feeds from the feed as it is now, never
from its last known contents.
- Stop before installing when the operation was canceled while the feed
was being read.
- Give a tool's whole exchange one deadline, and stop waiting for its
pipes when a process it left running keeps them open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
[P2] Changing Location leaves the previous feed's preview visible, and an in-flight inspection can also publish results after the address changes. This shows feed A's packages and trust warning under feed B's address even though Apply saves B. Clear the preview when Location changes and discard results and errors from inspections of an older location.
A failed Check feed operation only changes Summary, which is rendered as an ordinary TextBlock without a live-region announcement. Screen-reader users therefore receive no failure message when the asynchronous check finishes. Announce this error through AccessibilityAnnouncementService with Assertive priority, as the surrounding Catalog Editor does for its errors.
Skip macOS icon conversion when indexing packages on Linux
[P2] Linux builds expose pkg-index, but indexing a readable .pkg or .zip with an embedded icon launches the macOS-only /usr/bin/sips here. Process.Start throws Win32Exception, which the feed/icon readers do not catch, aborting index generation. Skip icon conversion outside macOS so Linux can still index these packages without generated icons.
Deduplicate ZIP icon entries before building the dictionary
[P2] ZIP archives can contain duplicate entry names, so duplicate Contents/Resources/AppIcon.icns entries make ToDictionary throw ArgumentException. Cold folder scans extract ZIP icons, and PkgFeed.IsUnreadable does not catch this exception, so one archive aborts the whole feed scan. Deduplicate icon entry names before constructing the dictionary.
Keep receipt-free update comparisons in the app-version domain
[P2] When receipt-free detection uses the installed app's bundle version, this constructor pairs it with the feed's package/component version, which can use a different numbering scheme. An app update from 2026.2 to 2026.3 inside package version 1.1 passes HasUpdate but is rejected as a downgrade by UpgradablePackagesLoader.IsPackageValid. Keep update comparisons in the app-version domain while retaining the feed's package version separately for installation target selection.
return Refuse($"No catalog with the id {request.Catalog} declares a package feed.");
var feed = feeds[0];
Uri location = feed.Location;
This branch has not been deployed
No deployments
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
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.
What it adds
A macOS "Installer packages" manager (
pkg), fed by the catalog itself. A catalog in the machine-wideSoftwareCatalog.jsondeclares apkgFeed: a folder or mounted share read directly, or an https folder read through theindex.jsonthatunigetui pkg-index <folder>writes. Every package in the feed joins that catalog..pkg,.dmgholding an app or a.pkg, and.zipholding an app. Identity, version, architectures and icon are read from the file (xar table of contents and payload, a read-only mounted image, or the zip entries).allowUnsignedPackages), then installed as root behind the macOS administrator prompt..dmg/.zipapps are swapped into/Applicationsso that a failure keeps the previous version. Uninstall is refused (newCanUninstallcapability).sudo unigetui pkg-helper install-daemonsets up a root LaunchDaemon so standard users can install too. A client sends only catalog id, package id and version over a Unix socket. The helper resolves and checks the package itself, and refuses anything the catalog does not publish or that a standard user could change (catalog, feed, package file, UniGetUI itself). Without it, installs fall back to the prompt.Shared changes:
InstalledPackagesKeepTheirSourceandSources.CanBeEditedcapabilities (replacing Chocolatey and manager-id special cases), and an unpinned catalog entry of a NuGet-based manager now asks the feed for its latest version instead ofVersion='', so its icon is found.Docs:
docs/PKG_FEEDS.mdfor administrators,docs/CLI.md, and a section inAGENTS.md.Testing
UniGetUI.PackageEngine.Testson macOS: all 99 pkg-feed tests and the NuGet tests pass, including realpkgbuild,productbuild,hdiutilanddittofixtures, an install over the helper socket, and the refusal cases. The rest of the suite fails the same 107 tests asmainon macOS (Windows-only areas)..pkg), GitKraken and ChatGPT (.dmg, ChatGPT installed), and the Devolutions Mac downloads (.dmgand.pkg, detected from the existing installs with updates offered), plus the Package feed dialog.dotnet format whitespaceanddotnet format style(--verify-no-changes) pass, and the osx-arm64 NativeAOT publish adds no trim or AOT warnings.UniGetUI.Tests(catalog model, Catalog Editor and identity tests) and the Chocolatey change, which this PR's CI covers. The package helper has not yet run as a real LaunchDaemon on a Mac.