Files
opencode/.github/workflows/check-process-daemon.yml

222 lines
10 KiB
YAML

name: Process daemon
on:
pull_request:
paths:
- packages/process-daemon/**
- .github/workflows/check-process-daemon.yml
push:
branches: [dev]
paths:
- packages/process-daemon/**
- .github/workflows/check-process-daemon.yml
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
defaults:
run:
working-directory: packages/process-daemon
jobs:
miri:
name: Miri (callback ownership)
runs-on: ubuntu-22.04
timeout-minutes: 15
env:
CARGO_TARGET_DIR: target/miri
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install Miri
run: rustup toolchain install nightly-2026-09-02 --profile minimal --component miri --component rust-src
- run: cargo +nightly-2026-09-02 miri setup
- name: Check Stacked Borrows
run: |
cargo +nightly-2026-09-02 miri test --locked --manifest-path tests/ghostty-effects/Cargo.toml
cargo +nightly-2026-09-02 miri test --locked --manifest-path tests/transport-retained/Cargo.toml
- name: Check Tree Borrows
env:
MIRIFLAGS: -Zmiri-tree-borrows
run: |
cargo +nightly-2026-09-02 miri test --locked --manifest-path tests/ghostty-effects/Cargo.toml
cargo +nightly-2026-09-02 miri test --locked --manifest-path tests/transport-retained/Cargo.toml
test:
name: Core (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, macos-15]
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: mlugg/setup-zig@8d6198c65fb0feaa111df26e6b467fea8345e46f # v2.0.5
with:
version: 0.16.0
- name: Install Rust
run: rustup toolchain install 1.90.0 --profile minimal --component rustfmt --component clippy
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
key: core-${{ matrix.os }}-zig-0.16.0
workspaces: packages/process-daemon -> target
cache-workspace-crates: true
- run: cargo fmt --check
- run: cargo clippy --locked --all-targets --all-features -- -D warnings
- name: Test Linux
if: runner.os == 'Linux'
run: cargo test --locked
# Match the release workflow's macOS playground exclusion.
- name: Test macOS
if: runner.os == 'macOS'
run: cargo test --locked --lib --test service --test runtime --test ownership --test pipe --test rows --test ghostty-effects
windows:
name: Windows (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- arch: x64
runner: windows-2025
target: x86_64-pc-windows-msvc
machine: "8664"
- arch: arm64
runner: windows-11-arm
target: aarch64-pc-windows-msvc
machine: "aa64"
defaults:
run:
shell: pwsh
working-directory: packages/process-daemon
env:
CARGO_TERM_COLOR: always
CARGO_BUILD_TARGET: ${{ matrix.target }}
RUSTUP_TOOLCHAIN: 1.90.0-x86_64-pc-windows-msvc
RUST_BACKTRACE: "1"
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Enable long dependency paths
run: git config --global core.longpaths true
- uses: mlugg/setup-zig@8d6198c65fb0feaa111df26e6b467fea8345e46f # v2.0.5
if: matrix.arch == 'x64'
with:
version: 0.16.0
cache-key: ${{ matrix.target }}
# Zig 0.16 crashes when building Ghostty on the native ARM64 host.
# Use x64-hosted compilers on this ARM64 runner, but execute ARM64 tests natively.
- name: Install x64 Zig for ARM64 cross-compilation
if: matrix.arch == 'arm64'
run: |
$archive = Join-Path $env:RUNNER_TEMP 'zig-x64.zip'
Invoke-WebRequest 'https://ziglang.org/download/0.16.0/zig-x86_64-windows-0.16.0.zip' -OutFile $archive
if ((Get-FileHash $archive -Algorithm SHA256).Hash -ne '68659eb5f1e4eb1437a722f1dd889c5a322c9954607f5edcf337bc3684a75a7e') { throw 'Zig archive checksum mismatch' }
Expand-Archive $archive -DestinationPath $env:RUNNER_TEMP
Join-Path $env:RUNNER_TEMP 'zig-x86_64-windows-0.16.0' >> $env:GITHUB_PATH
- name: Fix Zig 0.16 ARM64 function-pointer alignment
if: matrix.arch == 'arm64'
run: |
# ARM64 function pointers require 4-byte alignment, unlike *anyopaque.
# Zig 0.16's Windows stack-trace helper omits the required alignment assertion.
$file = Join-Path $env:RUNNER_TEMP 'zig-x86_64-windows-0.16.0/lib/std/debug/SelfInfo/Windows.zig'
$source = [System.IO.File]::ReadAllText($file)
$old = '@ptrCast(try si.loadNtdllProc(@tagName(proc)))'
if (!$source.Contains($old)) { throw 'Zig alignment workaround no longer matches; re-evaluate it' }
[System.IO.File]::WriteAllText($file, $source.Replace($old, '@ptrCast(@alignCast(try si.loadNtdllProc(@tagName(proc))))'))
- name: Install Rust toolchain and target
run: rustup toolchain install $env:RUSTUP_TOOLCHAIN --force-non-host --profile minimal --component rustfmt --component clippy --target $env:CARGO_BUILD_TARGET
- name: Verify runner and compiler architecture
env:
EXPECTED_ARCH: ${{ matrix.arch }}
run: |
$arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
Write-Output "Windows architecture: $arch"
if ($arch -ine $env:EXPECTED_ARCH) { throw "Expected native $env:EXPECTED_ARCH runner, got $arch" }
$compiler = rustc -vV
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$compiler
if ($compiler -notcontains 'host: x86_64-pc-windows-msvc') { throw 'Unexpected Rust compiler host' }
Write-Output "Executable target: $env:CARGO_BUILD_TARGET"
zig version
# No cache-on-failure: a timed-out run once saved a partial cache that broke re-runs.
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
with:
key: windows-${{ matrix.target }}-zig-0.16.0
workspaces: packages/process-daemon -> target
# The native Ghostty build is now an output of the workspace crate.
cache-workspace-crates: true
- name: Check formatting
run: cargo fmt --check
- name: Check Clippy
run: cargo clippy --locked --all-targets --all-features -- -D warnings
- name: Build executable
id: build
run: cargo build --locked
- name: Run tests
id: tests
timeout-minutes: 10
run: cargo test --locked --all-targets --all-features
- name: Run standalone library tests
env:
EXPECTED_MACHINE: ${{ matrix.machine }}
run: |
$artifacts = cargo test --locked --lib --no-run --message-format=json | ForEach-Object { $_ | ConvertFrom-Json }
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$binary = ($artifacts | Where-Object { $_.reason -eq 'compiler-artifact' -and $_.profile.test -and $_.executable }).executable
# Cargo adds native library paths to PATH, which can hide accidental DLL dependencies.
# Copy only the executable so neither Cargo nor adjacent build artifacts can supply them.
$directory = Join-Path $env:RUNNER_TEMP 'pty-standalone-tests'
New-Item -ItemType Directory -Force $directory | Out-Null
$standalone = Join-Path $directory 'opencode-pty-tests.exe'
Copy-Item $binary $standalone
$bytes = [System.IO.File]::ReadAllBytes($standalone)
$header = [BitConverter]::ToInt32($bytes, 0x3c)
$machine = [BitConverter]::ToUInt16($bytes, $header + 4)
if ($machine -ne [Convert]::ToUInt16($env:EXPECTED_MACHINE, 16)) { throw 'Test executable architecture does not match the runner' }
& $standalone
- name: Diagnose failed library tests
if: failure() && steps.tests.outcome == 'failure'
timeout-minutes: 10
run: cargo test --locked --lib --all-features -- --test-threads=1 --nocapture
- name: Diagnose failed runtime tests
if: failure() && steps.tests.outcome == 'failure'
timeout-minutes: 3
run: cargo test --locked --test runtime -- --test-threads=1 --nocapture
- name: Diagnose failed daemon tests
if: failure() && steps.tests.outcome == 'failure'
timeout-minutes: 3
run: cargo test --locked --test windows-daemon -- --test-threads=1 --nocapture
- name: Smoke test executable
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
run: |
$metadata = cargo metadata --locked --no-deps --format-version 1 | ConvertFrom-Json
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$version = ($metadata.packages | Where-Object name -eq 'opencode-pty').version
$binary = Join-Path $metadata.target_directory "$env:CARGO_BUILD_TARGET/debug/opencode-pty.exe"
$output = & $binary --version
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Write-Output $output
if ($output -notmatch "^opencode-pty $([regex]::Escape($version)) \(protocol \d+\)$") { throw "Unexpected executable version: $output" }
- name: Document current coverage
if: always()
run: |
@"
## Windows ${{ matrix.arch }}
Target: $env:CARGO_BUILD_TARGET
This job builds the executable and runs all enabled tests natively, including libghostty parser, protocol, direct TerminalService ConPTY runtime, named-pipe transport, and daemon tests.
The runtime suite spawns real Rust console children and checks input/output, Unicode, cwd/environment/argv/PATH, OS resize, snapshots, replay, terminal replies, and independent terminals.
Daemon tests cover private atomic registration, ownership/handoff, stale registration/locking, partial requests, blocked subscribers, and live ConPTY operations over real named pipes.
The older service, ownership, playground, and rows integration suites remain Unix-only.
Basic tests do not establish exhaustive lifecycle coverage or Windows interactive CLI parity.
No packages or releases are published.
"@ >> $env:GITHUB_STEP_SUMMARY