Skip to content

Ship dt-pty-host in the Devolutions.Terminal.Control NuGet package - #60

Merged
Marc-André Moreau (mamoreau-devolutions) merged 2 commits into
masterfrom
claude/dt-missing-features-a28685
Oct 24, 2026
Merged

Marc-André Moreau (mamoreau-devolutions) merged 2 commits into
masterfrom
claude/dt-missing-features-a28685

Conversation

@mamoreau-devolutions

Copy link
Copy Markdown
Contributor

Summary

Devolutions.Terminal.Control only packed the Windows ConPTY hosts. Avalonia apps embedding it on Linux or macOS failed at startup with FileNotFoundException: The Unix PTY host is missing., even though PACKAGE.md said one package reference was enough on every platform. This PR packs dt-pty-host for all four Unix RIDs and adds CI that proves a shell actually starts from the packed package.

Changes

Package

  • Devolutions.Terminal.Control.csproj packs runtimes/{linux,osx}-{x64,arm64}/native/dt-pty-host from native/linux-pty/<rid>/.
    • -p:RequireUnixPtyHosts=true fails the pack if any host is missing. Local packs report the missing hosts and continue, because macOS hosts can only be built on macOS.
  • New buildTransitive/Devolutions.Terminal.Control.targets imports the existing ConPtyHost.targets plus a new UnixPtyHost.targets. The new targets run chmod +x on the host after build and publish when building on Linux or macOS.

Runtime (Devolutions.Terminal.Connection)

  • New UnixPtyHost helper. LinuxPtyConnection now looks for the host next to the app first, then under runtimes/<rid>/native/, which is where NuGet puts it for RID-less builds.
  • It restores the execute bit that NuGet drops, so outputs built on Windows and deployed to Linux or macOS also work. If the mode can't be changed, it fails with a clear message.
  • The app's own layout is unchanged: the host next to the executable still wins.

CI (build-terminal.yml)

  • New unix-pty-hosts job: builds all four hosts on one macos-26 runner. macOS hosts use Apple clang; Linux hosts cross-compile with Zig via Build-LinuxPtyHost.ps1.
  • nuget-pack packs with RequireUnixPtyHosts=true, then:
    • checks the package with the new scripts/Test-ControlNuGetPackage.ps1 (required entries, plus ELF/Mach-O format and architecture of each host);
    • starts a ConPTY session from the packed package.
  • New nuget-pack-unix-smoke job (Linux x64, Linux ARM64, macOS ARM64) runs the sample's --pty-smoke from the packed package in three cases: RID-less build, host with the execute bit removed, and RID-specific publish. release now depends on it.

Sample and docs

  • The sample has a headless --pty-smoke mode and launches $SHELL on Unix. Before this, it couldn't open a shell on Linux even with the host present, because ProfileSettings defaults to the Windows PowerShell path on every OS. PACKAGE.md and release.md now say so.
  • PACKAGE.md:
    • adds a platform table (transport and native host per OS);
    • fixes the broken tree/main sample link to tree/master;
    • corrects the MeasureCell(profile, scale) parameter name.
  • release.md and the sample README describe the native hosts, the local pack behaviour and the new CI checks. The sample README also explains how to avoid the NuGet cache serving a stale same-version pack.

This branch also carries a small doc fix: docs/parity-status.md now says signed, notarized macOS DMGs ship from CI, matching docs/macos.md.

Testing

Verified locally on Windows:

  • New UnixPtyHostTests: 7 passed; the 2 execute-bit cases are Unix-only and skipped.
  • The pack contains both Linux hosts (cross-compiled with Zig), and RequireUnixPtyHosts=true fails when the macOS hosts are missing.
  • Test-ControlNuGetPackage.ps1 accepts a correct package and rejects a host built for the wrong architecture. I checked this with Zig-built dummy Mach-O binaries.
  • The sample builds against the packed package; the RID-less output has runtimes/linux-*/native/dt-pty-host; and --pty-smoke starts a ConPTY session.

Not verified locally: running the host on Linux or macOS. The new unix-pty-hosts and nuget-pack-unix-smoke jobs are the first real test there. In particular, cross-compiling the Linux hosts with Zig on the macOS runner has not run yet.

Reviewer notes

  • The .nupkg can't carry Unix file modes (it is packed on Windows, and NuGet doesn't preserve them on extraction anyway). That's why the execute bit is restored both at build time and at runtime.
  • The Linux hosts target glibc 2.31 and macOS 13.0, the same as the desktop app.

🤖 Generated with Claude Code

docs/parity-status.md still listed notarization and DMG as out of scope
for macOS, while docs/macos.md documents signed, notarized .dmg release
artifacts built in CI by scripts/Release-MacOsPackage.ps1. Align the
distribution row and the final UI results bullet with macos.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Control package only carried the Windows ConPTY hosts, so embedding
apps on Linux and macOS failed with "The Unix PTY host is missing."
Pack dt-pty-host for linux-x64, linux-arm64, osx-x64 and osx-arm64 as
runtimes/<rid>/native assets.

LinuxPtyConnection now also probes runtimes/<rid>/native, where NuGet
places the host for RID-less builds, and restores the execute bit that
NuGet drops. A buildTransitive UnixPtyHost.targets marks the host
executable when building on Linux or macOS.

CI builds the four hosts on one macOS runner, packs with
RequireUnixPtyHosts=true, validates the package with
scripts/Test-ControlNuGetPackage.ps1, and runs the sample's new headless
--pty-smoke mode on Windows, Linux x64/ARM64 and macOS ARM64. The sample
and PACKAGE.md now launch $SHELL on Unix, because ProfileSettings
defaults to Windows PowerShell on every OS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
publish-dry-run — 5e248847 Deployed Oct 23, 2026 by mamoreau-devolutions via MSIX packages #374
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant