Skip to content

Migrate Web CI into github actions #24219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 28, 2025
Merged
Changes from all commits
Commits
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
71 changes: 71 additions & 0 deletions .github/actions/linux-web-init-and-check/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: "Web Build Initialize and Check on Linux"
description: "Initializes and checks the ONNX Runtime Web build on Linux."
runs:
using: "composite"
steps:
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "22.x"

- name: npm ci /js/
run: npm ci
shell: bash
working-directory: ${{ github.workspace }}/js

- name: npm ci /js/common/
run: npm ci
shell: bash
working-directory: ${{ github.workspace }}/js/common

- name: run onnxruntime-common tests
run: npm test
shell: bash
working-directory: ${{ github.workspace }}/js/common

- name: run onnxruntime-common tests (enable Float16Array)
run: npm run test:f16
shell: bash
working-directory: ${{ github.workspace }}/js/common

- name: npm ci /js/web/
run: npm ci
shell: bash
working-directory: ${{ github.workspace }}/js/web

- name: run TypeScript type check in /js/web/
run: npm run prebuild
shell: bash
working-directory: ${{ github.workspace }}/js/web

- name: run ESLint
run: npm run lint
shell: bash
working-directory: ${{ github.workspace }}/js

- name: Format code
run: npm run format
shell: bash
working-directory: ${{ github.workspace }}/js

- name: Check unformatted files
run: |
node -e "a=require('child_process').execSync('git diff --name-only').toString();if(a)throw new Error('Following source files are not formatted: (did you run \"npm run format\"?)\n'+a)"
shell: bash
working-directory: ${{ github.workspace }}/js

- name: TypeDoc Validation
run: npx typedoc --emit none --treatWarningsAsErrors
shell: bash
working-directory: ${{ github.workspace }}/js/common

- name: Generating documents
run: npm run build:doc
shell: bash
working-directory: ${{ github.workspace }}/js/web

- name: Check out of dated documents
run: |
node -e "a=require('child_process').execSync('git diff --name-only').toString();if(a)throw new Error('Following documents are not up-to-date: (did you run \"npm run build:doc\"?)\n'+a)"
shell: bash
working-directory: ${{ github.workspace }}/js/web
28 changes: 28 additions & 0 deletions .github/actions/webgpu-validate-shader-key/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: "WebGPU Validate Shader Key"
description: "Validate if the shader key is consistent for WebGPU shaders."

inputs:
log_file_path:
required: true
type: string
is_chromium_log:
required: false
type: boolean
default: false

runs:
using: "composite"
steps:
- name: Validate shader keys (chromium log)
if: ${{ inputs.is_chromium_log }}
shell: cmd
run: |
node parse-chromium-debug-log.js < "${{ inputs.log_file_path }}" | node validate-shader-key.js
working-directory: ${{ github.action_path }}

- name: Validate shader keys (native log)
if: ${{ !inputs.is_chromium_log }}
shell: cmd
run: |
node validate-shader-key.js < "${{ inputs.log_file_path }}"
working-directory: ${{ github.action_path }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

"use strict";

const { EOL } = require("os");
const readline = require("readline");

// This script is used to parse the Chromium debug log and extract the raw log data.

async function processChromiumDebugLog() {
const rl = readline.createInterface({
input: process.stdin,
crlfDelay: Infinity,
});

for await (const line of rl) {
const result =
/^\[.+INFO:CONSOLE\(\d+\)]\ "(?<raw_log_data>.+)",\ source:\ [^"]+?\(\d+\)$/.exec(
line
);
if (!result) {
continue;
}
const rawLogData = result.groups.raw_log_data;
process.stdout.write(`${rawLogData}${EOL}`);
}
}

processChromiumDebugLog();
94 changes: 94 additions & 0 deletions .github/actions/webgpu-validate-shader-key/validate-shader-key.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

"use strict";

const { EOL } = require("os");
const readline = require("readline");

// This script is used to parse the raw log data and check if there are inconsistent shader.
// When the shader key is the same, the shader code should be the same.

const shaderMap = new Map();

const regexShaderStart =
/^===\ WebGPU\ Shader\ code\ \[.+?Key=\"(?<key>.+)\"]\ Start\ ===$/;
const regexShaderEnd =
/^===\ WebGPU\ Shader\ code\ \[.+?Key=\"(?<key>.+)\"]\ End\ ===$/;

async function processVerboseLog() {
const rl = readline.createInterface({
input: process.stdin,
crlfDelay: Infinity,
});

let currentShaderKey = null;
let currentShaderCode = null;

for await (const line of rl) {
const resultStart = regexShaderStart.exec(line);
if (resultStart) {
if (currentShaderKey) {
throw new Error(
`Found incomplete shader code for key "${currentShaderKey}".`
);
}

currentShaderKey = resultStart.groups.key;
currentShaderCode = "";
continue;
}

const resultEnd = regexShaderEnd.exec(line);
if (resultEnd) {
if (!currentShaderKey) {
throw new Error(
`Found unexpected shader end for key "${resultEnd.groups.key}".`
);
} else if (currentShaderKey !== resultEnd.groups.key) {
throw new Error(
`Found inconsistent shader key. Expected "${currentShaderKey}", but got "${resultEnd.groups.key}".`
);
}

if (shaderMap.has(currentShaderKey)) {
if (shaderMap.get(currentShaderKey) !== currentShaderCode) {
throw new Error(`Found inconsistent shader code for key "${currentShaderKey}".
=== Previous Shader Start ===
${shaderMap.get(currentShaderKey)}
=== Previous Shader End ===

=== Current Shader Start ===
${currentShaderCode}
=== Current Shader End ===`);
}
} else {
shaderMap.set(currentShaderKey, currentShaderCode);
}

currentShaderKey = null;
currentShaderCode = null;
continue;
}

if (currentShaderKey) {
currentShaderCode += line + EOL;
}
}

if (currentShaderKey) {
throw new Error(
`Found incomplete shader code for key "${currentShaderKey}".`
);
}

if (shaderMap.size === 0) {
throw new Error("No shader code found.");
}

console.log(
`All shader code is consistent. Total ${shaderMap.size} shader code found.`
);
}

processVerboseLog();
136 changes: 136 additions & 0 deletions .github/workflows/linux-wasm-ci-build-and-test-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
name: "Linux WASM CI Reusable Workflow for build and test"
description: "This is a reusable workflow for Linux WASM CI pipelines to build and test"

on:
workflow_call:
inputs:
build_config:
required: true
type: string
extra_build_args:
required: false
type: string
default: ""
skip_publish:
required: false
type: boolean
default: false
build_jsep:
required: false
type: boolean
default: false
build_webgpu:
required: false
type: boolean
default: false

jobs:
build-wasm:
runs-on: ["self-hosted", "1ES.Pool=onnxruntime-github-Ubuntu2204-AMD-CPU"]
env:
buildArch: x64
common_build_args: --parallel --use_vcpkg --use_vcpkg_ms_internal_asset_cache --config ${{ inputs.build_config }} --skip_submodule_sync --build_wasm --enable_wasm_simd --enable_wasm_threads ${{ inputs.extra_build_args }}

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.12"
architecture: ${{ env.buildArch }}

- name: Export GitHub Actions cache environment variables
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');

- name: Install EMSDK
run: |
set -ex
cd ${{ github.workspace }}/cmake/external/emsdk
./emsdk install 4.0.4
./emsdk activate 4.0.4

- name: Build and test (browser) (simd + threads)
run: |
set -e -x
source ${{ github.workspace }}/cmake/external/emsdk/emsdk_env.sh
cd '${{ github.workspace }}'
python ./tools/ci_build/build.py \
${{ env.common_build_args }} \
--build_dir ${{ github.workspace }}/build/wasm_inferencing \
--wasm_run_tests_in_browser

- name: Build (simd + threads + JSEP)
if: ${{ inputs.build_jsep == true }}
run: |
set -e -x
source ${{ github.workspace }}/cmake/external/emsdk/emsdk_env.sh
cd '${{ github.workspace }}'
python ./tools/ci_build/build.py \
${{ env.common_build_args }} \
--build_dir ${{ github.workspace }}/build/wasm_inferencing_jsep \
--use_jsep \
--use_webnn \
--target onnxruntime_webassembly \
--skip_tests

- name: Build (simd + threads + WebGPU experimental)
if: ${{ inputs.build_webgpu == true }}
run: |
set -e -x
source ${{ github.workspace }}/cmake/external/emsdk/emsdk_env.sh
cd '${{ github.workspace }}'
python ./tools/ci_build/build.py \
${{ env.common_build_args }} \
--build_dir ${{ github.workspace }}/build/wasm_inferencing_webgpu \
--use_webgpu \
--use_jsep \
--use_webnn \
--target onnxruntime_webassembly \
--skip_tests

- name: Create Artifacts
if: ${{ inputs.skip_publish != true }}
run: |
mkdir -p ${{ github.workspace }}/artifacts/wasm/
cp ${{ github.workspace }}/build/wasm_inferencing/${{ inputs.build_config }}/ort-wasm-simd-threaded.wasm ${{ github.workspace }}/artifacts/wasm/
cp ${{ github.workspace }}/build/wasm_inferencing/${{ inputs.build_config }}/ort-wasm-simd-threaded.mjs ${{ github.workspace }}/artifacts/wasm/
if [ -d ${{ github.workspace }}/build/wasm_inferencing_jsep ]; then
cp ${{ github.workspace }}/build/wasm_inferencing_jsep/${{ inputs.build_config }}/ort-wasm-simd-threaded.jsep.wasm ${{ github.workspace }}/artifacts/wasm/
cp ${{ github.workspace }}/build/wasm_inferencing_jsep/${{ inputs.build_config }}/ort-wasm-simd-threaded.jsep.mjs ${{ github.workspace }}/artifacts/wasm/
fi

- name: Create WebGPU Artifacts
if: ${{ inputs.skip_publish != true && inputs.build_webgpu == true }}
run: |
mkdir -p ${{ github.workspace }}/artifacts/wasm_webgpu/
cp ${{ github.workspace }}/build/wasm_inferencing_webgpu/${{ inputs.build_config }}/ort-wasm-simd-threaded.jsep.wasm ${{ github.workspace }}/artifacts/wasm_webgpu/
cp ${{ github.workspace }}/build/wasm_inferencing_webgpu/${{ inputs.build_config }}/ort-wasm-simd-threaded.jsep.mjs ${{ github.workspace }}/artifacts/wasm_webgpu/

- name: Upload WASM artifacts
if: ${{ inputs.skip_publish != true }}
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.build_config }}_wasm
path: ${{ github.workspace }}/artifacts/wasm

- name: Upload WebGPU artifacts
if: ${{ inputs.skip_publish != true && inputs.build_webgpu == true }}
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.build_config }}_wasm_webgpu
path: ${{ github.workspace }}/artifacts/wasm_webgpu

- name: Publish test results
if: ${{ always() && inputs.build_config == 'Debug' }}
uses: actions/upload-artifact@v4
with:
name: test-results
path: ${{ github.workspace }}/build/**/*.results.xml
Loading