Skip to content
Merged
Show file tree
Hide file tree
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
16 changes: 15 additions & 1 deletion .github/workflows/core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,32 @@ jobs:
runner: macos-15
executable_suffix: ""
binary_dir: build-ci/bin
cmake_arch: ""
- id: macos-x64
runner: macos-15-intel
executable_suffix: ""
binary_dir: build-ci/bin
cmake_arch: ""
- id: windows-x64
runner: windows-2022
executable_suffix: ".exe"
binary_dir: build-ci/bin/Release
cmake_arch: x64
- id: windows-arm64
runner: windows-11-arm
executable_suffix: ".exe"
binary_dir: build-ci/bin/Release
cmake_arch: ARM64
- id: linux-x64
runner: ubuntu-24.04
executable_suffix: ""
binary_dir: build-ci/bin
cmake_arch: ""
- id: linux-arm64
runner: ubuntu-24.04-arm
executable_suffix: ""
binary_dir: build-ci/bin
cmake_arch: ""
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
Expand All @@ -54,7 +68,7 @@ jobs:
- name: Configure without dependency network access (Windows)
if: runner.os == 'Windows'
shell: bash
run: cmake -S . -B build-ci -G "Visual Studio 17 2022" -A x64 -DLIGHT_OCR_DEPENDENCY_CACHE_DIR="$PWD/.cache/dependencies"
run: cmake -S . -B build-ci -G "Visual Studio 17 2022" -A ${{ matrix.cmake_arch }} -DLIGHT_OCR_DEPENDENCY_CACHE_DIR="$PWD/.cache/dependencies"
- name: Build
run: cmake --build build-ci --config Release --parallel
- name: Test real PP-OCRv6 models
Expand Down
36 changes: 31 additions & 5 deletions .github/workflows/npm-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ on:
workflow_dispatch:
inputs:
version:
description: Lockstep version for all six packages
description: Lockstep version for all release packages
required: true
default: 0.3.0
default: 0.3.1
type: string
publish_to_registry:
description: Publish the fully gated candidate to npm after preflight
Expand Down Expand Up @@ -67,10 +67,20 @@ jobs:
runner: ubuntu-24.04
executable_suffix: ""
runtime_flavor: webgpu
- id: linux-arm64
runner: ubuntu-24.04-arm
executable_suffix: ""
runtime_flavor: cpu
- id: windows-x64
runner: windows-2022
executable_suffix: ".exe"
runtime_flavor: webgpu
cmake_arch: x64
- id: windows-arm64
runner: windows-11-arm
executable_suffix: ".exe"
runtime_flavor: cpu
cmake_arch: ARM64
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
Expand Down Expand Up @@ -146,7 +156,7 @@ jobs:
if: runner.os == 'Windows'
shell: pwsh
run: >-
cmake -S . -B build-npm -G "Visual Studio 17 2022" -A x64
cmake -S . -B build-npm -G "Visual Studio 17 2022" -A ${{ matrix.cmake_arch }}
"-DLIGHT_OCR_DEPENDENCY_CACHE_DIR=$env:GITHUB_WORKSPACE/.cache/dependencies"
-DLIGHT_OCR_ONNXRUNTIME_FLAVOR=${{ matrix.runtime_flavor }}
"-DLIGHT_OCR_WEBGPU_SDK_DIR=$env:GITHUB_WORKSPACE/dist/webgpu-sdk/${{ matrix.id }}"
Expand Down Expand Up @@ -286,7 +296,7 @@ jobs:
python tools/webgpu/package_bundle.py
python tools/apple/package_bundle.py \
--qualification-report contracts/apple-provider-baselines.json
- name: Assemble and deterministically pack six packages
- name: Assemble and deterministically pack all release packages
shell: bash
run: |
python tools/npm_release.py assemble \
Expand Down Expand Up @@ -337,6 +347,14 @@ jobs:
runner: ubuntu-24.04
node: "24"
native: linux-x64-gnu
- id: linux-arm64
runner: ubuntu-24.04-arm
node: "22"
native: linux-arm64-gnu
- id: linux-arm64
runner: ubuntu-24.04-arm
node: "24"
native: linux-arm64-gnu
- id: windows-x64
runner: windows-2022
node: "22"
Expand All @@ -345,6 +363,14 @@ jobs:
runner: windows-2022
node: "24"
native: win32-x64
- id: windows-arm64
runner: windows-11-arm
node: "22"
native: win32-arm64
- id: windows-arm64
runner: windows-11-arm
node: "24"
native: win32-arm64
runs-on: ${{ matrix.runner }}
env:
RELEASE_VERSION: ${{ inputs.version }}
Expand Down Expand Up @@ -402,7 +428,7 @@ jobs:
with:
name: light-ocr-npm-${{ inputs.version }}
path: dist/release
- name: Publish all six tarballs to an isolated registry
- name: Publish all release tarballs to an isolated registry
shell: bash
run: |
npm install --global npm@11.0.0 verdaccio@6.8.0
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

