Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 56 additions & 15 deletions .github/workflows/build-terminal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,32 @@ jobs:
dotnet-version: 10.0.x

- name: Restore
run: dotnet restore Devolutions.Terminal.slnx -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}
run: dotnet restore Devolutions.Terminal.slnx -p:Configuration=Release -p:EnablePowerShellIse=true -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}

- name: Build
run: dotnet build Devolutions.Terminal.slnx -c Release --no-restore -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}
run: dotnet build Devolutions.Terminal.slnx -c Release --no-restore -p:EnablePowerShellIse=true -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}

- name: Test
run: dotnet test Devolutions.Terminal.slnx -c Release --no-build -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}
# Engine tests resolve the separately installed runtime; it is not a DT package payload.
run: pwsh -NoLogo -NoProfile -File scripts/Install-IsebergTestPowerShell.ps1

- name: Run tests
timeout-minutes: 60
run: >-
dotnet test Devolutions.Terminal.slnx -c Release --no-build -m:1
-p:EnablePowerShellIse=true -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}
--logger "console;verbosity=normal" --logger trx
--results-directory artifacts/test-results/windows
--blame-hang --blame-hang-timeout 5min --blame-hang-dump-type none

- name: Upload Windows test diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: windows-test-diagnostics
path: artifacts/test-results/windows
if-no-files-found: ignore
retention-days: 7

- name: Test macOS legal notice layout
run: pwsh -NoLogo -NoProfile -File scripts/Test-MacOsLegalNotices.ps1
Expand Down Expand Up @@ -212,7 +231,7 @@ jobs:
if-no-files-found: error

native-aot:
name: NativeAOT ${{ matrix.rid }}
name: NativeAOT desktop ${{ matrix.rid }}
runs-on: windows-latest
needs: release-metadata
strategy:
Expand Down Expand Up @@ -241,16 +260,28 @@ jobs:
with:
dotnet-version: 10.0.x

- name: Publish
- name: Publish NativeAOT desktop
run: >
dotnet publish src/Devolutions.Terminal/Devolutions.Terminal.csproj
-c Release
-r ${{ matrix.rid }}
--self-contained
-p:EnablePowerShellIse=false
-p:PublishAot=true
-p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}
-o artifacts/${{ matrix.rid }}

- name: Upload native executable
- name: Check opt-in managed Iseberg desktop
run: >
dotnet publish src/Devolutions.Terminal/Devolutions.Terminal.csproj
-c Release
-r ${{ matrix.rid }}
--self-contained
-p:EnablePowerShellIse=true
-p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}
-o artifacts/iseberg-validation/${{ matrix.rid }}

- name: Upload NativeAOT desktop
uses: actions/upload-artifact@v4
with:
name: DevolutionsTerminal-${{ matrix.rid }}
Expand Down Expand Up @@ -282,10 +313,13 @@ jobs:
dotnet-version: 10.0.x

- name: Restore
run: dotnet restore Devolutions.Terminal.slnx -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}
run: dotnet restore Devolutions.Terminal.slnx -p:Configuration=Release -p:EnablePowerShellIse=true -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}

- name: Run managed tests
run: dotnet test Devolutions.Terminal.slnx -c Release --no-restore -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}
run: pwsh -NoLogo -NoProfile -File scripts/Install-IsebergTestPowerShell.ps1

- name: Test managed projects
run: dotnet test Devolutions.Terminal.slnx -c Release --no-restore -p:EnablePowerShellIse=true -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}

- name: Validate Linux scripts and metadata
run: bash scripts/Test-LinuxPackagingMetadata.sh
Expand Down Expand Up @@ -315,10 +349,13 @@ jobs:
dotnet-version: 10.0.x

- name: Restore
run: dotnet restore Devolutions.Terminal.slnx -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}
run: dotnet restore Devolutions.Terminal.slnx -p:Configuration=Release -p:EnablePowerShellIse=true -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}

- name: Run managed tests
run: dotnet test Devolutions.Terminal.slnx -c Release --no-restore -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}
run: pwsh -NoLogo -NoProfile -File scripts/Install-IsebergTestPowerShell.ps1

- name: Test managed projects
run: dotnet test Devolutions.Terminal.slnx -c Release --no-restore -p:EnablePowerShellIse=true -p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}

