ONNX Runtime WASM #102
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: ONNX Runtime WASM | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: 'Dry run (build only, no release)' | |
| type: boolean | |
| default: true | |
| force: | |
| description: 'Force rebuild (ignore cache)' | |
| type: boolean | |
| default: false | |
| build_mode: | |
| description: 'Build mode' | |
| type: choice | |
| options: | |
| - prod | |
| - dev | |
| default: prod | |
| workflow_call: | |
| inputs: | |
| dry_run: | |
| type: boolean | |
| default: true | |
| force: | |
| type: boolean | |
| default: false | |
| build_mode: | |
| type: string | |
| default: prod | |
| permissions: | |
| contents: read | |
| jobs: | |
| build: | |
| permissions: | |
| contents: read | |
| runs-on: ubuntu-22.04 | |
| timeout-minutes: 90 | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| with: | |
| persist-credentials: false | |
| - name: Load tool versions from external-tools.json | |
| id: tool-versions | |
| run: | | |
| NODE_VERSION=$(jq -r '.tools.node.versions.recommendedVersion' packages/build-infra/external-tools.json) | |
| ONNX_VERSION=$(jq -r '.sources.onnxruntime.version' packages/onnxruntime-builder/package.json) | |
| echo "node-version=$NODE_VERSION" >> $GITHUB_OUTPUT | |
| echo "onnx-version=$ONNX_VERSION" >> $GITHUB_OUTPUT | |
| echo "Loaded Node.js: $NODE_VERSION, ONNX Runtime: $ONNX_VERSION" | |
| - name: Setup Node.js | |
| uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 | |
| with: | |
| node-version: ${{ steps.tool-versions.outputs.node-version }} | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0 | |
| # Note: version is specified in package.json packageManager field, not here | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Generate ONNX Runtime cache key | |
| id: cache-key | |
| run: | | |
| # Per-checkpoint cumulative hashing (like node-smol) | |
| hash_dir() { | |
| local dir=$1 | |
| if [ -d "$dir" ]; then | |
| find "$dir" -type f -name "*.mjs" 2>/dev/null | sort | xargs shasum -a 256 2>/dev/null | shasum -a 256 | cut -d' ' -f1 || echo "" | |
| else | |
| echo "" | |
| fi | |
| } | |
| # Cache version - bump to force-invalidate all caches | |
| CACHE_VERSION="v3" | |
| COMMON=$(hash_dir packages/onnxruntime-builder/scripts/common) | |
| PACKAGE_JSON=$(shasum -a 256 packages/onnxruntime-builder/package.json | cut -d' ' -f1) | |
| BUILD_MJS=$(shasum -a 256 packages/onnxruntime-builder/scripts/build.mjs | cut -d' ' -f1) | |
| # source-cloned: cache-version + common + source-cloned + build.mjs + package.json | |
| SOURCE_CLONED_DIR=$(hash_dir packages/onnxruntime-builder/scripts/source-cloned) | |
| SOURCE_CLONED_HASH=$(echo "${CACHE_VERSION}${COMMON}${SOURCE_CLONED_DIR}${BUILD_MJS}${PACKAGE_JSON}" | shasum -a 256 | cut -d' ' -f1) | |
| # wasm-compiled | |
| WASM_COMPILED_DIR=$(hash_dir packages/onnxruntime-builder/scripts/wasm-compiled) | |
| WASM_COMPILED_HASH=$(echo "${SOURCE_CLONED_HASH}${WASM_COMPILED_DIR}" | shasum -a 256 | cut -d' ' -f1) | |
| # wasm-released | |
| WASM_RELEASE_DIR=$(hash_dir packages/onnxruntime-builder/scripts/wasm-released) | |
| WASM_RELEASE_HASH=$(echo "${WASM_COMPILED_HASH}${WASM_RELEASE_DIR}" | shasum -a 256 | cut -d' ' -f1) | |
| # wasm-optimized | |
| WASM_OPTIMIZED_DIR=$(hash_dir packages/onnxruntime-builder/scripts/wasm-optimized) | |
| WASM_OPTIMIZED_HASH=$(echo "${WASM_RELEASE_HASH}${WASM_OPTIMIZED_DIR}" | shasum -a 256 | cut -d' ' -f1) | |
| # wasm-synced: wasm-optimized + wasm-synced + wasm-sync-wrapper.mjs (shared infra) | |
| WASM_SYNC_DIR=$(hash_dir packages/onnxruntime-builder/scripts/wasm-synced) | |
| WASM_SYNC_WRAPPER=$(shasum -a 256 packages/build-infra/wasm-synced/wasm-sync-wrapper.mjs | cut -d' ' -f1) | |
| WASM_SYNC_HASH=$(echo "${WASM_OPTIMIZED_HASH}${WASM_SYNC_DIR}${WASM_SYNC_WRAPPER}" | shasum -a 256 | cut -d' ' -f1) | |
| # finalized | |
| FINAL_DIR=$(hash_dir packages/onnxruntime-builder/scripts/finalized) | |
| FINAL_HASH=$(echo "${WASM_SYNC_HASH}${FINAL_DIR}" | shasum -a 256 | cut -d' ' -f1) | |
| echo "cache_version=${CACHE_VERSION}" >> $GITHUB_OUTPUT | |
| echo "source_cloned_hash=${SOURCE_CLONED_HASH}" >> $GITHUB_OUTPUT | |
| echo "wasm_final_hash=${FINAL_HASH}" >> $GITHUB_OUTPUT | |
| - name: Install build tools | |
| run: | | |
| sudo apt-get update | |
| # Load versions from external-tools.json (single source of truth) | |
| NINJA_VERSION=$(node packages/build-infra/scripts/get-tool-version.mjs ninja apt) | |
| sudo apt-get install -y ninja-build=${NINJA_VERSION}-* | |
| echo "Installed ninja-build ${NINJA_VERSION} for faster builds" | |
| - name: Set build mode | |
| id: build-mode | |
| env: | |
| INPUT_BUILD_MODE: ${{ inputs.build_mode }} | |
| run: | | |
| # Sanitize input - only allow 'prod' or 'dev' | |
| if [ "$INPUT_BUILD_MODE" = "dev" ]; then | |
| BUILD_MODE="dev" | |
| else | |
| BUILD_MODE="prod" | |
| fi | |
| echo "mode=$BUILD_MODE" >> $GITHUB_OUTPUT | |
| echo "Build mode: $BUILD_MODE" | |
| - name: Restore ONNX Runtime checkpoint cache | |
| uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 | |
| id: onnx-checkpoint-cache | |
| if: ${{ !inputs.force }} | |
| with: | |
| path: | | |
| packages/onnxruntime-builder/build/shared/checkpoints | |
| packages/onnxruntime-builder/build/${{ steps.build-mode.outputs.mode }}/checkpoints | |
| key: onnx-checkpoints-${{ steps.cache-key.outputs.cache_version }}-v${{ steps.tool-versions.outputs.onnx-version }}-${{ runner.os }}-${{ steps.build-mode.outputs.mode }}-${{ steps.cache-key.outputs.wasm_final_hash }} | |
| - name: Validate checkpoint cache integrity | |
| id: validate-cache | |
| if: steps.onnx-checkpoint-cache.outputs.cache-hit == 'true' | |
| env: | |
| BUILD_MODE: ${{ steps.build-mode.outputs.mode }} | |
| run: | | |
| echo "Validating cached ONNX Runtime checkpoints..." | |
| SHARED_CHECKPOINT_DIR="packages/onnxruntime-builder/build/shared/checkpoints" | |
| MODE_CHECKPOINT_DIR="packages/onnxruntime-builder/build/${BUILD_MODE}/checkpoints" | |
| # Check if required checkpoint files exist | |
| # source-cloned is in shared directory, others are in mode-specific directory | |
| # Note: wasm-optimized.json only exists in prod mode (optimization is skipped in dev mode) | |
| # Check shared checkpoints | |
| if [ ! -f "${SHARED_CHECKPOINT_DIR}/source-cloned.json" ] || \ | |
| [ ! -f "${SHARED_CHECKPOINT_DIR}/source-cloned.tar.gz" ]; then | |
| echo "❌ Shared checkpoint files incomplete" | |
| rm -rf "${SHARED_CHECKPOINT_DIR}" "${MODE_CHECKPOINT_DIR}" | |
| echo "cache_valid=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Check mode-specific checkpoints | |
| MODE_CHECKPOINTS="wasm-compiled wasm-released wasm-synced finalized" | |
| if [ "${BUILD_MODE}" = "prod" ]; then | |
| MODE_CHECKPOINTS="wasm-compiled wasm-released wasm-optimized wasm-synced finalized" | |
| fi | |
| for checkpoint in ${MODE_CHECKPOINTS}; do | |
| if [ ! -f "${MODE_CHECKPOINT_DIR}/${checkpoint}.json" ] || \ | |
| [ ! -f "${MODE_CHECKPOINT_DIR}/${checkpoint}.tar.gz" ]; then | |
| echo "❌ Mode checkpoint files incomplete (missing: ${checkpoint})" | |
| rm -rf "${SHARED_CHECKPOINT_DIR}" "${MODE_CHECKPOINT_DIR}" | |
| echo "cache_valid=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| done | |
| # Check tarball integrity for both directories | |
| for checkpoint_dir in "${SHARED_CHECKPOINT_DIR}" "${MODE_CHECKPOINT_DIR}"; do | |
| for tarball in "${checkpoint_dir}"/*.tar.gz; do | |
| if [ -f "$tarball" ]; then | |
| if ! gzip -t "$tarball" 2>/dev/null; then | |
| echo "❌ Corrupted tarball: $(basename "$tarball")" | |
| rm -rf "${SHARED_CHECKPOINT_DIR}" "${MODE_CHECKPOINT_DIR}" | |
| echo "cache_valid=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| fi | |
| done | |
| done | |
| echo "✅ Checkpoint cache validation passed" | |
| echo "cache_valid=true" >> $GITHUB_OUTPUT | |
| - name: Set checkpoint chain for build mode | |
| id: checkpoint-chain | |
| env: | |
| BUILD_MODE: ${{ steps.build-mode.outputs.mode }} | |
| run: | | |
| # Dev mode: skip wasm-optimized (optimization is disabled) | |
| # Prod mode: include wasm-optimized | |
| if [ "$BUILD_MODE" = "prod" ]; then | |
| CHAIN="finalized,wasm-synced,wasm-optimized,wasm-released,wasm-compiled,source-cloned" | |
| else | |
| CHAIN="finalized,wasm-synced,wasm-released,wasm-compiled,source-cloned" | |
| fi | |
| echo "checkpoint_chain=$CHAIN" >> $GITHUB_OUTPUT | |
| echo "Checkpoint chain for $BUILD_MODE mode: $CHAIN" | |
| - name: Restore build output from checkpoint chain | |
| id: restore-checkpoint | |
| uses: ./.github/actions/restore-checkpoint | |
| with: | |
| package-name: 'onnxruntime-builder' | |
| build-mode: ${{ steps.build-mode.outputs.mode }} | |
| checkpoint-chain: ${{ steps.checkpoint-chain.outputs.checkpoint_chain }} | |
| cache-hit: ${{ steps.onnx-checkpoint-cache.outputs.cache-hit }} | |
| cache-valid: ${{ steps.validate-cache.outputs.cache_valid }} | |
| - name: Build ONNX Runtime WASM | |
| id: build | |
| if: | | |
| (steps.onnx-checkpoint-cache.outputs.cache-hit != 'true' || steps.validate-cache.outputs.cache_valid == 'false') || | |
| steps.restore-checkpoint.outputs.needs-build == 'true' | |
| env: | |
| BUILD_MODE: ${{ steps.build-mode.outputs.mode }} | |
| run: pnpm --filter onnxruntime-builder build --$BUILD_MODE | |
| - name: Validate build output | |
| env: | |
| BUILD_MODE: ${{ steps.build-mode.outputs.mode }} | |
| run: | | |
| echo "Validating ONNX Runtime build output..." | |
| if [ ! -f "packages/onnxruntime-builder/build/${BUILD_MODE}/out/Final/ort.wasm" ]; then | |
| echo "❌ Build failed: WASM file missing" | |
| exit 1 | |
| fi | |
| if [ ! -f "packages/onnxruntime-builder/build/${BUILD_MODE}/out/Final/ort.mjs" ]; then | |
| echo "❌ Build failed: MJS file missing" | |
| exit 1 | |
| fi | |
| if [ ! -f "packages/onnxruntime-builder/build/${BUILD_MODE}/out/Final/ort-sync.js" ]; then | |
| echo "❌ Build failed: Sync JS file missing" | |
| exit 1 | |
| fi | |
| WASM_SIZE=$(stat -c%s "packages/onnxruntime-builder/build/${BUILD_MODE}/out/Final/ort.wasm" 2>/dev/null || stat -f%z "packages/onnxruntime-builder/build/${BUILD_MODE}/out/Final/ort.wasm") | |
| if [ "$WASM_SIZE" -lt 1000000 ]; then | |
| echo "❌ Build failed: WASM file too small ($WASM_SIZE bytes)" | |
| exit 1 | |
| fi | |
| echo "✅ Build validation passed" | |
| - name: Upload ONNX Runtime artifacts | |
| uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 | |
| with: | |
| name: onnxruntime-wasm | |
| path: packages/onnxruntime-builder/build/${{ steps.build-mode.outputs.mode }}/out/Final/ | |
| retention-days: 30 | |
| if-no-files-found: error | |
| release: | |
| needs: build | |
| if: | | |
| (github.event_name == 'workflow_dispatch' && !inputs.dry_run) || | |
| (github.event_name == 'release') | |
| runs-on: ubuntu-22.04 | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| with: | |
| persist-credentials: false | |
| - name: Load ONNX version from package.json | |
| id: tool-versions | |
| run: | | |
| ONNX_VERSION=$(jq -r '.sources.onnxruntime.version' packages/onnxruntime-builder/package.json) | |
| echo "onnx-version=$ONNX_VERSION" >> $GITHUB_OUTPUT | |
| echo "Loaded ONNX Runtime: $ONNX_VERSION" | |
| - name: Download ONNX Runtime artifacts | |
| uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 | |
| with: | |
| name: onnxruntime-wasm | |
| path: packages/onnxruntime-builder/build/prod/out/Final/ | |
| - name: Generate version | |
| id: version | |
| run: | | |
| source .github/scripts/generate-version.sh | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Version: $VERSION" | |
| - name: Generate checksums | |
| run: | | |
| cd packages/onnxruntime-builder/build/prod/out/Final | |
| shasum -a 256 *.wasm *.js > checksums.txt | |
| cat checksums.txt | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| STEPS_VERSION_OUTPUTS_VERSION: ${{ steps.version.outputs.version }} | |
| run: | | |
| VERSION="${STEPS_VERSION_OUTPUTS_VERSION}" | |
| TAG="onnxruntime-v${VERSION}" | |
| # Check if release already exists | |
| if gh release view "$TAG" &>/dev/null; then | |
| echo "Release $TAG already exists, uploading assets..." | |
| gh release upload "$TAG" \ | |
| packages/onnxruntime-builder/build/prod/out/Final/*.wasm \ | |
| packages/onnxruntime-builder/build/prod/out/Final/*.js \ | |
| packages/onnxruntime-builder/build/prod/out/Final/checksums.txt \ | |
| --clobber | |
| else | |
| echo "Creating new release $TAG..." | |
| gh release create "$TAG" \ | |
| --title "ONNX Runtime WASM v${VERSION}" \ | |
| --notes "ONNX Runtime v${{ steps.tool-versions.outputs.onnx-version }} compiled to WASM with SIMD and threading support. | |
| ## Included Files | |
| - \`ort.wasm\` - Main WASM binary with SIMD + threading | |
| - \`ort.mjs\` - ES module loader | |
| - \`checksums.txt\` - SHA256 checksums for verification | |
| ## Usage | |
| \`\`\`javascript | |
| import * as ort from './ort.mjs'; | |
| \`\`\` | |
| Built from ONNX Runtime v${{ steps.tool-versions.outputs.onnx-version }}" \ | |
| packages/onnxruntime-builder/build/prod/out/Final/*.wasm \ | |
| packages/onnxruntime-builder/build/prod/out/Final/*.js \ | |
| packages/onnxruntime-builder/build/prod/out/Final/checksums.txt | |
| fi |