This file records user-visible changes to `light-ocr`. Published artifact details and immutable hashes remain in [`docs/releases/`](docs/releases/).

## [0.3.1] - 2026-07-21

### Added

- Added CPU-only prebuilt native runtimes for Linux arm64 (glibc) and Windows arm64, expanding the npm package set from four to six platform builds. Both new packages use the same PP-OCRv6 Small model and CPU result contract as the existing platforms.
- Added `@arcships/light-ocr-linux-arm64-gnu` and `@arcships/light-ocr-win32-arm64` as optional dependencies of the facade package, selected automatically by the Node.js loader on matching hosts.
- Added `ubuntu-24.04-arm` and `windows-11-arm` GitHub-hosted runners to the core CI and the npm release pipeline so both new builds compile, test, and pack on real arm64 hardware.

### Changed

- Extended the CMake ONNX Runtime CPU staging to select `runtimes/linux-arm64/native` and `runtimes/win-arm64/native` from the pinned NuGet package based on `CMAKE_SYSTEM_PROCESSOR`, instead of assuming x86_64.
- Made the npm release package-count invariant dynamic so adding platforms no longer requires editing a hard-coded count.

### Notes

- WebGPU acceleration remains limited to Linux x64 and Windows x64. The official ONNX Runtime WebGPU Plugin EP 0.1.0 does not ship a Linux arm64 binary, and Windows arm64 WebGPU has not passed the real-device Provider Gate required for a production-qualified release. Both arm64 packages are CPU-only in this version.

## [0.3.0] - 2026-07-19

### Added
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ if(APPLE AND (NOT DEFINED CMAKE_OSX_DEPLOYMENT_TARGET OR
"Minimum macOS deployment target" FORCE)
endif()

project(light_ocr VERSION 0.3.0 LANGUAGES CXX)
project(light_ocr VERSION 0.3.1 LANGUAGES CXX)

if(APPLE)
enable_language(OBJCXX)
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,16 @@ await engine.close();

## Platform acceleration

The npm package provides the following four builds. The default `createEngine()` call uses Auto mode:
The npm package provides the following six builds. The default `createEngine()` call uses Auto mode:

| Platform | Auto mode |
| --- | --- |
| macOS on Apple Silicon | Core ML on macOS 15+, then CPU |
| macOS on Intel | CPU |
| Linux x64 with glibc | WebGPU through Vulkan, then CPU |
| Linux arm64 with glibc | CPU |
| Windows x64 | WebGPU through D3D12, then CPU |
| Windows arm64 | CPU |

Applications that need explicit control can choose `auto`, `cpu`, `apple`, or `webgpu` through the [`execution` option](bindings/node/README.md#使用).

Expand Down
4 changes: 3 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,16 @@ await engine.close();

## 平台加速

npm 包提供以下四个平台版本。默认的 `createEngine()` 使用 Auto 模式:
npm 包提供以下六个平台版本。默认的 `createEngine()` 使用 Auto 模式:

| 平台 | Auto 模式 |
| --- | --- |
| macOS / Apple Silicon | macOS 15+ 优先使用 Core ML,然后使用 CPU |
| macOS / Intel | CPU |
| Linux x64 glibc | 通过 Vulkan 使用 WebGPU,然后使用 CPU |
| Linux arm64 glibc | CPU |
| Windows x64 | 通过 D3D12 使用 WebGPU,然后使用 CPU |
| Windows arm64 | CPU |

需要明确控制时,可以通过 [`execution` 选项](bindings/node/README.md#使用)选择 `auto`、`cpu`、`apple` 或 `webgpu`。

Expand Down
21 changes: 16 additions & 5 deletions bindings/node/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,25 @@ elseif(APPLE)
set(_light_ocr_node_platform_os "darwin")
set(_light_ocr_node_platform_arch "x86_64")
elseif(WIN32)
set(_light_ocr_node_platform_id "windows-x64")
set(_light_ocr_node_platform_os "win32")
set(_light_ocr_node_platform_arch "x86_64")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(ARM64|arm64|aarch64)$")
set(_light_ocr_node_platform_id "windows-arm64")
set(_light_ocr_node_platform_os "win32")
set(_light_ocr_node_platform_arch "arm64")
else()
set(_light_ocr_node_platform_id "windows-x64")
set(_light_ocr_node_platform_os "win32")
set(_light_ocr_node_platform_arch "x86_64")
endif()
else()
set(_light_ocr_node_platform_id "linux-x64")
set(_light_ocr_node_platform_os "linux")
set(_light_ocr_node_platform_arch "x86_64")
set(_light_ocr_node_platform_libc "glibc")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$")
set(_light_ocr_node_platform_id "linux-arm64")
set(_light_ocr_node_platform_arch "arm64")
else()
set(_light_ocr_node_platform_id "linux-x64")
set(_light_ocr_node_platform_arch "x86_64")
endif()
endif()
set(_light_ocr_node_has_apple_provider 0)
if(APPLE AND _light_ocr_node_platform_arch STREQUAL "arm64")
Expand Down
2 changes: 1 addition & 1 deletion bindings/node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ node --test --test-concurrency=1 bindings/node/test/adapter.test.cjs
# 或:ctest --test-dir build-node -R '^light_ocr_node_tests$' --output-on-failure
```

当前源码加载器不会搜索任意 cwd。开发构建优先接受 `LIGHT_OCR_NODE_BINARY`,发布构建则按 `process.platform`、`process.arch` 和 Linux libc 固定选择四个 `@arcships/light-ocr-<platform>` optional packages。facade 通过 model package 导出的 manifest 定位默认 bundle,并在进入 native addon 前核对 bundle ID。详见 [npm package 设计](../../docs/npm-packaging.md)。
当前源码加载器不会搜索任意 cwd。开发构建优先接受 `LIGHT_OCR_NODE_BINARY`,发布构建则按 `process.platform`、`process.arch` 和 Linux libc 固定选择六个 `@arcships/light-ocr-<platform>` optional packages。facade 通过 model package 导出的 manifest 定位默认 bundle,并在进入 native addon 前核对 bundle ID。详见 [npm package 设计](../../docs/npm-packaging.md)。

## 使用

Expand Down
14 changes: 14 additions & 0 deletions bindings/node/js/load-native.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function platformIdentity() {
const identities = {
'darwin-arm64': { id: 'macos-arm64', os: 'darwin', architecture: 'arm64' },
'darwin-x64': { id: 'macos-x64', os: 'darwin', architecture: 'x86_64' },
'win32-arm64': { id: 'windows-arm64', os: 'win32', architecture: 'arm64' },
'win32-x64': { id: 'windows-x64', os: 'win32', architecture: 'x86_64' },
};
if (key === 'linux-x64') {
Expand All @@ -30,6 +31,17 @@ function platformIdentity() {
key,
);
}
if (key === 'linux-arm64') {
const report = process.report?.getReport?.();
if (report?.header?.glibcVersionRuntime) {
return { id: 'linux-arm64', os: 'linux', architecture: 'arm64', libc: 'glibc' };
}
throw adapterError(
'unsupported_platform',
'light-ocr currently supports Linux arm64 with glibc only',
key,
);
}
const identity = identities[key];
if (!identity) {
throw adapterError('unsupported_platform', `light-ocr does not support ${key}`, key);
Expand All @@ -41,8 +53,10 @@ function platformPackage() {
const packages = {
'macos-arm64': '@arcships/light-ocr-darwin-arm64',
'macos-x64': '@arcships/light-ocr-darwin-x64',
'windows-arm64': '@arcships/light-ocr-win32-arm64',
'windows-x64': '@arcships/light-ocr-win32-x64',
'linux-x64': '@arcships/light-ocr-linux-x64-gnu',
'linux-arm64': '@arcships/light-ocr-linux-arm64-gnu',
};
return packages[platformIdentity().id];
}
Expand Down
2 changes: 1 addition & 1 deletion bindings/node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@arcships/light-ocr",
"version": "0.3.0",
"version": "0.3.1",
"private": true,
"description": "Node-API adapter for the light-ocr C++ core",
"license": "Apache-2.0",
Expand Down
15 changes: 14 additions & 1 deletion cmake/Dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,11 @@ function(light_ocr_configure_dependencies)
else()
set(_ort_include "${onnxruntime_package_SOURCE_DIR}/build/native/include")
if(WIN32)
set(_ort_runtime_dir "runtimes/win-x64/native")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(ARM64|arm64|aarch64)$")
set(_ort_runtime_dir "runtimes/win-arm64/native")
else()
set(_ort_runtime_dir "runtimes/win-x64/native")
endif()
set(_ort_library "${onnxruntime_package_SOURCE_DIR}/${_ort_runtime_dir}/onnxruntime.dll")
set(_ort_implib "${onnxruntime_package_SOURCE_DIR}/${_ort_runtime_dir}/onnxruntime.lib")
elseif(APPLE)
Expand All @@ -210,6 +214,15 @@ function(light_ocr_configure_dependencies)
file(CREATE_LINK "libonnxruntime.so" "${_ort_soname_library}" SYMBOLIC)
endif()
set(_ort_runtime_files "${_ort_library}" "${_ort_soname_library}")
elseif(UNIX AND CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64|ARM64)$")
set(_ort_runtime_dir "runtimes/linux-arm64/native")
set(_ort_library "${onnxruntime_package_SOURCE_DIR}/${_ort_runtime_dir}/libonnxruntime.so")
set(_ort_soname_library
"${onnxruntime_package_SOURCE_DIR}/${_ort_runtime_dir}/libonnxruntime.so.1")
if(NOT EXISTS "${_ort_soname_library}")
file(CREATE_LINK "libonnxruntime.so" "${_ort_soname_library}" SYMBOLIC)
endif()
set(_ort_runtime_files "${_ort_library}" "${_ort_soname_library}")
else()
message(FATAL_ERROR
"Unsupported ONNX Runtime target: ${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}")
Expand Down
18 changes: 9 additions & 9 deletions docs/build-and-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,18 +213,18 @@ macOS arm64 高分辨率绝对 RSS gates 由 `light_ocr_memory_gate` 独立进

`.github/workflows/core.yml` 定义三类 job:

- `tier1`:四个 Tier 1 原生 runner,锁定依赖/模型、离线缓存复核、Release 构建、真实模型测试、sterile/offline 检查、RSS gate、manifest/license/SBOM。
- `tier1`:六个 Tier 1 原生 runner,锁定依赖/模型、离线缓存复核、Release 构建、真实模型测试、sterile/offline 检查、RSS gate、manifest/license/SBOM。
- `safety`:Linux ASan+UBSan+LSan、TSan、四个 libFuzzer 入口。
- `oracle`:hash-locked Python 环境、committed corpus/golden 身份校验、同机 live oracle 的 14 个语料全阶段对齐和首 bundle 质量基线。

`.github/workflows/npm-release.yml` 是仅允许从 `main` 手动触发的发布候选与发布流程。默认 `publish_to_registry=false`,所以第一次运行不会读取 `NPM_TOKEN` 或改动 npm registry:

- 0.3.0 候选先在 Linux/Python 3.11 的哈希锁工具链中重现并校验内部 WebGPU FP16 派生工件,再在 macOS/Python 3.12 中派生固定 Core ML FP16 package hashes,最后把合并的 native superset bundle 交给 Linux assemble;WebGPU 公共执行 profile 只发布 FP32,用户安装、postinstall 和首次运行都不会执行转换或联网。
- 四个平台分别原生构建 Node-API addon,并保存许可证与 SPDX SBOM。
- 汇聚为一个 facade、一个 model 和四个 native packages,执行两次 `npm pack` 并要求 tarball SHA-256 完全一致。
- 在 macOS arm64/x64、Linux x64 glibc、Windows x64 上分别使用 Node.js 22 和 24,从本地 tarballs 执行 `--ignore-scripts` 安装、CJS/ESM bounded OCR、单次 tiled contract/结果 smoke 与 TypeScript compile test。
- 六个 tarball 先发布到一次性 Verdaccio registry,只安装 facade 后停止 registry,再执行真实 bounded 与 tiled OCR,证明没有运行时下载依赖。
- 只有以上功能/制品 gates、需要时已经单独完成的受审 baseline,以及 `publish_to_registry=true` 同时满足时,`npm-release` GitHub environment 才能读取 `NPM_TOKEN`;先发布五个依赖到 `next`,通过 registry facade 安装后再发布主包,最终禁网运行并可显式提升到 `latest`。
- 六个平台分别原生构建 Node-API addon,并保存许可证与 SPDX SBOM。
- 汇聚为一个 facade、一个 model 和六个 native packages,执行两次 `npm pack` 并要求 tarball SHA-256 完全一致。
- 在 macOS arm64/x64、Linux x64 glibc、Linux arm64 glibc、Windows x64、Windows arm64 上分别使用 Node.js 22 和 24,从本地 tarballs 执行 `--ignore-scripts` 安装、CJS/ESM bounded OCR、单次 tiled contract/结果 smoke 与 TypeScript compile test。
- 八个 tarball 先发布到一次性 Verdaccio registry,只安装 facade 后停止 registry,再执行真实 bounded 与 tiled OCR,证明没有运行时下载依赖。
- 只有以上功能/制品 gates、需要时已经单独完成的受审 baseline,以及 `publish_to_registry=true` 同时满足时,`npm-release` GitHub environment 才能读取 `NPM_TOKEN`;先发布七个依赖到 `next`,通过 registry facade 安装后再发布主包,最终禁网运行并可显式提升到 `latest`。

`0.3.0` 发布前 dry-run 的触发命令为:

Expand Down Expand Up @@ -277,16 +277,16 @@ bytes: 31334400
sha256: 74e246bf075c141da51e58515c731298fdabee9fd5bd8feb7cf6c7f4f352de17
```

npm release 按 [npm-packaging.md](npm-packaging.md) 生成一个 facade、一个 model 和四个 native staging packages。model package 保存上述归档的精确解包内容;发布候选另外记录六个 npm tarballs 的 bytes、SHA-256、npm integrity 和 registry identity,不能把 Core USTAR hash 当作 npm tarball hash。
npm release 按 [npm-packaging.md](npm-packaging.md) 生成一个 facade、一个 model 和六个 native staging packages。model package 保存上述归档的精确解包内容;发布候选另外记录八个 npm tarballs 的 bytes、SHA-256、npm integrity 和 registry identity,不能把 Core USTAR hash 当作 npm tarball hash。

## 10. 发布候选门槛

1. 在带 revision 的干净 Git snapshot 上运行全部 CI。
2. 四个 Tier 1 原生 job 全绿;不得用交叉编译替代。
2. 六个 Tier 1 原生 job 全绿;不得用交叉编译替代。
3. 保存 parity、quality、benchmark、leak、Sanitizer、fuzz 和 offline 报告。
4. 将精确 bundle 文件打入 `@arcships/light-ocr-model-ppocrv6-small`,验证 sterile install,并记录 npm tarball SHA-256/integrity。独立 USTAR mirror 是非 npm 分发项,不阻塞 npm package release。
5. 为每个平台生成 manifest、许可证清单和 SBOM。
6. 在隔离环境验证六个 npm tarballs、platform 选择、默认 `createEngine()` 和模型 payload hash;已安装后的运行测试必须禁网。
6. 在隔离环境验证八个 npm tarballs、platform 选择、默认 `createEngine()` 和模型 payload hash;已安装后的运行测试必须禁网。
7. 对照 [implementation-status.md](implementation-status.md) 关闭所有 Pending 项。

registry 发布由受保护的 `npm-release` environment 执行;workflow 成功记录、registry metadata 与安装复验才构成完成证据。签名、公证、非 npm 公共下载地址和长期保留策略仍是独立的外部事项。
Loading
Loading