-
Notifications
You must be signed in to change notification settings - Fork 11
200 lines (195 loc) · 9.33 KB
/
Copy pathtypescript-ci.yml
File metadata and controls
200 lines (195 loc) · 9.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
name: typescript-ci
# Every-PR gates for the TypeScript/Node binding (npm package transcribe-cpp).
# Thin per-binding workflow on the shared rails: the binding-agnostic C
# contracts are certified in native-ci.yml; this file adds only what the koffi
# FFI layer introduces.
#
# - ts-gates: the generated-FFI drift gate (generate.py --check, which covers
# _generated.py + transcribe.abihash + the TS _generated.ts),
# version-sync, and the TypeScript typecheck (tsc). No native
# build — fast, runs everywhere including forks.
# - ts-build: build a shared libtranscribe, then run the node:test suite and
# the 5 canonical examples against it on linux, macos, and windows.
#
# Two test tiers (requirements §4): the no-model tests (version/ABI/error
# mapping/device discovery) always run, including on forks. The model-gated
# tests (transcription, streaming, cancel, family extensions) un-skip only when
# the canary GGUFs are fetched — same fetch-canary action + HF_TOKEN gate the
# other bindings use — and skip cleanly otherwise.
#
# Scope: Node only for v1 (koffi is N-API-based; Bun/Deno are a documented
# follow-up). Path filters follow native-ci.yml's shape: the binding's own tree
# plus the native paths it loads from. Branch protection is OFF (project
# decision, 2026-06-13).
on:
push:
branches: [main]
paths: &paths
- "bindings/typescript/**"
- "src/**"
- "include/**"
- "ggml/**"
- "cmake/**"
- "CMakeLists.txt"
- "CMakePresets.json"
- "bindings/python/_generate/generate.py"
- "bindings/python/_generate/check_version_sync.py"
- ".github/workflows/typescript-ci.yml"
- ".github/actions/**"
pull_request:
paths: *paths
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
ts-gates:
# Cheap, native-build-free gates. generate.py --check needs libclang; users
# never do, because the FFI surface (_generated.ts) is committed.
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v8.2.0 # for the python gate scripts
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Install libclang (FFI generator)
run: sudo apt-get update && sudo apt-get install -y libclang-dev clang
- name: FFI drift gate (_generated.py + transcribe.abihash + _generated.ts)
run: uv run --no-project --with 'libclang==18.1.1' bindings/python/_generate/generate.py --check
- name: Version sync (header <-> every active manifest)
run: uv run --no-project python bindings/python/_generate/check_version_sync.py
- name: Install + typecheck
working-directory: bindings/typescript
run: |
npm install --no-audit --no-fund
npm run build
ts-build:
name: ts-build (${{ matrix.label }})
strategy:
fail-fast: false
matrix:
include:
- label: linux-x64
runner: blacksmith-2vcpu-ubuntu-2404
lib: libtranscribe.so
lib_subdir: src # .so is a LIBRARY artifact (build-shared/src)
cmake_flags: ""
# macOS arm64 on owned hardware; Metal on, embedded library (matches
# native-ci / the shipped macOS wheel posture).
- label: macos-arm64
runner: [self-hosted, macOS, ARM64]
lib: libtranscribe.dylib
lib_subdir: src
cmake_flags: "-DGGML_METAL=ON -DGGML_METAL_EMBED_LIBRARY=ON"
# Windows / MSVC (Blacksmith windows-2025, the wheel + rust-ci image).
# The first koffi load of this tree on Windows: it proves transcribe.dll
# and its co-located ggml DLLs resolve. koffi's first LoadLibraryW fails
# to find the sibling DLLs (Windows does not search a DLL's own dir for
# its deps), then retries with LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR, which
# does — so the package-local layout the loader/pack-platform ship is
# exactly what this exercises. transcribe.dll is a RUNTIME artifact
# (build-shared/bin, not src). MSVC + Ninja mirror rust-ci's
# Windows leg; no ccache there, so it's a cold ggml build every run.
- label: windows-x64
runner: blacksmith-2vcpu-windows-2025
lib: transcribe.dll
lib_subdir: bin # DLL is a RUNTIME artifact (build-shared/bin)
cmake_flags: ""
runs-on: ${{ matrix.runner }}
timeout-minutes: 60 # headroom for the cold MSVC ggml build on Windows
env:
# Gates the model-test tier: present on this repo's branches, absent on
# forks (where fetch-canary skips and the model tests skip cleanly).
HF_TOKEN: ${{ secrets.HF_TOKEN }}
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: denoland/setup-deno@v2
if: runner.os != 'Windows' # the Deno smoke below is non-Windows only
with:
deno-version: v2.x
- uses: astral-sh/setup-uv@v8.2.0 # fetch-canary fetches via uvx
- name: Install build deps (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y cmake ninja-build ccache
# Windows toolchain mirrors rust-ci's Windows leg: cl.exe ambient for the
# Ninja generator (else CMake silently falls back to the image's MinGW gcc).
- name: Set up MSVC (cl.exe on PATH for the Ninja generator)
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Install build deps (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
choco install ninja --no-progress -y
- name: Enable ccache launcher
if: runner.os == 'Linux'
run: |
echo "CMAKE_C_COMPILER_LAUNCHER=ccache" >> "$GITHUB_ENV"
echo "CMAKE_CXX_COMPILER_LAUNCHER=ccache" >> "$GITHUB_ENV"
- name: CPU ISA signature (ccache key)
if: runner.os == 'Linux'
run: echo "CPU_SIG=$(grep -m1 '^flags' /proc/cpuinfo | sha256sum | cut -c1-8)" >> "$GITHUB_ENV"
- name: ccache (native source build)
if: runner.os == 'Linux'
uses: actions/cache@v5
with:
path: ~/.cache/ccache
key: ccache-ts-build-${{ matrix.label }}-${{ env.CPU_SIG }}-${{ github.sha }}
restore-keys: ccache-ts-build-${{ matrix.label }}-${{ env.CPU_SIG }}-
- name: Build shared libtranscribe
# bash on every OS (Git Bash on the Windows runner) so the body and the
# GITHUB_ENV append are identical. $GITHUB_WORKSPACE, not $PWD: on Windows
# bash, $PWD is an MSYS /d/... path koffi/node can't open; the workspace
# var is a native path. lib_subdir selects bin (Windows RUNTIME) vs src.
shell: bash
run: |
cmake -B build-shared -G Ninja -DTRANSCRIBE_BUILD_SHARED=ON ${{ matrix.cmake_flags }}
cmake --build build-shared --target transcribe
echo "TRANSCRIBE_LIBRARY=$GITHUB_WORKSPACE/build-shared/${{ matrix.lib_subdir }}/${{ matrix.lib }}" >> "$GITHUB_ENV"
- name: Install + build the binding
working-directory: bindings/typescript
run: |
npm install --no-audit --no-fund
npm run build
# The canary GGUFs upgrade the suite from no-model gates to real
# transcription/streaming/cancel/extension coverage. jfk.wav ships in-repo;
# only the model paths need exporting (fetch-canary handles that).
#
# Windows now runs the full model tier too. It was previously no-model-only
# because loading a model spun up ggml's CPU threadpool, and on MSVC with
# OpenMP (vcomp) the runtime crashed (0xC0000005) at process exit under
# node/koffi. The build now defaults to ggml's native threadpool (no OpenMP
# — see the OpenMP CENTRAL POLICY in CMakeLists.txt), so there is no vcomp
# pool to crash at teardown, and the native barrier is MSVC-correct (the
# transcribe_threadpool_oversubscription test guards it). Re-enabled here.
- uses: ./.github/actions/fetch-canary
with:
hf-token: ${{ secrets.HF_TOKEN }}
- name: Conformance (no-model tier always; model tier when canary present)
working-directory: bindings/typescript
run: npm test
- name: Examples (CI-executed; §6 Rosetta set)
working-directory: bindings/typescript
shell: bash # the loop is bash syntax; Git Bash makes it identical on Windows
run: |
for ex in transcribe-file streaming batch backend-select error-handling; do
echo "=== example: $ex ==="
node "examples/$ex.mjs"
done
# Deno runs the SAME unmodified package via N-API (no adapter). Proves the
# binding is engine-portable; self-skips when the canary is absent. Non-
# Windows: the Windows leg's job is the Node + koffi DLL-resolution proof,
# and Deno-on-Windows N-API quirks shouldn't gate that signal.
- name: Deno smoke (run the package as-is under deno)
if: runner.os != 'Windows'
working-directory: bindings/typescript
run: deno run --allow-ffi --allow-read --allow-env --allow-sys examples/transcribe-file.mjs
- name: ccache stats
if: runner.os == 'Linux'
run: ccache -s | head -8