typescript bindings (#31) #3
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: publish | |
| # Release pipeline (plan M5 tail). Auth is PyPI Trusted Publishing (OIDC) — | |
| # no tokens anywhere; the publishers are registered against THIS filename | |
| # (publish.yml) and the `testpypi` / `pypi` environments. | |
| # | |
| # REHEARSAL (workflow_dispatch): builds + validates the FULL matrix by | |
| # calling python-wheels.yml and cuda-windows.yml, publishes the | |
| # PyPI-bound set (api wheel, native wheels, sdist — the dist-* | |
| # artifacts) to TestPyPI, then proves a clean-machine install FROM | |
| # TestPyPI with a real transcription. cu12 wheels are NOT rehearsed on | |
| # TestPyPI (197 MB > its 100 MB file cap); their home is GitHub release | |
| # assets + the /whl/cu12 index, exercised on real tags. | |
| # NOTE: (Test)PyPI filenames are write-once — a failed rehearsal | |
| # iteration means bumping the patch version in include/transcribe.h | |
| # (the single version source) and re-dispatching. | |
| # | |
| # RELEASE (push tag v0.0.X): same full build, then with the `pypi` | |
| # environment (add a required-reviewer rule once the repo is public): | |
| # dist-* → PyPI, cu12 wheels → draft GitHub release assets for the tag, | |
| # Rust crates → crates.io, Swift → the draft release, then the draft is | |
| # published and wheel-index.yml is dispatched so /whl/cu12 picks it up. | |
| # cu12 → PyPI additionally when the repo variable CU12_ON_PYPI is "true" | |
| # (set it once the PyPI file-size request is granted). | |
| on: | |
| push: | |
| tags: ["v[0-9]*"] | |
| workflow_dispatch: | |
| # Never two publishes at once; never cancel one mid-upload. | |
| concurrency: | |
| group: publish | |
| cancel-in-progress: false | |
| jobs: | |
| # Full build + validation matrix; the artifacts published below are the | |
| # exact files these workflows tested post-repair. | |
| wheels: | |
| uses: ./.github/workflows/python-wheels.yml | |
| secrets: inherit | |
| cuda-windows: | |
| # Tags only: the heaviest build in the project (nvcc over ggml-cuda x 5 | |
| # arches under MSVC, ~3 h on 16vcpu), and the TestPyPI rehearsal neither | |
| # publishes nor smokes its output (cu12 wheels exceed TestPyPI's file | |
| # cap). Validate it on demand with its own workflow_dispatch. | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: ./.github/workflows/cuda-windows.yml | |
| secrets: inherit | |
| create-release: | |
| # Tags only. Owns creation of the GitHub Release object. Keep it draft | |
| # until the mandatory publishers have succeeded and assets are verified. | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Create the draft release for the tag | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -e | |
| tag="${GITHUB_REF_NAME}" | |
| gh release view "$tag" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1 || \ | |
| gh release create "$tag" --repo "$GITHUB_REPOSITORY" \ | |
| --title "$tag" --notes "transcribe.cpp $tag" --verify-tag --draft | |
| publish-testpypi: | |
| # Rehearsal target (dispatch only — real tags go to PyPI). | |
| # Blacksmith, not the Hetzner box: gh-action-pypi-publish is a Docker | |
| # container action and needs docker on the runner. | |
| if: github.event_name == 'workflow_dispatch' | |
| needs: [wheels] | |
| runs-on: blacksmith-2vcpu-ubuntu-2404 | |
| timeout-minutes: 30 | |
| environment: testpypi | |
| permissions: | |
| id-token: write # OIDC for trusted publishing | |
| steps: | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| pattern: dist-* | |
| merge-multiple: true | |
| path: dist | |
| - run: ls -la dist/ | |
| - uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| repository-url: https://test.pypi.org/legacy/ | |
| packages-dir: dist | |
| smoke-testpypi: | |
| # The point of the rehearsal: a clean machine installs from TestPyPI | |
| # alone (resolver pulls the native provider through the hard pin) and | |
| # transcribes. Mirrors clean-install, but the index is the real thing. | |
| needs: [publish-testpypi] | |
| runs-on: blacksmith-2vcpu-ubuntu-2404 | |
| container: python:3.12-slim | |
| timeout-minutes: 30 | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| steps: | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| name: dist-api | |
| path: dist-api | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| # samples/jfk.wav (+ the degradation script), uploaded by api-wheel; | |
| # files keep their repo-relative paths inside the artifact. | |
| name: smoke-assets | |
| path: smoke-assets | |
| - name: Install from TestPyPI (exact just-published version) and transcribe | |
| run: | | |
| set -e | |
| ver=$(ls dist-api/transcribe_cpp-*.whl | sed -E 's/.*transcribe_cpp-([0-9][^-]*)-.*/\1/') | |
| echo "published version: $ver" | |
| # TestPyPI can lag a few seconds behind a fresh upload; retry. | |
| for i in $(seq 1 10); do | |
| pip install -q \ | |
| --index-url https://test.pypi.org/simple/ \ | |
| --extra-index-url https://pypi.org/simple/ \ | |
| "transcribe-cpp==$ver" && break | |
| echo "index not ready yet ($i); retrying in 30s" && sleep 30 | |
| done | |
| python - <<'EOF' | |
| import transcribe_cpp as t | |
| print("provider:", t.native_provider()) | |
| print("devices:", [(d.name, d.kind) for d in t.backends()]) | |
| assert t.backend_available("cpu") | |
| EOF | |
| if [ -z "$HF_TOKEN" ]; then | |
| echo "no HF_TOKEN (fork?) — install-only check passed, skipping the model smoke" | |
| exit 0 | |
| fi | |
| pip install -q huggingface_hub | |
| hf download handy-computer/whisper-tiny-gguf \ | |
| whisper-tiny-Q5_K_M.gguf --local-dir canary | |
| python - <<'EOF' | |
| import array, wave | |
| import transcribe_cpp as t | |
| with wave.open("smoke-assets/samples/jfk.wav", "rb") as w: | |
| pcm16 = array.array("h"); pcm16.frombytes(w.readframes(w.getnframes())) | |
| pcm = array.array("f", (s / 32768.0 for s in pcm16)) | |
| with t.Model("canary/whisper-tiny-Q5_K_M.gguf") as m, m.session() as s: | |
| text = s.run(pcm).text | |
| print("text:", text.strip()) | |
| assert "country" in text.lower(), text | |
| print("ok: clean machine installed from TestPyPI and transcribed") | |
| EOF | |
| publish-pypi: | |
| # The real thing. Gate: only version tags reach this, and the `pypi` | |
| # environment carries the approval rule once the repo is public. | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: [wheels, cuda-windows] | |
| # Blacksmith for the same docker-container-action reason as testpypi. | |
| runs-on: blacksmith-2vcpu-ubuntu-2404 | |
| timeout-minutes: 30 | |
| environment: pypi | |
| permissions: | |
| id-token: write | |
| steps: | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| pattern: dist-* | |
| merge-multiple: true | |
| path: dist | |
| - name: Add cu12 wheels (only once the PyPI size request is granted) | |
| if: vars.CU12_ON_PYPI == 'true' | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: cuda-dist-* | |
| merge-multiple: true | |
| path: dist | |
| - run: ls -la dist/ | |
| - uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist | |
| release-assets: | |
| # cu12's primary distribution home: wheels as GitHub release assets, | |
| # served to pip through the PEP 503 index on Pages (wheel-index.yml). | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: [create-release, wheels, cuda-windows] | |
| runs-on: [self-hosted, Linux, X64, hetzner] | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: write # upload release assets | |
| steps: | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| pattern: cuda-dist-* | |
| merge-multiple: true | |
| path: cu12 | |
| # The canonical native bundles (extracted from the repaired wheels by | |
| # python-wheels.yml). On the release they become the distribution home | |
| # for non-PyPI ecosystems: npm platform packages and prebuilt-Rust | |
| # fetch these exact bytes by tag instead of rebuilding. | |
| - uses: actions/download-artifact@v8 | |
| with: | |
| pattern: native-* | |
| merge-multiple: true | |
| path: native-bundles | |
| - name: Attach the cu12 wheels | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -e | |
| tag="${GITHUB_REF#refs/tags/}" | |
| gh release upload "$tag" cu12/*.whl --repo "$GITHUB_REPOSITORY" --clobber | |
| - name: Attach the native bundles (versioned names) | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -e | |
| tag="${GITHUB_REF#refs/tags/}" | |
| ver="${tag#v}" | |
| mkdir -p upload | |
| for f in native-bundles/transcribe-native-*.tar.gz; do | |
| base="$(basename "$f" .tar.gz)" | |
| tuple="${base#transcribe-native-}" | |
| cp "$f" "upload/transcribe-native-${ver}-${tuple}.tar.gz" | |
| done | |
| ls -la upload/ | |
| gh release upload "$tag" upload/*.tar.gz --repo "$GITHUB_REPOSITORY" --clobber | |
| # ---- Rust crates (crates.io) ------------------------------------------------- | |
| # The Rust release path mirrors the Python one: a dispatch REHEARSAL that | |
| # validates without uploading, and a tag-gated RELEASE cut from CI. crates.io | |
| # has no TestPyPI analog, so the rehearsal is `cargo publish --dry-run` plus | |
| # the packed-crate smoke (build libtranscribe from the shipped tarball and | |
| # transcribe) — the Rust "test the shipped artifact" gate (requirements §4). | |
| rust-rehearsal: | |
| # Dispatch only. Independent of the wheel jobs (no `needs`): the Rust and | |
| # Python release artifacts don't share build steps. | |
| if: github.event_name == 'workflow_dispatch' | |
| runs-on: blacksmith-2vcpu-ubuntu-2404 | |
| timeout-minutes: 40 | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: astral-sh/setup-uv@v8.2.0 # fetch-canary + the packed-smoke script | |
| - name: Install build deps | |
| run: sudo apt-get update && sudo apt-get install -y cmake ninja-build zlib1g-dev | |
| - name: Dry-run transcribe-cpp-sys (full verify build, as cargo would publish it) | |
| run: cargo publish --dry-run --allow-dirty -p transcribe-cpp-sys | |
| # The safe crate cannot be dry-run-verified pre-publish: it depends on | |
| # transcribe-cpp-sys = "0.0.1", and the registry only has the 0.0.0 | |
| # name-reservation placeholder until sys actually publishes (sys-first | |
| # ordering). Its packaging is verified at release time below; here the | |
| # packed-crate smoke exercises it against the packed sys instead. | |
| - uses: ./.github/actions/fetch-canary | |
| with: | |
| hf-token: ${{ secrets.HF_TOKEN }} | |
| - name: Packed-crate smoke (build the native lib from the shipped tarball, transcribe) | |
| run: uv run --no-project python scripts/ci/rust_packed_smoke.py | |
| rust-release: | |
| # Tags only. Releases are cut from CI, never a laptop (requirements §5). | |
| # sys publishes FIRST (the safe crate resolves it by version), then the safe | |
| # crate. The `crates-io` environment carries the approval gate (CJ approves) | |
| # and holds CARGO_REGISTRY_TOKEN. cargo waits for the index to carry sys | |
| # before returning, so the safe publish that follows resolves it. | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| runs-on: blacksmith-2vcpu-ubuntu-2404 | |
| timeout-minutes: 40 | |
| environment: crates-io | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Install build deps | |
| run: sudo apt-get update && sudo apt-get install -y cmake ninja-build zlib1g-dev | |
| - name: Publish transcribe-cpp-sys (the native-carrying crate; verifies the build) | |
| run: cargo publish -p transcribe-cpp-sys | |
| - name: Publish transcribe-cpp (the safe wrapper; resolves the just-published sys) | |
| run: cargo publish -p transcribe-cpp | |
| # --------------------------------------------------------------------------- | |
| # Swift binding (TranscribeCpp) — xcframework release. | |
| # | |
| # The Swift package is consumed as a prebuilt static `.xcframework` | |
| # binaryTarget (notes/swift-bindings-plan.md; requirements §5). Releasing it | |
| # means: build the four Apple slices, zip + checksum the xcframework, attach | |
| # the zip as a release asset, then point the mirror repo's Package.swift | |
| # `binaryTarget(url:checksum:)` at it. "Releases are cut from CI, never a | |
| # laptop." macOS runner: the xcframework needs Xcode (libtool/xcodebuild). | |
| # --------------------------------------------------------------------------- | |
| swift-rehearsal: | |
| # The shipped-artifact gate (§4): build the real xcframework and run the | |
| # suite against it (transcribes the canary through the published shape). | |
| if: github.event_name == 'workflow_dispatch' | |
| runs-on: macos-15 | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: astral-sh/setup-uv@v8.2.0 | |
| - run: brew install ninja | |
| - name: Build the full xcframework (macOS + iOS device + iOS simulator) | |
| run: scripts/ci/build_xcframework.sh | |
| - name: Package + checksum (proves the release artifact + licenses) | |
| run: scripts/ci/package_xcframework.sh | |
| - uses: ./.github/actions/fetch-canary | |
| with: | |
| hf-token: ${{ secrets.HF_TOKEN }} | |
| - name: swift test against the built xcframework | |
| working-directory: bindings/swift | |
| run: swift test | |
| swift-release: | |
| # Tags only. Builds the artifact, attaches the zip to the tag's release, and | |
| # emits the checksum. The mirror-repo Package.swift bump is the final step | |
| # (CJ-gated — needs the dedicated SwiftPM repo + a deploy key; see the plan). | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: [create-release] | |
| runs-on: macos-15 | |
| permissions: | |
| contents: write # attach the release asset | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - run: brew install ninja | |
| - name: Build the full xcframework | |
| run: scripts/ci/build_xcframework.sh | |
| - name: Package + checksum | |
| id: pkg | |
| run: | | |
| scripts/ci/package_xcframework.sh | tee pkg.txt | |
| echo "checksum=$(awk '/checksum:/ {print $2}' pkg.txt)" >> "$GITHUB_OUTPUT" | |
| - name: Attach the xcframework zip to the release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -e | |
| tag="${GITHUB_REF_NAME}" | |
| gh release upload "$tag" \ | |
| bindings/swift/build-apple/TranscribeCpp.xcframework.zip \ | |
| --repo "$GITHUB_REPOSITORY" --clobber | |
| - name: Checksum for the mirror repo's Package.swift | |
| run: | | |
| echo "binaryTarget(url: .../TranscribeCpp.xcframework.zip," | |
| echo " checksum: \"${{ steps.pkg.outputs.checksum }}\")" | |
| # TODO(CJ): push the thin Swift sources + the url/checksum-bearing | |
| # Package.swift to the dedicated mirror repo (transcribe-cpp-swift) and tag | |
| # it, so `swift package add` resolves the release. Needs the mirror repo + | |
| # 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 | |
| # gate and does not host artifacts on this release. | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: [publish-pypi, release-assets, swift-release] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write # publish the draft release | |
| actions: write # dispatch wheel-index | |
| steps: | |
| - name: Verify assets and publish the release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| tag="${GITHUB_REF_NAME}" | |
| ver="${tag#v}" | |
| assets_file="$(mktemp)" | |
| gh release view "$tag" --repo "$GITHUB_REPOSITORY" \ | |
| --json assets --jq '.assets[].name' | sort > "$assets_file" | |
| echo "Release assets:" | |
| sed 's/^/ /' "$assets_file" | |
| require_asset() { | |
| name="$1" | |
| if ! grep -Fxq "$name" "$assets_file"; then | |
| echo "::error::missing release asset: $name" | |
| exit 1 | |
| fi | |
| } | |
| require_asset "TranscribeCpp.xcframework.zip" | |
| require_asset "transcribe-native-${ver}-linux-x86_64-cpu-vulkan.tar.gz" | |
| require_asset "transcribe-native-${ver}-linux-aarch64-cpu-vulkan.tar.gz" | |
| require_asset "transcribe-native-${ver}-macos-arm64-metal.tar.gz" | |
| require_asset "transcribe-native-${ver}-macos-x86_64-cpu.tar.gz" | |
| require_asset "transcribe-native-${ver}-windows-x86_64-cpu-vulkan.tar.gz" | |
| cu12_count="$(grep -Ec '^transcribe_cpp_native_cu12-.*\.whl$' "$assets_file" || true)" | |
| if [ "$cu12_count" -lt 2 ]; then | |
| echo "::error::expected at least two cu12 provider wheels, found $cu12_count" | |
| exit 1 | |
| fi | |
| gh release edit "$tag" --repo "$GITHUB_REPOSITORY" --draft=false | |
| - name: Refresh the PEP 503 index (requires Pages enabled on the repo) | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh workflow run wheel-index.yml --repo "$GITHUB_REPOSITORY" || \ | |
| echo "::warning::wheel-index dispatch failed — is the workflow on the default branch and Pages enabled?" |