Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
118 changes: 118 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,124 @@ jobs:
# a deploy key secret. Until then, the asset + checksum above are produced
# but not wired into a resolvable SwiftPM tag.

# ---- npm (TypeScript) --------------------------------------------------------
# Mirrors the Python/Rust path: a dispatch REHEARSAL that builds + installs the
# shipped tarballs and transcribes the canary (the §4 shipped-artifact gate),
# and a tag-gated RELEASE that turns the native bundles into per-platform npm
# packages and publishes them with the pure-TS API package.

ts-rehearsal:
# Pack the API + a platform package, install from the tarballs into a clean
# dir, and transcribe the canary — exactly the clean-machine path, no
# TRANSCRIBE_LIBRARY (the install stands on the platform package alone).
if: github.event_name == 'workflow_dispatch'
runs-on: [self-hosted, macOS, ARM64]
env:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- uses: astral-sh/setup-uv@v8.2.0
- run: command -v ninja >/dev/null || brew install ninja
- name: Build + install a shared libtranscribe (the native-bundle twin)
run: |
cmake -B build-shared -G Ninja -DTRANSCRIBE_BUILD_SHARED=ON \
-DGGML_METAL=ON -DGGML_METAL_EMBED_LIBRARY=ON
cmake --build build-shared --target transcribe
cmake --install build-shared --prefix "$PWD/ts-native"
- name: Pack API + platform tarballs
working-directory: bindings/typescript
run: |
npm install --no-audit --no-fund
npm run build
ver="$(node -p "require('./package.json').version")"
node scripts/pack-platform.mjs --lib-dir "$GITHUB_WORKSPACE/ts-native/lib" \
--tuple darwin-arm64-metal --version "$ver" --out /tmp/ts-platform
rm -rf /tmp/ts-tarballs && mkdir -p /tmp/ts-tarballs
npm pack --pack-destination /tmp/ts-tarballs
(cd /tmp/ts-platform/darwin-arm64-metal && npm pack --pack-destination /tmp/ts-tarballs)
- uses: ./.github/actions/fetch-canary
with:
hf-token: ${{ secrets.HF_TOKEN }}
- name: Install from tarballs (clean dir) and transcribe the canary
run: |
rm -rf /tmp/ts-consumer && mkdir -p /tmp/ts-consumer
(cd /tmp/ts-consumer && npm init -y >/dev/null && \
npm install --no-audit --no-fund /tmp/ts-tarballs/*.tgz)
TS_PKG_ENTRY=/tmp/ts-consumer/node_modules/transcribe-cpp/dist/index.js \
TRANSCRIBE_SMOKE_AUDIO="$GITHUB_WORKSPACE/samples/jfk.wav" \
node scripts/ci/ts_packed_smoke.mjs

ts-release:
# Tags only. Build each @transcribe-cpp/<platform> package from its native
# bundle (the exact bytes the wheels shipped) and publish them + the pure-TS
# API package to npm. The `npm` environment carries the approval gate.
if: startsWith(github.ref, 'refs/tags/v')
needs: [create-release, wheels]
runs-on: blacksmith-2vcpu-ubuntu-2404
environment: npm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: "https://registry.npmjs.org"
- uses: actions/download-artifact@v8
with:
pattern: native-*
merge-multiple: true
path: native-bundles
- name: Build platform packages from the native bundles
working-directory: bindings/typescript
run: |
set -e
npm install --no-audit --no-fund
npm run build
ver="$(node -p "require('./package.json').version")"
# Sync the API package's @transcribe-cpp/* platform pins to the version
# we publish, so the tarball pins the exact bytes we ship (the static
# gate in check_version_sync.py enforces the base match; this nails the
# exact pin). Without this the pins go stale on the first version bump
# and the install resolves a mismatched native -> load-time AbiError.
node -e '
const fs = require("node:fs");
const v = process.argv[1];
const p = JSON.parse(fs.readFileSync("package.json", "utf8"));
for (const k of Object.keys(p.optionalDependencies || {})) p.optionalDependencies[k] = v;
fs.writeFileSync("package.json", JSON.stringify(p, null, 2) + "\n");
' "$ver"
out=/tmp/ts-platform
# build-tuple (bundle) -> npm tuple (Node platform-arch convention)
map() { case "$1" in
macos-arm64-metal) echo darwin-arm64-metal ;;
macos-x86_64-cpu) echo darwin-x64-cpu ;;
linux-x86_64-cpu-vulkan) echo linux-x64-cpu-vulkan ;;
linux-aarch64-cpu-vulkan) echo linux-arm64-cpu-vulkan ;;
windows-x86_64-cpu-vulkan) echo win32-x64-cpu-vulkan ;;
*) echo "" ;; esac; }
for f in "$GITHUB_WORKSPACE"/native-bundles/transcribe-native-*.tar.gz; do
base="$(basename "$f" .tar.gz)"; build_tuple="${base#transcribe-native-}"
npm_tuple="$(map "$build_tuple")"
[ -n "$npm_tuple" ] || { echo "::warning::no npm tuple for $build_tuple"; continue; }
ex="/tmp/extract/$build_tuple"; rm -rf "$ex"; mkdir -p "$ex"; tar xzf "$f" -C "$ex"
libdir="$(dirname "$(find "$ex" \( -name 'libtranscribe.*' -o -name 'transcribe.dll' \) | head -1)")"
node scripts/pack-platform.mjs --lib-dir "$libdir" --tuple "$npm_tuple" --version "$ver" --out "$out"
done
echo "TS_VER=$ver" >> "$GITHUB_ENV"
- name: Publish platform packages, then the API package
working-directory: bindings/typescript
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -e
for d in /tmp/ts-platform/*/; do
echo "publishing $(node -p "require('${d}package.json').name")"
npm publish "$d" --access public
done
npm publish --access public # the API package (transcribe-cpp)

finalize-release:
# Tags only. Publish the draft GitHub Release only after its hosted assets
# and the core PyPI publish have completed. crates.io has its own approval
Expand Down
24 changes: 23 additions & 1 deletion .github/workflows/rust-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ name: rust-ci
# `return` early (a clean skip) otherwise. One `cargo test` invocation runs
# both tiers; the model tier self-skips when the canary env is absent.
#
# Posture cost split: the model tier (canary fetch + the model-gated tests + the
# examples) runs ONLY on the representative *-static leg of each OS. The *-shared
# and *-dynamic-backends legs are LINK-posture proofs, not marshalling proofs
# (the marshalling code is byte-identical across postures), so they run build +
# the -sys FFI smoke + the always-on no-model tier (which still covers the
# dynamic-backends module load) and skip the canary fetch — dropping the
# duplicated model-tier cost (chiefly the moonshine streaming conformance) from
# the legs that can't catch a marshalling regression anyway.
#
# Windows joins here: the first cargo-driven MSVC build of this tree (static
# posture). cl.exe comes from msvc-dev-cmd, zlib from vcpkg as a static lib
# against the dynamic CRT (`x64-windows-static-md`, matching Rust's default /MD
Expand Down Expand Up @@ -105,9 +114,11 @@ jobs:
- label: linux-static
runner: blacksmith-2vcpu-ubuntu-2404
features: ""
model_tier: true # representative leg for linux: full model tier
- label: linux-shared
runner: blacksmith-2vcpu-ubuntu-2404
features: "--features shared"
model_tier: false # link-posture proof only (build + -sys smoke + no-model)
# Loadable per-ISA backend modules (the multi-ISA CPU provider posture)
# exercised through Rust: build.rs turns on BACKEND_DL + CPU_ALL_VARIANTS
# on x86, the modules install into the -sys prefix, and the safe crate's
Expand All @@ -116,15 +127,18 @@ jobs:
- label: linux-dynamic-backends
runner: blacksmith-2vcpu-ubuntu-2404
features: "--features dynamic-backends"
model_tier: false # no-model tier still covers the DL module load
# macOS arm64 on the bare-metal M4 mini (all macOS arm64 CI on owned
# hardware; the source build turns Metal on, and real hardware is the
# trustworthy place to exercise it). Same posture as native-ci.
- label: macos-arm64-static
runner: [self-hosted, macOS, ARM64]
features: ""
model_tier: true # representative leg for macOS: full model tier
- label: macos-arm64-shared
runner: [self-hosted, macOS, ARM64]
features: "--features shared"
model_tier: false # link-posture proof only
# Windows / MSVC (Blacksmith windows-2025, same image family as the
# wheel lane). First cargo-driven MSVC build of this tree; no ccache
# here (MSVC + ccache is fiddly), so every run is a cold ggml build —
Expand All @@ -134,9 +148,11 @@ jobs:
- label: windows-static
runner: blacksmith-2vcpu-windows-2025
features: ""
model_tier: true # representative leg for windows: full model tier
- label: windows-shared
runner: blacksmith-2vcpu-windows-2025
features: "--features shared"
model_tier: false # link-posture proof only
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
env:
Expand Down Expand Up @@ -215,7 +231,12 @@ jobs:
# (forks have none): without it the model tests skip cleanly, leaving the
# always-on no-model tier. jfk.wav ships in-repo, so only the two model
# paths need exporting (fetch-canary handles that).
- uses: ./.github/actions/fetch-canary
# Model tier runs only on the representative *-static leg per OS. The
# shared / dynamic-backends legs skip the canary fetch, so the model-gated
# tests in the conformance step below self-skip and only the no-model tier
# runs there (build + link posture + FFI surface proof).
- if: matrix.model_tier
uses: ./.github/actions/fetch-canary
with:
hf-token: ${{ secrets.HF_TOKEN }}
- name: Safe-crate conformance (no-model tier always; model tier when canary present)
Expand All @@ -225,6 +246,7 @@ jobs:
# the TRANSCRIBE_SMOKE_* env fetch-canary exports (or skips cleanly + exits
# 0). shell:bash so the loop is identical on the windows runner (Git Bash).
- name: Examples (CI-executed; §6 Rosetta set)
if: matrix.model_tier # examples need the canary models; only the model-tier legs have them
shell: bash
run: |
for ex in transcribe-file streaming batch backend-select error-handling; do
Expand Down
Loading
Loading