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
79 changes: 79 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: CI

on:
pull_request:
push:
branches:
- main

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
verify:
name: verify (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- name: Install Native SDK CLI
run: npm install -g @native-sdk/cli@0.4.1
- name: Doctor (strict, macOS)
if: runner.os == 'macOS'
run: native doctor --strict
- name: Doctor (Linux)
if: runner.os == 'Linux'
run: native doctor
- name: Validate manifest
run: native validate app.zon
- name: Logic + view tests (headless null platform)
run: native test -Dplatform=null
- name: Check markup + manifest
run: native check --strict
- name: Build release binary (macOS)
if: runner.os == 'macOS'
run: native build

smoke:
name: automation smoke (Linux · Xvfb)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- name: Install GTK + Xvfb
run: sudo apt-get update && sudo apt-get install -y libgtk-4-dev libwebkitgtk-6.0-dev xvfb dbus
- name: Allow unprivileged user namespaces (bwrap dbus-proxy sandbox)
# Ubuntu 24.04 runners restrict unprivileged userns via AppArmor,
# which breaks the SDK's bubblewrap-sandboxed dbus-proxy.
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- name: Install Native SDK CLI
run: npm install -g @native-sdk/cli@0.4.1
- name: Build headless app (automation enabled)
run: native build -Dplatform=linux -Dautomation=true
- name: Drive the app headless and assert the widget tree
run: ./scripts/smoke.sh
- name: Upload rendered screenshot
if: always()
uses: actions/upload-artifact@v4
with:
name: smoke-screenshot
path: .zig-cache/native-sdk-automation/screenshot-main-canvas.png
if-no-files-found: ignore
64 changes: 64 additions & 0 deletions scripts/smoke.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
#
# Automation smoke test: launch the app, wait for its embedded automation
# server, assert the widget tree actually rendered, and capture a
# deterministic screenshot artifact. Portable — uses a virtual framebuffer
# (Xvfb) when available (CI), otherwise runs the window directly (local
# macOS). The app must be built with -Dautomation=true.
#
set -euo pipefail

BIN=zig-out/bin/studio
AUTO_DIR=.zig-cache/native-sdk-automation

if [ ! -x "$BIN" ]; then
echo "smoke: $BIN not found — build first: native build -Dautomation=true" >&2
exit 1
fi

# Start from a clean automation dir so 'wait' only sees this run.
rm -rf "$AUTO_DIR"

# Headless GTK needs no accessibility bus; without this the app aborts on a
# missing a11y DBus service. A private session bus (dbus-run-session) then
# satisfies the runtime's DBus handshake. Both are no-ops on macOS.
export GTK_A11Y=none
export NO_AT_BRIDGE=1

launch() {
if command -v xvfb-run >/dev/null 2>&1; then
if command -v dbus-run-session >/dev/null 2>&1; then
dbus-run-session -- xvfb-run -a "$BIN"
else
xvfb-run -a "$BIN"
fi
else
"$BIN"
fi
}

launch &
APP_PID=$!
trap 'kill "$APP_PID" >/dev/null 2>&1 || true' EXIT

# Block until the runtime publishes ready=true (or fail loudly on timeout).
native automate wait --timeout-ms 60000

# The window must exist with the expected widget tree. These come from the
# semantics snapshot (GPU-independent), so they hold under software GL too.
native automate assert --timeout-ms 30000 \
'ready=true' \
'role=button name="Reset"' \
'role=button name="\+"' \
'role=button name="-"' \
'OCPP DebugKit Studio' \
'count: 0'

# No runtime/dispatch errors surfaced during startup.
native automate assert --absent 'error event='

# A real-pixel capture must land as a non-empty PNG.
native automate screenshot main-canvas
test -s "$AUTO_DIR/screenshot-main-canvas.png"

echo "smoke: ok — window rendered and automation drove the widget tree"
Loading