Fix dnx ownership in native Linux packages - #7824
Conversation
Extend Microsoft.DotNet.Build.Tasks.Installers so the RPM writer can express package authoring that was previously impossible, in support of fixing the dnx file-ownership conflict between dotnet-host 11 and released dotnet-sdk-10.0. - RpmBuilder: emit RPMFILE_GHOST (flag 64) file header records that are omitted from the CPIO payload (AddGhostFile), and emit RPM file-trigger headers (AddFileTrigger) using the per-script / per-condition parallel-array layout that rpm expects (FileTriggerScripts/ScriptProg/ScriptFlags/Priorities and FileTriggerName/Version/Flags/Index). - RpmHeaderTag: add the file-trigger header tags (5066-5072, 5084). - CreateRpmPackage: add GhostFiles and FileTriggers task inputs. Ghost paths are harvested from the layout (mode/type/link target) and dropped from the payload; a declared ghost path missing from the payload is a hard error. - installer.build.targets: wire the documented LinuxPostRemoveScript to the RPM Postun scriptlet (previously only the undocumented LinuxPostRmScript was wired), keeping LinuxPostRmScript as a compatibility alias and warning when both are set. Flow RpmGhostFile / RpmFileTrigger items into CreateRpmPackage. - README: document RpmGhostFile, RpmFileTrigger, the DebControlFile 'triggers' usage, and the LinuxPostRemoveScript RPM Postun behavior. - Add Microsoft.DotNet.Build.Tasks.Installers.Tests with round-trip coverage for ghost header records + CPIO omission, file-trigger header arrays, input validation, and the missing-ghost error path. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 235e2408-e09f-4748-868f-94768c6ea552
…ntent The Deb/Rpm packaging layout target (_CreateLinuxPackagingLayout) invokes the project's PublishToDisk target with GenerateDeb/GenerateRpm intentionally stripped via RemoveProperties, so a project cannot condition layout content on those properties to emit files only into the native Linux packages (and not into archives, the macOS pkg, or the MSI, which reuse PublishToDisk). Pass an explicit _PublishLinuxPackageContent=true property from the Linux packaging layout's PublishToDisk invocation. Because it is passed explicitly (not a global property), it survives RemoveProperties and is set only for the Deb/Rpm layout, letting a project lay out package-only repair content without affecting other installer formats. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 235e2408-e09f-4748-868f-94768c6ea552
Released dotnet-sdk-10.0 native Linux packages own /usr/share/dotnet/dnx and /usr/bin/dnx, but with different file records than dotnet-host. When dependency resolution pulls in dotnet-host 11 alongside dotnet-sdk-10.0 (for example during a co-install or upgrade), RPM rejects the non-identical file overlap and the installation fails. dotnet-host owns the authoritative, robust dnx dispatcher, so the fix belongs here and must cover both RPM and DEB while preserving supported SDK 10 / SDK 11 side-by-side use. dotnet-host now ships a private, non-conflicting copy of the robust dispatcher at /usr/share/dotnet/dnx.dispatcher (native Linux packages only, gated by _PublishLinuxPackageContent) and uses it as the repair source for the public entries: - Shared scripts: dnx-postinst.sh idempotently restores /usr/share/dotnet/dnx from the private copy and points /usr/bin/dnx at it; dnx-postremove.sh removes the generated public entries only on final host removal. Both are safe for DEB (postinst configure/triggered, postrm remove/purge) and RPM (%post, %postun count) invocation conventions. - DEB: bounded 'Replaces: dotnet-sdk-10.0 (<< 10.1.0)' lets the host take over the dnx files from released SDK 10 without a conflict while never affecting SDK 11, and a dpkg 'interest-noawait' path trigger on both dnx paths re-runs the repair when an SDK is installed/reinstalled after the host and overwrites them. - RPM: the public paths are owned as %ghost entries (recorded in the header but omitted from the payload, since RPM rejects a non-identical dual-owned file) and a %filetriggerin on both paths re-runs the repair when an SDK install/reinstall touches them. macOS, archive, and MSI behavior is unchanged. /usr/bin/dotnet is unchanged. With the host installed, /usr/bin/dnx is a symlink to ../share/dotnet/dnx and /usr/share/dotnet/dnx is the robust host dispatcher, so direct 'dnx' execution works. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 235e2408-e09f-4748-868f-94768c6ea552
…mission Cover the two package-metadata mechanisms the host dnx ownership fix relies on, in a form that runs cross-platform (no Docker required): - CreateRpmPackageTests.PostInAndPostUnScripts_AreEmittedAsRpmScriptletHeaders: proves a Scripts item with Kind=Postun (wired from LinuxPostRemoveScript) produces a real RPMTAG_POSTUN header entry alongside RPMTAG_POSTIN, closing the Postun-compatibility gap for the final-removal cleanup scriptlet. - CreateControlFileTests.ReplacesAdditionalProperty_IsEmittedVerbatim: proves a DebControlProperty named Replaces is written to the DEB control file verbatim, including the bounded version relation "dotnet-sdk-10.0 (<< 10.1.0)". All 10 installer task tests pass. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 235e2408-e09f-4748-868f-94768c6ea552
Use an RPM transaction file trigger so SDK payload extraction cannot overwrite the repaired dnx paths, and validate the generated host package metadata. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: bac0961d-290f-4081-ad4c-92ff0040f82d
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7a5de763-fe9f-41ba-8033-7c6e08e2955b Copilot-Session: 235e2408-e09f-4748-868f-94768c6ea552
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 235e2408-e09f-4748-868f-94768c6ea552
|
Azure Pipelines: Successfully started running 1 pipeline(s). 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Document the package lifecycle and test intent, promote the Linux package publication context to a public property, and remove unnecessary unsafe compilation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 235e2408-e09f-4748-868f-94768c6ea552
Preserve the Linux package dispatcher repair source alongside the Windows dnx.cmd publication added on main. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 235e2408-e09f-4748-868f-94768c6ea552
|
Azure Pipelines: Successfully started running 1 pipeline(s). 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR addresses Linux native package conflicts around the public dnx entry points when upgrading dotnet-host to .NET 11 while keeping released .NET 10 SDK packages installed. It makes dotnet-host authoritative for /usr/bin/dnx and /usr/share/dotnet/dnx (via RPM ghost ownership + triggers, and DEB bounded Replaces + triggers) and validates the behavior with new lifecycle tests.
Changes:
- Extend Arcade RPM packaging to support ghost-owned paths and RPM file/transaction triggers, plus improved post-uninstall script wiring.
- Update
dotnet-hostnative Linux packaging to ship a private dispatcher (dnx.dispatcher) and repair/cleanup the publicdnxentries via postinst/postrm + triggers. - Add installer/lifecycle test coverage to ensure
dnxworks across install/reinstall/remove ordering for RPM (Azure Linux) and DEB (Debian).
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/Microsoft.DotNet.Installer.Tests/LinuxInstallerTests.cs | Adds DEB/RPM dnx lifecycle tests and validates host package metadata (ghost paths, triggers, bounded replaces). |
| test/Microsoft.DotNet.Installer.Tests/assets/dnx-package-lifecycle.sh | Container-side script that exercises install/reinstall/remove ordering and asserts dnx repair/cleanup behavior. |
| src/runtime/src/installer/pkg/sfx/installers/triggers | Registers dpkg path triggers to re-run dnx repair when the public paths change. |
| src/runtime/src/installer/pkg/sfx/installers/dotnet-host.proj | Adds dnx.dispatcher, deb Replaces + triggers, rpm ghost ownership, and transaction trigger registration for dnx repair. |
| src/runtime/src/installer/pkg/sfx/installers/dnx-postinst.sh | Idempotent repair script to restore robust dispatcher and ensure /usr/bin/dnx is a symlink. |
| src/runtime/src/installer/pkg/sfx/installers/dnx-postremove.sh | Final-removal cleanup script to delete generated public dnx entry points. |
| src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmHeaderTag.cs | Adds missing RPM header tag IDs for file triggers and transaction file triggers. |
| src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/RpmBuilder.cs | Implements ghost files and emits file trigger/transaction trigger parallel header arrays. |
| src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/src/CreateRpmPackage.cs | Plumbs ghost file harvesting and file trigger authoring through the RPM creation task. |
| src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/README.md | Documents IsPublishingToLinuxPackage, ghost files, and file trigger support; clarifies postrm/postun wiring. |
| src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/Microsoft.DotNet.Build.Tasks.Installers.csproj | Adds InternalsVisibleTo for the new Installers test assembly. |
| src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers/build/installer.build.targets | Sets IsPublishingToLinuxPackage=true, fixes RPM postun script wiring, and passes ghost files + triggers to RPM creation. |
| src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers.Tests/RpmBuilderTests.cs | Unit tests for ghost file behavior and trigger header emission in RpmBuilder. |
| src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers.Tests/CreateRpmPackageTests.cs | Integration-style tests ensuring CreateRpmPackage harvests ghost files and emits trigger/scriptlet headers. |
| src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers.Tests/CreateControlFileTests.cs | Test for emitting bounded Replaces control properties verbatim. |
| src/arcade/src/Microsoft.DotNet.Build.Tasks.Installers.Tests/Microsoft.DotNet.Build.Tasks.Installers.Tests.csproj | New test project for Installers task functionality. |
| src/arcade/Arcade.slnx | Includes the new Installers tests project in the Arcade solution. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 235e2408-e09f-4748-868f-94768c6ea552
|
Reviewers: this will need to get in for Preview 7 so prioritize this appropriately. |
jkoritzinsky
left a comment
There was a problem hiding this comment.
Some concerns on the MSBuild targets logic. Everything else looks good.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 235e2408-e09f-4748-868f-94768c6ea552
|
/backport to release/11.0.1xx-preview7 |
|
Started backporting to |
|
@mthalman backporting to git am output$ git am --3way --empty=keep --ignore-whitespace --keep-non-patch changes.patch
Applying: Arcade installers: add RPM ghost files, file triggers, and Postun wiring
Applying: Add _PublishLinuxPackageContent gate for Linux-package-only layout content
Applying: Fix dnx file ownership in dotnet-host Linux packages
Using index info to reconstruct a base tree...
M src/runtime/src/installer/pkg/sfx/installers/dotnet-host.proj
Falling back to patching base and 3-way merge...
Auto-merging src/runtime/src/installer/pkg/sfx/installers/dotnet-host.proj
CONFLICT (content): Merge conflict in src/runtime/src/installer/pkg/sfx/installers/dotnet-host.proj
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch=diff' to see the failed patch
hint: When you have resolved this problem, run "git am --continue".
hint: If you prefer to skip this patch, run "git am --skip" instead.
hint: To restore the original branch and stop patching, run "git am --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Patch failed at 0003 Fix dnx file ownership in dotnet-host Linux packages
Error: The process '/usr/bin/git' failed with exit code 128 |
Summary
Released .NET 10 SDK native packages own the public dnx entries, which conflicts when package resolution upgrades the shared dotnet-host package to .NET 11. This makes the shared host authoritative for those entries while preserving side-by-side SDK 10 and SDK 11 use.
Design notes
The RPM package ghost-owns /usr/bin/dnx and /usr/share/dotnet/dnx so it can coexist with released SDK 10 packages without payload conflicts. A transaction file trigger repairs the entries after SDK installation or servicing.
The DEB package uses Replaces: dotnet-sdk-10.0 (<< 10.1.0) to transfer ownership only from affected SDK 10 packages. Its post-install script and path triggers provide the same idempotent repair behavior.
The lifecycle tests accept both the legacy SDK layout and a future SDK package that already uses the repaired layout. The current Azure Linux package exercises the legacy broken direct invocation, while the current Debian package verifies forward-compatible interoperability.
Testing
Note
This pull request description was generated with GitHub Copilot.