- name: Validate macOS scripts and metadata
run: pwsh -NoLogo -NoProfile -File scripts/Test-MacOsPackagingMetadata.ps1
Expand All @@ -333,7 +370,7 @@ jobs:
run: pwsh -NoLogo -NoProfile -File scripts/Test-MacOsNuGetSigning.ps1

macos-native-aot:
name: macOS NativeAOT ${{ matrix.rid }}
name: macOS NativeAOT desktop ${{ matrix.rid }}
runs-on: macos-26
needs:
- macos-managed
Expand Down Expand Up @@ -364,19 +401,21 @@ jobs:
with:
dotnet-version: 10.0.x

- name: NativeAOT publish
- name: NativeAOT desktop publish
run: >
dotnet publish src/Devolutions.Terminal/Devolutions.Terminal.csproj
-c Release
-r ${{ matrix.rid }}
--self-contained true
-p:EnablePowerShellIse=false
-p:PublishAot=true
-p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}
-p:DebugSymbols=false
-p:DebugType=None
-p:NativeDebugSymbols=false
-o artifacts/publish/${{ matrix.rid }}

- name: Upload macOS NativeAOT publish
- name: Upload macOS desktop publish
uses: actions/upload-artifact@v4
with:
name: DevolutionsTerminal-${{ matrix.rid }}
Expand Down Expand Up @@ -649,14 +688,16 @@ jobs:
throw "Runtime hash mismatch: expected ${{ matrix.runtime_sha256 }}, got $actualHash."
}

- name: NativeAOT publish
- name: NativeAOT desktop publish
shell: pwsh
run: |
$publishArgs = @(
"src/Devolutions.Terminal/Devolutions.Terminal.csproj",
"-c", "Release",
"-r", "${{ matrix.rid }}",
"--self-contained", "true",
"-p:EnablePowerShellIse=false",
"-p:PublishAot=true",
"-p:VersionPrefix=${{ needs.release-metadata.outputs.release_version }}",
"-p:DebugSymbols=false",
"-p:DebugType=None",
Expand All @@ -668,7 +709,7 @@ jobs:
}
dotnet publish @publishArgs

- name: Upload Linux NativeAOT publish
- name: Upload Linux desktop publish
uses: actions/upload-artifact@v4
with:
name: DevolutionsTerminal-${{ matrix.rid }}
Expand Down
12 changes: 10 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,22 @@ as a tab on `/pty/{id}` (`ws` on this HTTP host, `wss` when the page is HTTPS).
The browser never supplies a command line, and the host does not send one back.
Elevated profiles are listed and refused.

Warnings are errors. Prefer small, reviewable changes that keep NativeAOT publish green.
Warnings are errors. Keep the default NativeAOT desktop publish green.
Iseberg is disabled by default; its managed developer build and regression suite
require `-p:EnablePowerShellIse=true`. See [Iseberg](docs/iseberg.md) for the
installed-engine boundary.

## NativeAOT
## Desktop publish

```powershell
dotnet publish src/Devolutions.Terminal -c Release -r win-x64 --self-contained
```

The default desktop and release packages use NativeAOT and exclude Iseberg.
Add `-p:EnablePowerShellIse=true` only for an optional managed, untrimmed
Iseberg developer build. Use the same flag for restore/build/test/run commands
when working on Iseberg. The CLI remains NativeAOT in both variants.

Linux packages:

```bash
Expand Down
5 changes: 5 additions & 0 deletions Devolutions.Terminal.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
<Project Path="src/Devolutions.Terminal.Settings.Editor/Devolutions.Terminal.Settings.Editor.csproj" />
<Project Path="src/Devolutions.Terminal/Devolutions.Terminal.csproj" />
</Folder>
<Folder Name="/src/Devolutions.Terminal.Ise/">
<Project Path="src/Devolutions.Terminal.Ise/Iseberg.Core/Iseberg.Core.csproj" />
<Project Path="src/Devolutions.Terminal.Ise/Iseberg.Editor/Iseberg.Editor.csproj" />
<Project Path="src/Devolutions.Terminal.Ise/Iseberg/Iseberg.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/Devolutions.Terminal.Connection.Tests/Devolutions.Terminal.Connection.Tests.csproj" />
<Project Path="tests/Devolutions.Terminal.Control.Tests/Devolutions.Terminal.Control.Tests.csproj" />
Expand Down
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<InvariantGlobalization>false</InvariantGlobalization>
<NuGetAudit>true</NuGetAudit>
<NuGetAuditMode>direct</NuGetAuditMode>
<EnablePowerShellIse Condition="'$(EnablePowerShellIse)' == ''">false</EnablePowerShellIse>
<DevolutionsTerminalWindowsIcon>$(MSBuildThisFileDirectory)assets\icons\terminal.ico</DevolutionsTerminalWindowsIcon>
</PropertyGroup>
</Project>
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
<PackageVersion Include="Avalonia.Headless.XUnit" Version="12.1.1" />
<PackageVersion Include="Avalonia.Skia" Version="12.1.1" />
<PackageVersion Include="AvaloniaUI.DiagnosticsSupport" Version="2.2.3" />
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="12.0.0" />
<PackageVersion Include="Microsoft.PowerShell.SDK" Version="7.6.6" />
<PackageVersion Include="SkiaSharp" Version="3.119.4" />
<PackageVersion Include="SkiaSharp.HarfBuzz" Version="3.119.4" />
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.4" />
Expand Down
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Devolutions Terminal

A cross-platform terminal emulator implemented in C# on **.NET 10**, published
with **NativeAOT**, and rendered with **Avalonia 12** / Skia:
as a self-contained **NativeAOT** desktop app, and rendered with **Avalonia 12** / Skia:

- **ConPTY** on Windows and a real **forkpty** transport on Linux and macOS
- Azure Cloud Shell for remote Azure sessions
- selectable built-in or **Ghostty** VT engine
- Windows Terminal-compatible `settings.json`, actions, keybindings, and `dt` CLI
- optional source-integrated **Iseberg** PowerShell ISE profiles in managed
developer builds only (disabled in default NativeAOT builds and release packages)

This is the [Devolutions Terminal](https://github.com/Devolutions/devolutions-terminal)
source tree. Projects, namespaces, and the GUI host use `Devolutions.Terminal.*`.
Expand All @@ -29,23 +31,29 @@ Install [gsudo](https://gerardog.github.io/gsudo/) and ensure `gsudo.exe` is on
Elevation is only available for local Windows sessions; UAC approval is still
required, and cancelling it does not launch an unelevated shell.

## NativeAOT publish
## Desktop publish

```powershell
dotnet publish src/Devolutions.Terminal -c Release -r win-x64 --self-contained
```

The native executable is written to
The executable is written to
`src/Devolutions.Terminal/bin/Release/net10.0/win-x64/publish/Devolutions.Terminal.exe`.

macOS NativeAOT app bundles (Darwin only):
The default desktop and `dt` CLI are NativeAOT. Release packages exclude Iseberg.
An explicitly opted-in `-p:EnablePowerShellIse=true` developer build uses a
managed, untrimmed desktop and separately installed compatible PowerShell; see
[docs/iseberg.md](docs/iseberg.md). Saved Iseberg profiles remain intact, but
cannot launch in the default build.

macOS app bundles (Darwin only):

```bash
pwsh scripts/Build-MacOsPackage.ps1 osx-arm64 2026.3.0 artifacts/packages
pwsh scripts/Test-MacOsPackage.ps1 osx-arm64 artifacts/packages/*.zip
```

Linux x64 and ARM64 NativeAOT packages are built on Linux with:
Linux x64 and ARM64 packages are built on Linux with:

```bash
scripts/Build-LinuxPackage.sh linux-x64 2026.3.0 artifacts/packages all
Expand Down Expand Up @@ -113,7 +121,8 @@ Devolutions.Terminal.Connection ConPTY + Linux PTY + Azure Cloud Shell + brows
Devolutions.Terminal.Settings Layered Windows Terminal-compatible JSON settings
Devolutions.Terminal.Control Avalonia TermControl renderer
Devolutions.Terminal.App Tabs, title bar, panes, actions, window behavior
Devolutions.Terminal NativeAOT executable and composition root
Devolutions.Terminal.Ise Imported Iseberg engine, editor and embedded workbench
Devolutions.Terminal Desktop executable and composition root
Devolutions.Terminal.Browser Avalonia WebAssembly host (in-process dt-wasm shell)
Devolutions.Terminal.Browser.Host Loopback server for profile tabs and a real PTY
```
Expand Down
Binary file added assets/profile-icons/iseberg.scale-100.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions assets/profile-icons/iseberg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading