chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:29:49 +08:00
commit 505bac6b53
1470 changed files with 457757 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

+152
View File
@@ -0,0 +1,152 @@
#!/usr/bin/env bash
# Linux canvas smoke under Xvfb — run on a machine with NO WebKitGTK dev
# package, which makes the build itself the native-only link test.
#
# Exercises the Linux gpu_surface software path without a display server:
# builds examples/ui-inbox (a native-only app, so its GTK host compiles
# with the WebKitGTK stub seam and never links webkitgtk-6.0) with
# -Dplatform=linux -Dweb-engine=system -Dautomation=true, runs it under
# Xvfb, and asserts against the automation snapshot:
#
# 1. the built ELF carries no WebKitGTK reference (no libwebkitgtk
# DT_NEEDED entry, no webkit_/jsc_ dynamic symbol), audited on the
# real binary by tools/audit_web_layer.zig
# 2. snapshot ready=true (app booted, automation server live)
# 3. gpu_backend=software (the software present path is active)
# 4. gpu_nonblank=true (real pixels were presented)
# 5. widget-click "Add task" -> '4 open' (automation input mutates state)
# 6. automate screenshot renders a non-empty PNG
# 7. ZERO WebKit helper processes for the whole run (a native-only app
# has no web layer to boot WebKit with)
#
# Deliberately NOT `set -e` (same as windows-canvas-smoke.sh): grep exits 1
# on zero matches, and under `set -e` an assignment like `x=$(grep ...)` or
# a swallowed `$(cli 2>&1)` capture dies with NO output — this job failed
# three times with nothing in the log but the exit code. Every assertion
# goes through fail(), which dumps the snapshot and the app log.
set -u
# No WebKit sandbox workaround: a native-only app's host is compiled
# without the web layer entirely (and even in web builds the main
# WebView is lazy), so no WebKit helper processes start and the runner's
# user-namespace restrictions never come into play. The zero-WebKit
# assertion below keeps it that way.
# GTK_A11Y=none: under Xvfb there is no session bus providing org.a11y.Bus,
# and GTK4's a11y init blocks ~25 s on the GDBus name lookup before warning
# and continuing — the app's first runtime event landed after the readiness
# window had already expired (reproduced in a local container: without this
# the wait times out at startup; with it the full smoke passes).
# Accessibility is not what this smoke tests.
export GTK_A11Y="${GTK_A11Y:-none}"
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
app_dir="$repo_root/examples/ui-inbox"
snap="$app_dir/.zig-cache/native-sdk-automation/snapshot.txt"
cli="$repo_root/zig-out/bin/native"
app_log="${TMPDIR:-/tmp}/linux-canvas-smoke-app.log"
# Readiness budget. Even with GTK_A11Y=none, shared ubuntu-24.04 runners
# show a consistent ~27 s stall between EGL init and the app's first
# runtime event (measured in runs 28690855597 pass / 28691951139 fail —
# the SAME stall in both; the old hard 30 s `automate wait` window flipped
# green/red on one or two seconds of runner noise). Local containers show
# no stall at all. Widen the budget here instead of weakening the CLI
# default; every correctness assertion stays strict.
ready_timeout_ms=90000
app_pid=""
cleanup() {
[ -n "$app_pid" ] && kill "$app_pid" >/dev/null 2>&1
# xvfb-run does not forward signals to an already-detached app; reap the
# app and its Xvfb directly so local runs exit clean (CI would otherwise
# rely on the runner's orphan sweep).
pkill -f "$app_dir/zig-out/bin/ui-inbox" >/dev/null 2>&1
}
trap cleanup EXIT
diagnostics() {
echo "---- diagnostics ----"
echo "-- snapshot ($snap):"
if [ -f "$snap" ]; then tr '|' '\n' < "$snap" | sed 's/^/ /'; else echo " (missing)"; fi
echo "-- app log head ($app_log):"
head -20 "$app_log" 2>/dev/null | sed 's/^/ /'
echo "-- app log tail ($app_log):"
tail -40 "$app_log" 2>/dev/null | sed 's/^/ /'
echo "---------------------"
}
fail() {
echo "FAIL: $1"
diagnostics
exit 1
}
# Canvas apps must never spawn WebKit: the window's main WebView is
# created lazily and nothing in this app materializes it, so any
# WebKitWebProcess/WebKitNetworkProcess during the run means an eager
# creation regressed (and with it launch latency, resident helper
# processes, and the sandbox trouble this smoke used to work around).
assert_no_webkit() {
local helpers
helpers=$(pgrep -af 'WebKit(Web|Network)Process' 2>/dev/null)
if [ -n "$helpers" ]; then
echo "-- WebKit helper processes found ($1):"
echo "$helpers" | sed 's/^/ /'
fail "canvas app spawned WebKit processes ($1)"
fi
}
# ---- build ----------------------------------------------------------------
(cd "$repo_root" && zig build) || fail "root zig build (CLI) failed"
(cd "$app_dir" && zig build -Dplatform=linux -Dweb-engine=system -Dautomation=true) \
|| fail "ui-inbox Linux build failed (a native-only app must build without the WebKitGTK dev package)"
# ---- 1: the native-only ELF carries no WebKitGTK reference -----------------
(cd "$repo_root" && zig run tools/audit_web_layer.zig -- "$app_dir/zig-out/bin/ui-inbox" absent) \
|| fail "native-only ELF audit failed (the binary references WebKitGTK)"
echo "== native-only ELF audit ok"
# ---- launch ---------------------------------------------------------------
cd "$app_dir" || fail "missing $app_dir"
rm -rf .zig-cache/native-sdk-automation
xvfb-run -a "$app_dir/zig-out/bin/ui-inbox" > "$app_log" 2>&1 &
app_pid=$!
# ---- 2: automation snapshot becomes ready ---------------------------------
# `automate assert` self-reports on timeout (missing patterns + snapshot
# tail) and prints the measured latency on success, so green logs carry
# the readiness margin.
"$cli" automate assert --timeout-ms "$ready_timeout_ms" 'ready=true' \
|| fail "snapshot never became ready"
# ---- 3 + 4: software backend presented non-blank pixels --------------------
"$cli" automate assert --timeout-ms 30000 'gpu_nonblank=true' \
|| fail "gpu_nonblank never became true"
grep -q 'gpu_backend=software' "$snap" || fail "gpu_backend is not software"
echo "== canvas: $(grep -o 'gpu_backend=[a-z]*' "$snap" | head -1)" \
"$(grep -o 'gpu_nonblank=[a-z]*' "$snap" | head -1)"
assert_no_webkit "after first presented frame"
echo "== zero WebKit processes after first presented frame"
# ---- 5: automation widget-click mutates the model --------------------------
echo "== open before click: $(grep -oE '[0-9]+ open' "$snap" | head -1)"
add_id=$(grep -o 'widget @w1/inbox-canvas#[0-9]* role=button name="Add task"' "$snap" \
| grep -o '#[0-9]*' | tr -d '#')
[ -n "$add_id" ] || fail "Add task button not found in snapshot"
"$cli" automate widget-click inbox-canvas "$add_id" || fail "CLI widget-click failed"
"$cli" automate assert --timeout-ms 30000 '4 open' \
|| fail "widget-click did not reach '4 open'"
echo "== open after click: $(grep -oE '[0-9]+ open' "$snap" | head -1)"
# ---- 6: screenshot renders a non-empty PNG ---------------------------------
"$cli" automate screenshot inbox-canvas || fail "CLI screenshot failed"
test -s .zig-cache/native-sdk-automation/screenshot-inbox-canvas.png \
|| fail "screenshot PNG missing or empty"
# ---- 7: still zero WebKit processes at the end of the run -------------------
assert_no_webkit "at end of run"
echo "== zero WebKit processes at end of run"
echo "PASS: linux canvas smoke"
exit 0
+172
View File
@@ -0,0 +1,172 @@
#!/usr/bin/env bash
# Windows canvas smoke under Wine.
#
# Exercises the Windows gpu_surface software path (src/platform/windows/
# webview2_host.cpp: child HWND, WM_TIMER frame events, SetDIBitsToDevice
# blits) without Windows hardware: cross-compiles examples/ui-inbox for
# x86_64-windows-gnu, runs the .exe under Xvfb + Wine, and asserts against
# the automation snapshot:
#
# 1. snapshot ready=true (app booted, automation server live)
# 2. gpu_backend=software (the SetDIBitsToDevice path is active)
# 3. gpu_nonblank=true (real pixels were presented)
# 4. widget-click "Add task" -> '4 open' (automation input mutates state)
# 5. real X11 click + typing lands in the draft textbox (XTEST -> Wine ->
# WM_LBUTTONDOWN/WM_CHAR -> runtime)
#
# Step 5 (xdotool) is deliberately included: it is the only coverage of the
# Win32 pointer/keyboard input mapping in webview2_host.cpp. It is also the
# flakiest step (window lookup, focus without a window manager), so every
# failure path dumps the X window list, the snapshot, and the app log.
#
# Deliberately NOT `set -e`: grep exits 1 on zero matches inside the poll
# loops, and we want explicit, diagnosable failures instead of silent early
# exits. Every assertion goes through fail(), which dumps diagnostics.
set -u
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
app_dir="$repo_root/examples/ui-inbox"
snap="$app_dir/.zig-cache/native-sdk-automation/snapshot.txt"
cli="$repo_root/zig-out/bin/native"
app_log="${TMPDIR:-/tmp}/windows-canvas-smoke-app.log"
# Wine needs an X display; when none is present (CI), re-exec the whole
# script under a private Xvfb server so the app and xdotool share it. The
# explicit screen size beats xvfb-run's 640x480x8 default: the app window is
# 720x520 and Wine wants a 24-bit visual.
if [ -z "${DISPLAY:-}" ]; then
exec xvfb-run -a --server-args="-screen 0 1280x800x24" "$0" "$@"
fi
export WINEPREFIX="${WINEPREFIX:-$repo_root/.zig-cache/wineprefix}"
export WINEDEBUG="${WINEDEBUG:--all}"
app_pid=""
cleanup() {
[ -n "$app_pid" ] && kill "$app_pid" >/dev/null 2>&1
wineserver -k >/dev/null 2>&1
}
trap cleanup EXIT
diagnostics() {
echo "---- diagnostics ----"
echo "-- X windows:"
xdotool search --name "." 2>/dev/null | while read -r w; do
echo " $w: $(xdotool getwindowname "$w" 2>/dev/null)"
done
echo "-- snapshot ($snap):"
if [ -f "$snap" ]; then tr '|' '\n' < "$snap" | sed 's/^/ /'; else echo " (missing)"; fi
echo "-- app log tail ($app_log):"
tail -40 "$app_log" 2>/dev/null | sed 's/^/ /'
echo "---------------------"
}
fail() {
echo "FAIL: $1"
diagnostics
exit 1
}
# poll <seconds> <pattern>: wait until $snap contains <pattern>.
poll() {
local deadline=$((SECONDS + $1))
while [ "$SECONDS" -lt "$deadline" ]; do
[ -f "$snap" ] && grep -q "$2" "$snap" && return 0
sleep 0.5
done
return 1
}
# ---- build ----------------------------------------------------------------
(cd "$repo_root" && zig build) || fail "root zig build (CLI) failed"
(cd "$app_dir" && zig build -Dtarget=x86_64-windows-gnu -Dplatform=windows -Dweb-engine=system -Dautomation=true) \
|| fail "ui-inbox Windows cross-compile failed"
# ---- wineprefix -----------------------------------------------------------
# First run initializes the prefix (measured ~10-30s on CI-class machines);
# subsequent runs are instant, so no cache step is needed.
start=$SECONDS
wineboot --init >/dev/null 2>&1
wineserver --wait >/dev/null 2>&1
echo "== wineprefix ready in $((SECONDS - start))s ($WINEPREFIX)"
# ---- launch ---------------------------------------------------------------
cd "$app_dir" || fail "missing $app_dir"
rm -rf .zig-cache/native-sdk-automation
mkdir -p .zig-cache/native-sdk-automation
wine zig-out/bin/ui-inbox.exe > "$app_log" 2>&1 &
app_pid=$!
# ---- 1: automation snapshot becomes ready ---------------------------------
poll 180 'ready=true' || fail "snapshot never became ready"
echo "== ready: $(head -1 "$snap" | cut -d'|' -f1)"
# ---- 2 + 3: software backend presented non-blank pixels --------------------
poll 60 'gpu_nonblank=true' || fail "gpu_nonblank never became true"
grep -q 'gpu_backend=software' "$snap" || fail "gpu_backend is not software"
echo "== canvas: $(grep -o 'gpu_backend=[a-z]*' "$snap" | head -1)" \
"$(grep -o 'gpu_nonblank=[a-z]*' "$snap" | head -1)" \
"$(grep -o 'gpu_sample=0x[0-9a-f]*' "$snap" | head -1)" \
"$(grep -o 'gpu_present_mode=[a-z]*' "$snap" | head -1)"
# ---- 4: automation widget-click mutates the model --------------------------
echo "== open before click: $(grep -oE '[0-9]+ open' "$snap" | head -1)"
add_id=$(grep -o 'widget @w1/inbox-canvas#[0-9]* role=button name="Add task"' "$snap" \
| grep -o '#[0-9]*' | tr -d '#')
[ -n "$add_id" ] || fail "Add task button not found in snapshot"
"$cli" automate widget-click inbox-canvas "$add_id" || fail "CLI widget-click failed"
poll 30 '4 open' || fail "widget-click did not reach '4 open'"
echo "== open after click: $(grep -oE '[0-9]+ open' "$snap" | head -1)"
# ---- 5: real X11 input through the Win32 path ------------------------------
# Root-coordinate math. Hidden-titlebar windows keep the full overlapped
# frame and reclaim the caption band through WM_NCCALCSIZE, so the Win32
# client area starts at the very top of the window. Under a WM-less Wine
# the X11 driver still places (and reports) the client X window at the
# DEFAULT frame offset - one caption band lower - so the X origin sits a
# band BELOW where Win32 client coordinates actually map, and the reported
# X height is short by exactly that band (measured: X window 718x489 at
# y=30 for a 718x519 client whose clicks land at y=0). The snapshot knows
# the true client height, so the height shortfall IS the y correction; a
# standard-frame window reports matching heights and corrects by zero.
win=""
for w in $(xdotool search --name "." 2>/dev/null); do
case "$(xdotool getwindowname "$w" 2>/dev/null)" in
*[Ii]nbox*) win="$w" ;;
esac
done
[ -n "$win" ] || fail "app X window not found"
eval "$(xdotool getwindowgeometry --shell "$win")"
client_h=$(grep -o 'window @w1 "[^"]*" bounds=([^)]*)' "$snap" | head -1 \
| sed -n 's/.*x\([0-9]*\)[^x]*$/\1/p')
[ -n "$client_h" ] || client_h=$HEIGHT
y_off=$((client_h - HEIGHT))
[ "$y_off" -ge 0 ] 2>/dev/null || y_off=0
echo "== x window $win: pos=($X,$Y) size=${WIDTH}x${HEIGHT} client_h=$client_h y_off=$y_off"
xdotool windowactivate "$win" >/dev/null 2>&1 || xdotool windowfocus "$win" >/dev/null 2>&1
draft_line=$(grep -o 'widget @w1/inbox-canvas#[0-9]* role=textbox[^|]*' "$snap" | head -1)
[ -n "$draft_line" ] || fail "draft textbox not found in snapshot"
bounds=$(echo "$draft_line" | grep -o 'bounds=([^)]*)')
bx=$(echo "$bounds" | sed -n 's/bounds=(\([0-9.]*\),.*/\1/p')
by=$(echo "$bounds" | sed -n 's/bounds=([0-9.]*,\([0-9.]*\) .*/\1/p')
bw=$(echo "$bounds" | sed -n 's/.* \([0-9.]*\)x[0-9.]*).*/\1/p')
bh=$(echo "$bounds" | sed -n 's/.* [0-9.]*x\([0-9.]*\)).*/\1/p')
[ -n "$bx" ] && [ -n "$by" ] && [ -n "$bw" ] && [ -n "$bh" ] || fail "could not parse draft bounds: $draft_line"
cx=$(awk "BEGIN{printf \"%d\", $X + $bx + $bw / 2}")
cy=$(awk "BEGIN{printf \"%d\", $Y - $y_off + $by + $bh / 2}")
echo "== clicking draft field $bounds at ($cx,$cy)"
xdotool mousemove "$cx" "$cy" click 1
# The click must move widget focus into the textbox before any keys are
# sent: spaces in the typed string would otherwise activate whatever
# widget held focus (a button press adds a task and the real failure -
# input landing in the wrong widget - would read as missing text).
poll 10 'role=textbox[^|]*focused=true' || fail "draft textbox did not take focus from the click"
sleep 1
xdotool type --delay 120 "hi from wine"
poll 30 'hi from wine' || fail "typed text never appeared in the snapshot"
echo "== draft after typing: $(grep -o 'widget @w1/inbox-canvas#[0-9]* role=textbox[^|]*' "$snap" | head -1 | cut -c1-160)"
echo "== input latency: $(grep -o 'gpu_input_latency_ns=[0-9]*' "$snap" | head -1)"
echo "PASS: windows canvas smoke"
exit 0
+165
View File
@@ -0,0 +1,165 @@
#!/usr/bin/env bash
# Windows effects smoke under Wine.
#
# Exercises the effect system's live Windows path (src/runtime/effects.zig
# worker threads -> PostMessageW wake in src/platform/windows/
# webview2_host.cpp -> loop-thread drain) without Windows hardware:
# cross-compiles examples/effects-probe for x86_64-windows-gnu, runs the
# .exe under Xvfb + Wine, and asserts against the automation snapshot and
# the app's trace log:
#
# 1. snapshot ready=true (app booted, automation server live)
# 2. gpu_backend=software + nonblank (the canvas presented real pixels)
# 3. widget-click "Start stream" (fx.spawn launches cmd.exe under
# Wine; streamed lines land in the
# model and grow the snapshot)
# 4. app log shows event=effects_wake (the worker's PostMessageW wake was
# marshalled through the message
# loop -- the wake path itself, not
# just the frame-tick drain)
# 5. widget-click "Cancel" (fx.cancel terminates the child;
# status shows "cancelled")
# 6. the line count freezes (no lines arrive after cancel,
# sampled across ~5 more would-be
# line intervals)
#
# Known caveat: the timer present mode also drains effect completions on
# every frame tick (ui_app.zig handleFrame), so line delivery alone cannot
# isolate the wake; that is why step 4 checks the trace log for the wake
# events directly instead of inferring the wake from model updates.
#
# Deliberately NOT `set -e`: grep exits 1 on zero matches inside the poll
# loops, and we want explicit, diagnosable failures instead of silent early
# exits. Every assertion goes through fail(), which dumps diagnostics.
set -u
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
app_dir="$repo_root/examples/effects-probe"
snap="$app_dir/.zig-cache/native-sdk-automation/snapshot.txt"
cli="$repo_root/zig-out/bin/native"
app_log="${TMPDIR:-/tmp}/windows-effects-smoke-app.log"
# Wine needs an X display; when none is present (CI), re-exec the whole
# script under a private Xvfb server. The explicit screen size beats
# xvfb-run's 640x480x8 default: the app window is 560x480 and Wine wants a
# 24-bit visual.
if [ -z "${DISPLAY:-}" ]; then
exec xvfb-run -a --server-args="-screen 0 1280x800x24" "$0" "$@"
fi
export WINEPREFIX="${WINEPREFIX:-$repo_root/.zig-cache/wineprefix}"
export WINEDEBUG="${WINEDEBUG:--all}"
app_pid=""
cleanup() {
[ -n "$app_pid" ] && kill "$app_pid" >/dev/null 2>&1
wineserver -k >/dev/null 2>&1
}
trap cleanup EXIT
diagnostics() {
echo "---- diagnostics ----"
echo "-- snapshot ($snap):"
if [ -f "$snap" ]; then tr '|' '\n' < "$snap" | sed 's/^/ /'; else echo " (missing)"; fi
echo "-- app log tail ($app_log):"
tail -40 "$app_log" 2>/dev/null | sed 's/^/ /'
echo "---------------------"
}
fail() {
echo "FAIL: $1"
diagnostics
exit 1
}
# poll <seconds> <pattern>: wait until $snap contains <pattern>.
poll() {
local deadline=$((SECONDS + $1))
while [ "$SECONDS" -lt "$deadline" ]; do
[ -f "$snap" ] && grep -q "$2" "$snap" && return 0
sleep 0.5
done
return 1
}
# The status bar renders "{N} lines total · {M} dropped"; extract N.
total_lines() {
grep -o '[0-9]* lines total' "$snap" 2>/dev/null | head -1 | grep -o '^[0-9]*'
}
# widget_id <name>: find a widget id in the snapshot by accessible name.
widget_id() {
grep -o "widget @w1/probe-canvas#[0-9]* role=button name=\"$1\"" "$snap" \
| grep -o '#[0-9]*' | tr -d '#'
}
# ---- build ----------------------------------------------------------------
(cd "$repo_root" && zig build) || fail "root zig build (CLI) failed"
# effects-probe is a zero-config app (app.zon + src, no build.zig): the CLI
# synthesizes its build graph. -Doptimize=Debug keeps the smoke binary at
# the debug shape (`native build` alone would inject ReleaseFast).
"$cli" build "$app_dir" -Dtarget=x86_64-windows-gnu -Dplatform=windows -Dweb-engine=system -Dautomation=true -Doptimize=Debug \
|| fail "effects-probe Windows cross-compile failed"
# ---- wineprefix -----------------------------------------------------------
start=$SECONDS
wineboot --init >/dev/null 2>&1
wineserver --wait >/dev/null 2>&1
echo "== wineprefix ready in $((SECONDS - start))s ($WINEPREFIX)"
# ---- launch ---------------------------------------------------------------
cd "$app_dir" || fail "missing $app_dir"
rm -rf .zig-cache/native-sdk-automation
mkdir -p .zig-cache/native-sdk-automation
wine zig-out/bin/effects-probe.exe > "$app_log" 2>&1 &
app_pid=$!
# ---- 1: automation snapshot becomes ready ---------------------------------
poll 180 'ready=true' || fail "snapshot never became ready"
echo "== ready: $(head -1 "$snap" | cut -d'|' -f1)"
# ---- 2: software backend presented non-blank pixels ------------------------
poll 60 'gpu_nonblank=true' || fail "gpu_nonblank never became true"
grep -q 'gpu_backend=software' "$snap" || fail "gpu_backend is not software"
echo "== canvas: $(grep -o 'gpu_backend=[a-z]*' "$snap" | head -1)" \
"$(grep -o 'gpu_nonblank=[a-z]*' "$snap" | head -1)"
grep -q 'idle' "$snap" || fail "probe did not start idle"
# ---- 3: Start spawns the stream and lines arrive ---------------------------
start_id=$(widget_id "Start stream")
[ -n "$start_id" ] || fail "Start stream button not found in snapshot"
"$cli" automate widget-click probe-canvas "$start_id" || fail "CLI widget-click Start failed"
poll 30 'streaming:' || fail "status never showed streaming (spawn failed under Wine?)"
# The Windows stream paces ~1 line/s (cmd for /L + ping); wait for at
# least 2 visible lines so cancel provably interrupts an active stream.
poll 60 'stream line 2' || fail "stream lines never reached the model"
echo "== streaming: $(grep -o 'streaming: [0-9]* lines' "$snap" | head -1), status-bar: $(total_lines) lines total"
# ---- 4: the PostMessage wake path fired ------------------------------------
# handleFrame also drains completions on every frame tick, so lines in the
# model alone cannot isolate the wake. The runner's default -Dtrace=events
# sink prints every runtime event; effects_wake records prove the worker's
# PostMessageW -> kWakeMessage -> kWake -> .effects_wake marshalling ran.
grep -q 'event="effects_wake"' "$app_log" || fail "no effects_wake events in the app log (PostMessage wake never fired)"
echo "== effects_wake events so far: $(grep -c 'event="effects_wake"' "$app_log")"
# ---- 5: Cancel terminates the child ----------------------------------------
cancel_id=$(widget_id "Cancel")
[ -n "$cancel_id" ] || fail "Cancel button not found in snapshot"
"$cli" automate widget-click probe-canvas "$cancel_id" || fail "CLI widget-click Cancel failed"
poll 30 'cancelled: code' || fail "status never showed cancelled"
frozen=$(total_lines)
[ -n "$frozen" ] || fail "could not read line count after cancel"
echo "== cancelled at $frozen lines: $(grep -o 'cancelled: code [0-9-]* after [0-9]* lines' "$snap" | head -1)"
# ---- 6: the line count is frozen -------------------------------------------
# ~5 more lines would have arrived at the ~1s cadence if the child were
# still alive or queued lines were still draining.
sleep 6
after=$(total_lines)
[ "$after" = "$frozen" ] || fail "line count moved after cancel ($frozen -> $after)"
grep -q 'streaming:' "$snap" && fail "status went back to streaming after cancel"
echo "== count frozen at $after lines across 6s"
echo "PASS: windows effects smoke"
exit 0
+133
View File
@@ -0,0 +1,133 @@
name: CEF Runtime
on:
workflow_dispatch:
inputs:
cef_version:
description: "CEF version to package"
required: true
default: "144.0.6+g5f7e671+chromium-144.0.7559.59"
source:
description: "Build from official archive or CEF source"
required: true
type: choice
default: "official"
options:
- official
- source
cef_branch:
description: "Optional CEF branch for source builds"
required: false
default: ""
permissions:
contents: write
jobs:
build:
name: Build ${{ matrix.platform }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- platform: macosarm64
runner: macos-14
- platform: macosx64
runner: macos-15
- platform: linux64
runner: ubuntu-latest
- platform: linuxarm64
runner: ubuntu-24.04-arm
- platform: windows64
runner: windows-2022
# windowsarm64 disabled: Zig 0.16.0 aarch64-windows binary is broken
# https://codeberg.org/ziglang/zig/issues/31865
# - platform: windowsarm64
# runner: windows-11-arm
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- name: Download official CEF
if: ${{ inputs.source == 'official' }}
shell: bash
run: |
set -euo pipefail
version="${{ inputs.cef_version }}"
platform="${{ matrix.platform }}"
archive="cef_binary_${version}_${platform}.tar.bz2"
base="https://cef-builds.spotifycdn.com"
mkdir -p zig-out/cef-download
curl --fail --location --output "zig-out/cef-download/${archive}" "${base}/${archive}"
curl --fail --location --output "zig-out/cef-download/${archive}.sha256" "${base}/${archive}.sha256"
expected="$(tr -d '[:space:]' < "zig-out/cef-download/${archive}.sha256")"
if command -v sha256sum &>/dev/null; then
actual="$(sha256sum "zig-out/cef-download/${archive}" | awk '{print $1}')"
elif command -v shasum &>/dev/null; then
actual="$(shasum -a 256 "zig-out/cef-download/${archive}" | awk '{print $1}')"
else
echo "::error::No SHA-256 hashing utility found"; exit 1
fi
test "$expected" = "$actual"
tar -xjf "zig-out/cef-download/${archive}" -C zig-out/cef-download
echo "CEF_ROOT=zig-out/cef-download/${archive%.tar.bz2}" >> "$GITHUB_ENV"
- name: Build CEF wrapper
if: ${{ inputs.source == 'official' }}
shell: bash
run: |
set -euo pipefail
cmake_args=(-S "$CEF_ROOT" -B "$CEF_ROOT/build/libcef_dll_wrapper")
case "${{ matrix.platform }}" in
*arm64) cmake_args+=(-DPROJECT_ARCH=arm64) ;;
macosx64) cmake_args+=(-DCMAKE_OSX_ARCHITECTURES=x86_64 -DPROJECT_ARCH=x86_64) ;;
esac
cmake "${cmake_args[@]}"
cmake --build "$CEF_ROOT/build/libcef_dll_wrapper" --target libcef_dll_wrapper --config Release
mkdir -p "$CEF_ROOT/libcef_dll_wrapper"
case "${{ matrix.platform }}" in
windows*) wrapper="libcef_dll_wrapper.lib" ;;
*) wrapper="libcef_dll_wrapper.a" ;;
esac
find "$CEF_ROOT/build/libcef_dll_wrapper" -name "$wrapper" -print -quit | xargs -I{} cp "{}" "$CEF_ROOT/libcef_dll_wrapper/$wrapper"
- name: Prepare native-sdk runtime archive
if: ${{ inputs.source == 'official' }}
shell: bash
run: |
zig build
cli="zig-out/bin/native"
if [[ "${{ runner.os }}" == "Windows" ]]; then cli="zig-out/bin/native.exe"; fi
"$cli" cef prepare-release --dir "$CEF_ROOT" --output zig-out/cef --version "${{ inputs.cef_version }}"
- name: Build CEF from source and prepare runtime
if: ${{ inputs.source == 'source' }}
shell: bash
run: |
zig build
chmod +x tools/cef/build-from-source.sh
args=(
tools/cef/build-from-source.sh
--platform "${{ matrix.platform }}"
--version "${{ inputs.cef_version }}"
--output zig-out/cef
--native-sdk-bin zig-out/bin/native
)
if [ -n "${{ inputs.cef_branch }}" ]; then
args+=(--cef-branch "${{ inputs.cef_branch }}")
fi
"${args[@]}"
- name: Publish release asset
uses: softprops/action-gh-release@v2
with:
tag_name: cef-${{ inputs.cef_version }}
name: CEF ${{ inputs.cef_version }}
files: |
zig-out/cef/native-sdk-cef-${{ inputs.cef_version }}-${{ matrix.platform }}.tar.gz
zig-out/cef/native-sdk-cef-${{ inputs.cef_version }}-${{ matrix.platform }}.tar.gz.sha256
+335
View File
@@ -0,0 +1,335 @@
name: CI
on:
pull_request:
push:
branches:
- main
permissions:
contents: read
jobs:
zig:
name: Zig Core
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- uses: actions/setup-node@v4
with:
node-version: 22
# The TypeScript core suites transpile at build/test time; without
# node they skip silently, so CI must provide it.
- run: npm ci --prefix packages/core
- run: zig build test
- run: zig build validate
macos-webview:
name: macOS WebView
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- run: zig build test-webview-system-link
- run: zig build test-webview-smoke
# Signed-package seal pin: an ad-hoc signed package must pass
# codesign --verify --strict (macOS runners are the only tier with
# codesign; the step skips loudly anywhere else).
- run: zig build test-package-signing
# Shared macos-14 runners are far noisier than a dev box (the second
# CI run measured a 576 ms automation-ready against the 500 ms local
# ceiling), so widen the smoke budgets here instead of weakening the
# local defaults. NATIVE_SDK_SMOKE_BUDGET_MS raises the first-frame latency
# budget and the automation-ready ceiling together; every correctness
# assertion in the smokes stays strict.
- run: zig build test-gpu-dashboard-smoke
env:
NATIVE_SDK_SMOKE_BUDGET_MS: "1500"
- run: zig build test-gpu-components-smoke
env:
NATIVE_SDK_SMOKE_BUDGET_MS: "1500"
macos-gpu-perf:
name: macOS GPU Perf
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
# Percentile perf check: 5 cold launches asserting p90
# first-frame latency under NATIVE_SDK_PERF_BUDGET_MS, then 5 steady-state
# widget clicks asserting p90 input latency under NATIVE_SDK_PERF_INPUT_BUDGET_MS.
# Its own job so a shared-runner slowdown is visible in isolation and
# never blocks the correctness smokes.
# Shared macos-14 runners are far noisier than a dev box (first CI run
# measured a 581 ms cold-start outlier against the 300 ms default), so
# widen the budgets here instead of weakening the local defaults: this
# job exists to catch step-function regressions, not runner noise.
- run: zig build test-gpu-dashboard-perf
env:
NATIVE_SDK_PERF_BUDGET_MS: "1500"
NATIVE_SDK_PERF_INPUT_BUDGET_MS: "500"
linux-webkitgtk:
name: Linux WebKitGTK
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- name: Install WebKitGTK dependencies
run: sudo apt-get update && sudo apt-get install -y libgtk-4-dev libwebkitgtk-6.0-dev
- run: zig build test-webview-system-link -Dplatform=linux
# Declare-to-use, proven on real Linux executables WITH the
# WebKitGTK dev package installed — the seam, not the environment:
# the native-only ui-inbox binary must carry no libwebkitgtk
# DT_NEEDED entry and no webkit_/jsc_ dynamic symbol even though
# the headers were right there, while the webview example must
# keep them. (The environment half — building native-only on a
# runner with no WebKitGTK dev package at all — is the
# linux-canvas-smoke job.)
- run: zig build test-linux-web-layer-audit
windows-webview:
name: Windows WebView
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
# Builds the WebView example with the system engine, compiling the
# full embedded-WebView host against the vendored WebView2 SDK
# header (third_party/webview2). The host refuses to fall back to
# the stubbed layer, so a regression — a missing header, a broken
# include path, or a conformance error in the embedded layer — is a
# compile failure here, not a silent WebViewNotFound at runtime.
- run: zig build test-webview-system-link -Dplatform=windows
cef-platform-tooling:
name: CEF Platform Tooling
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- uses: actions/setup-node@v4
with:
node-version: 22
# The TypeScript core suites transpile at build/test time; without
# node they skip silently, so CI must provide it.
- run: npm ci --prefix packages/core
- run: zig build test-tooling
npm-package:
name: npm Package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- run: npm --prefix packages/native-sdk run version:check
- run: npm --prefix packages/native-sdk run scripts:check
native-examples:
name: Native Examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- uses: actions/setup-node@v4
with:
node-version: 22
# The TypeScript examples transpile at build time; the transpiler
# needs its installed dependency.
- run: npm ci --prefix packages/core
- name: Install GTK dependencies
run: sudo apt-get update && sudo apt-get install -y libgtk-4-dev libwebkitgtk-6.0-dev
- run: zig build test-examples-native
# Declare-to-use, proven on real Windows executables: the
# canvas-only ui-inbox cross-compiles without the embedded WebView
# layer (no WebView2Loader.dll reference, no loader installed) and
# the webview example keeps it.
- run: zig build test-windows-web-layer-audit
linux-canvas-smoke:
name: Linux Canvas Smoke
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
# Deliberately NO libwebkitgtk-6.0-dev: ui-inbox declares no web
# use, so its host compiles with the WebKitGTK stub seam and this
# job doubles as the native-only Linux LINK test — the build can
# only succeed if nothing in a native-only app needs the WebKitGTK
# headers or pkg-config entry. (The seam under webkit-PRESENT
# conditions is the linux-webkitgtk job's ELF cross-audit.)
- name: Install GTK and Xvfb
run: sudo apt-get update && sudo apt-get install -y libgtk-4-dev xvfb
# Drives the gpu_surface software path under Xvfb: snapshot ready,
# gpu_backend=software, gpu_nonblank=true, automation widget-click,
# a rendered screenshot, an ELF audit that the built binary carries
# no WebKitGTK reference, and ZERO WebKit helper processes (canvas
# apps never boot WebKit). A11y env, the widened cold-start
# readiness budget (shared runners stall ~27 s before the first
# runtime event), and failure forensics (dump snapshot + app log)
# all live in the script.
- name: Build and drive ui-inbox headless
run: .github/scripts/linux-canvas-smoke.sh
windows-canvas-smoke:
name: Windows Canvas Smoke (Wine)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- name: Install Wine, Xvfb, and xdotool
run: sudo apt-get update && sudo apt-get install -y wine xvfb xdotool
# Cross-compiles ui-inbox for x86_64-windows-gnu and drives the
# gpu_surface software path (child HWND + WM_TIMER + SetDIBitsToDevice)
# under Wine: snapshot ready, gpu_backend=software, gpu_nonblank=true,
# automation widget-click, and real XTEST pointer/keyboard input.
# Wineprefix init happens inline in the script (measured 21s from
# scratch in an ubuntu-24.04 container, so no cache step).
- name: Build and drive ui-inbox.exe under Wine
run: .github/scripts/windows-canvas-smoke.sh
windows-effects-smoke:
name: Windows Effects Smoke (Wine)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- name: Install Wine and Xvfb
run: sudo apt-get update && sudo apt-get install -y wine xvfb
# Cross-compiles examples/effects-probe for x86_64-windows-gnu and
# proves the effect system's live Windows path under Wine: fx.spawn
# launches cmd.exe, streamed lines land in the model, the worker's
# PostMessageW wake shows up as effects_wake events in the trace
# log (frame ticks also drain, so the log is the wake's evidence),
# and fx.cancel terminates the child with the line count frozen.
- name: Build and drive effects-probe.exe under Wine
run: .github/scripts/windows-effects-smoke.sh
frontend-examples:
name: Frontend Examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- run: zig build test-examples-frontends
mobile-examples:
name: Mobile Examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- run: zig build test-examples-mobile
scaffold:
name: Generated App Scaffolds
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- uses: actions/setup-node@v4
with:
node-version: 22
# The default scaffold is a TypeScript core: its build runs the
# @native-sdk/core transpiler from this checkout's own install.
- run: npm ci --prefix packages/core
- run: zig build
- name: Scaffold and test the zero-config native app
run: |
set -euo pipefail
app=".zig-cache/scaffold-native-slim"
rm -rf "$app"
./zig-out/bin/native init "$app"
# Slim scaffold: no build files — the CLI's generated graph drives it.
test ! -f "$app/build.zig"
test ! -f "$app/build.zig.zon"
# The editor surface landed: package.json + tsconfig.json + the
# materialized @native-sdk/core copy stock tsc resolves.
test -f "$app/package.json"
test -f "$app/tsconfig.json"
test -f "$app/node_modules/@native-sdk/core/sdk/core.ts"
# ...and none of it is build truth: every verb works without it.
rm -rf "$app/node_modules"
./zig-out/bin/native test "$app" -Dplatform=null
./zig-out/bin/native check "$app"
# check self-healed the editor copy back into place.
test -f "$app/node_modules/@native-sdk/core/sdk/core.ts"
./zig-out/bin/native eject "$app"
(cd "$app" && zig build test -Dplatform=null)
- name: Scaffold and test frontend templates
run: |
set -euo pipefail
for frontend in native next vite react svelte vue; do
app=".zig-cache/scaffold-${frontend}"
rm -rf "$app"
./zig-out/bin/native init "$app" --frontend "$frontend" --full
(cd "$app" && zig build test -Dplatform=null && ../../zig-out/bin/native validate app.zon)
# Every scaffold ships a CI workflow; parse it as real YAML.
test -s "$app/.github/workflows/ci.yml"
python3 -c 'import sys, yaml; yaml.safe_load(open(sys.argv[1]))' "$app/.github/workflows/ci.yml"
done
evals-typecheck:
name: Evals Typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: pnpm/action-setup@v4
with:
version: 10.23.0
package_json_file: evals/package.json
- run: pnpm install --frozen-lockfile
working-directory: evals
- run: pnpm typecheck
working-directory: evals
docs:
name: Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: pnpm/action-setup@v4
with:
version: 10.23.0
package_json_file: docs/package.json
- run: pnpm install --frozen-lockfile
working-directory: docs
- run: pnpm check
working-directory: docs
+255
View File
@@ -0,0 +1,255 @@
name: Release
on:
push:
branches:
- main
workflow_dispatch:
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
check-release:
name: Check for new version
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
outputs:
should_release: ${{ steps.check.outputs.should_release }}
needs_github_release: ${{ steps.check.outputs.needs_github_release }}
version: ${{ steps.check.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
- name: Compare package.json version to npm and check GitHub release
id: check
run: |
LOCAL_VERSION=$(node -p "require('./packages/native-sdk/package.json').version")
echo "Local version: $LOCAL_VERSION"
NPM_VERSION=$(npm view @native-sdk/cli version 2>/dev/null || echo "0.0.0")
echo "npm version: $NPM_VERSION"
if [ "$LOCAL_VERSION" != "$NPM_VERSION" ]; then
echo "Version changed: $NPM_VERSION -> $LOCAL_VERSION"
echo "should_release=true" >> "$GITHUB_OUTPUT"
echo "needs_github_release=true" >> "$GITHUB_OUTPUT"
else
echo "Version unchanged on npm, skipping publish"
echo "should_release=false" >> "$GITHUB_OUTPUT"
TAG="v$LOCAL_VERSION"
EXISTING_ASSETS=$(gh release view "$TAG" --json assets --jq '.assets[].name' 2>/dev/null || true)
missing=0
for asset in \
CHECKSUMS.txt \
native-sdk-darwin-arm64 \
native-sdk-darwin-x64 \
native-sdk-linux-arm64 \
native-sdk-linux-x64 \
native-sdk-linux-musl-arm64 \
native-sdk-linux-musl-x64 \
native-sdk-win32-arm64.exe \
native-sdk-win32-x64.exe
do
if ! printf '%s\n' "$EXISTING_ASSETS" | grep -Fx "$asset" >/dev/null; then
echo "Missing release asset: $asset"
missing=1
fi
done
if [ "$missing" -eq 0 ]; then
echo "GitHub release $TAG exists with native assets"
echo "needs_github_release=false" >> "$GITHUB_OUTPUT"
else
echo "GitHub release $TAG is missing native assets, will create/update it"
echo "needs_github_release=true" >> "$GITHUB_OUTPUT"
fi
fi
echo "version=$LOCAL_VERSION" >> "$GITHUB_OUTPUT"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
github-release:
name: Create GitHub Release
needs: check-release
if: needs.check-release.outputs.needs_github_release == 'true'
runs-on: macos-14
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Zig
uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- name: Extract changelog entry
run: |
VERSION="${{ needs.check-release.outputs.version }}"
awk '/<!-- release:start -->/{found=1; next} /<!-- release:end -->/{exit} found{print}' CHANGELOG.md > /tmp/release-notes.md
LINES=$(wc -l < /tmp/release-notes.md | tr -d ' ')
if [ "$LINES" -lt 2 ]; then
echo "Error: No release notes found between <!-- release:start --> and <!-- release:end --> markers in CHANGELOG.md"
exit 1
fi
echo "Extracted release notes for $VERSION ($LINES lines)"
- name: Build native release assets (all platforms)
run: |
# Cross-compiles the CLI for all eight platforms and writes the
# flat release assets + CHECKSUMS.txt into zig-out/release/.
bash packages/native-sdk/scripts/build-binaries.sh
- name: Create GitHub Release
run: |
VERSION="${{ needs.check-release.outputs.version }}"
TAG="v$VERSION"
if gh release view "$TAG" &>/dev/null; then
echo "Release $TAG already exists, updating assets"
else
echo "Creating release $TAG..."
gh release create "$TAG" \
--title "$TAG" \
--notes-file /tmp/release-notes.md
fi
gh release upload "$TAG" \
zig-out/release/native-sdk-* \
zig-out/release/CHECKSUMS.txt \
--clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish:
name: Publish CLI to npm
needs: [check-release, github-release]
if: >-
always()
&& needs.check-release.outputs.should_release == 'true'
&& (needs.github-release.result == 'success' || needs.github-release.result == 'skipped')
runs-on: ubuntu-latest
timeout-minutes: 15
environment: Release
permissions:
contents: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
# Publishing uses npm trusted publishing (OIDC): the job's id-token
# permission lets npm mint short-lived credentials, so no npm token
# secret exists anywhere in this repo. All nine packages —
# @native-sdk/cli plus the eight @native-sdk/cli-* platform packages
# under packages/native-sdk/npm/ — must each be configured on
# npmjs.com with a GitHub Actions trusted publisher pointing at
# repository vercel-labs/native, workflow release.yml,
# environment Release. If a package is missing that configuration,
# npm publish fails loudly with an OIDC authentication error before
# anything is uploaded for that package. Trusted publishing requires
# npm >= 11.5.1, satisfied by the npm bundled with Node 24.
# @native-sdk/core (packages/core) joins that publish set the moment
# its "private" flag drops (see the publish step below) — its
# npmjs.com trusted-publisher configuration must exist BEFORE that
# flip lands.
- name: Check version sync
run: npm --prefix packages/native-sdk run version:check
- name: Check package scripts
run: npm --prefix packages/native-sdk run scripts:check
- name: Download release binaries
run: |
VERSION="${{ needs.check-release.outputs.version }}"
mkdir -p /tmp/native-sdk-release
gh release download "v$VERSION" \
--pattern 'native-sdk-*' \
--pattern 'CHECKSUMS.txt' \
--dir /tmp/native-sdk-release
(cd /tmp/native-sdk-release && sha256sum -c CHECKSUMS.txt)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Stage binaries into the platform packages
run: |
# Release-asset name -> npm platform package (same table as
# packages/native-sdk/scripts/build-binaries.sh).
stage() {
asset="$1"; key="$2"; ext="$3"
mkdir -p "packages/native-sdk/npm/$key/bin"
cp "/tmp/native-sdk-release/$asset" "packages/native-sdk/npm/$key/bin/native$ext"
chmod 755 "packages/native-sdk/npm/$key/bin/native$ext"
}
stage native-sdk-darwin-arm64 darwin-arm64 ""
stage native-sdk-darwin-x64 darwin-x64 ""
stage native-sdk-linux-arm64 linux-arm64-gnu ""
stage native-sdk-linux-x64 linux-x64-gnu ""
stage native-sdk-linux-musl-arm64 linux-arm64-musl ""
stage native-sdk-linux-musl-x64 linux-x64-musl ""
stage native-sdk-win32-arm64.exe win32-arm64 ".exe"
stage native-sdk-win32-x64.exe win32-x64 ".exe"
- name: Publish to npm
run: |
VERSION="${{ needs.check-release.outputs.version }}"
# Platform packages first, so the main package's
# optionalDependencies pins are resolvable the moment it lands.
# Re-runs skip anything already on the registry at this version.
publish_dir() {
dir="$1"
name=$(node -p "require('./$dir/package.json').name")
if npm view "$name@$VERSION" version >/dev/null 2>&1; then
echo "$name@$VERSION already published, skipping"
return 0
fi
(cd "$dir" && npm publish --provenance --access public)
}
for dir in packages/native-sdk/npm/*/; do
publish_dir "${dir%/}"
done
# @native-sdk/core (packages/core) rides the same version and
# release gate as the CLI (sync-version.js stamps it; the
# version check above refuses a half-bumped tree).
#
# ============================ LOUD FLIP REQUIREMENT ==========
# packages/core/package.json keeps "private": true until the
# 0.5.0 cut BY DESIGN, and that field IS the publish switch:
# while private, the guard below skips the package (npm would
# refuse to publish it anyway); the 0.5.0 change that drops
# "private" makes this same step start publishing it with no
# workflow edit. Before dropping the flag, configure the
# @native-sdk/core trusted publisher on npmjs.com (see the note
# above) or the first public release fails here.
# =============================================================
if [ "$(node -p "require('./packages/core/package.json').private === true")" = "true" ]; then
echo "@native-sdk/core is private (pre-0.5.0): skipping publish by design"
else
publish_dir packages/core
fi
publish_dir packages/native-sdk