Skip to content

feat: optional ILoggerFactory tracing; macos-26 CI #3

feat: optional ILoggerFactory tracing; macos-26 CI

feat: optional ILoggerFactory tracing; macos-26 CI #3

Workflow file for this run

name: build
on:
workflow_dispatch:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
release:
types: [published]
env:
NuGetDirectory: ${{ github.workspace }}/nuget
jobs:
build_and_test:
name: Build, Test, and Pack
# macos-26 is the image that ships the Xcode 26.x toolchain the net11.0-macos workload expects (macos-latest
# only carries Xcode 16.x). Matches the StreamWeaver native-head build.
runs-on: macos-26
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0 # MinVer needs full history
submodules: recursive # Syphon framework source for the native shim
# Select Xcode 26.5: the net11.0-macos preview workload's Microsoft.macOS links against the macOS 26.5 SDK,
# and the managed linker hard-fails (MM0179) on anything older. macos-26 ships 26.5 as default; pin it so a
# future default bump can't regress the toolchain. The deployment target stays at 15.0.
- name: Select Xcode 26.5
run: sudo xcode-select -s /Applications/Xcode_26.5.app/Contents/Developer && xcodebuild -version
- uses: actions/setup-dotnet@v5
with:
# net11.0 needs the .NET 11 preview SDK alongside .NET 10; global.json selects it.
dotnet-version: |
10.0.x
11.0.x
# The net11.0-macos TFM requires the macos workload; the preview SDK no longer bundles it, so install
# it explicitly before restore (NETSDK1147 otherwise).
- name: Install macos workload
run: dotnet workload install macos
- name: Build native shim (universal arm64 + x86_64)
run: bash native/build-native.sh
- name: Restore
run: dotnet restore Syphon.NET.slnx
- name: Build
run: dotnet build Syphon.NET.slnx --no-restore -c Release
- name: Test (value-type)
run: dotnet test Syphon.NET.slnx --no-build -c Release
--filter "TestCategory!=Transport&TestCategory!=Directory"
- name: Test (frame transport, byte-exact)
# Gating: a server publishes and a loopback client receives byte-exact, with no dependency
# on the distributed-notification directory. This works on headless runners with a Metal
# device and is the authoritative end-to-end read+write check.
run: dotnet test Syphon.NET.slnx --no-build -c Release
--filter "TestCategory=Transport"
- name: Test (directory discovery)
# Gating: discovery works on any Metal-capable host (incl. headless CI) once the run loop
# is pumped via SyphonServerDirectory.PumpEvents.
run: dotnet test Syphon.NET.slnx --no-build -c Release
--filter "TestCategory=Directory"
- name: Native probe (Metal + loopback transport + directory)
# Diagnostic: prints which layers actually work on the runner (Metal device,
# byte-exact frame transport via the loopback client, distributed-notification directory).
# Non-gating while we learn what a headless runner supports.
continue-on-error: true
run: dotnet run --project samples/Syphon.NET.Peer -c Release --no-build
- name: Cross-process transport (separate processes)
# Gating: spawns a separate server process and connects via its exported description (no
# directory), proving frames cross the real mach/IOSurface boundary byte-exact.
run: dotnet run --project samples/Syphon.NET.Peer -c Release --no-build -- crosstest
- uses: actions/setup-python@v5
with:
python-version: "3.12" # syphon-python ships wheels for cp38-cp312 only
- name: Install syphon-python (independent foreign peer)
run: |
python3 -m venv /tmp/sp
/tmp/sp/bin/pip install --quiet --upgrade pip
/tmp/sp/bin/pip install --quiet syphon-python numpy pyobjc-framework-Cocoa
- name: Foreign interop A (syphon-python server -> Syphon.NET client)
# Gating: an independent Syphon implementation publishes; we discover it via the directory
# and receive its frames byte-exact. This is the real cross-implementation receive case.
run: |
/tmp/sp/bin/python tests/interop/syphon_peer.py server --name PyForeign --w 64 --h 64 --seconds 45 &
PY=$!
sleep 4
dotnet run --project samples/Syphon.NET.Peer -c Release --no-build -- foreign-client --name PyForeign --timeout 25
RC=$?
kill $PY 2>/dev/null || true
exit $RC
- name: Foreign interop B (Syphon.NET server -> syphon-python client)
# Gating: a foreign client receives our published frames byte-exact. Connects via our
# exported description (syphon-python's directory wrapper crashes on an iconless server).
run: |
dotnet run --project samples/Syphon.NET.Peer -c Release --no-build -- server --name DotNetForeign --w 64 --h 64 --seconds 45 --desc-file /tmp/dn_desc.bin --ready-file /tmp/dn_ready &
CS=$!
for i in $(seq 1 50); do [ -f /tmp/dn_ready ] && break; sleep 0.2; done
/tmp/sp/bin/python tests/interop/syphon_peer.py client --name DotNetForeign --desc-file /tmp/dn_desc.bin --w 64 --h 64 --timeout 25
RC=$?
kill $CS 2>/dev/null || true
exit $RC
- name: Pack
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v')
run: dotnet pack src/Syphon.NET/Syphon.NET.csproj --no-build -c Release -o ${{ env.NuGetDirectory }}
- name: Upload package artifact
if: github.event_name == 'release' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v')
uses: actions/upload-artifact@v7
with:
name: nuget-package
if-no-files-found: error
retention-days: 1
path: ${{ env.NuGetDirectory }}/*.nupkg
publish_nuget:
name: Publish NuGet Packages
needs: [build_and_test]
# Publishing runs only when a GitHub release is published (or manually dispatched), matching
# the other Agash packages. Requires a NuGet.org trusted-publishing policy for this package.
if: (github.event_name == 'release' && github.event.action == 'published') || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest # the package is platform-agnostic; no macOS needed to push it
permissions:
contents: read
id-token: write # OIDC for NuGet.org trusted publishing
steps:
- name: Download package artifact
uses: actions/download-artifact@v8
with:
name: nuget-package
path: ${{ env.NuGetDirectory }}
- uses: actions/setup-dotnet@v5
- name: NuGet login (OIDC)
id: login
uses: NuGet/login@v1
with:
user: Agash
- name: Publish packages
run: |
for f in "${{ env.NuGetDirectory }}"/*.nupkg; do
echo "Pushing $f"
dotnet nuget push "$f" \
--api-key "${{ steps.login.outputs.NUGET_API_KEY }}" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate
done