Skip to content

wasm: one demo page with the whole pipeline (#157) #551

wasm: one demo page with the whole pipeline (#157)

wasm: one demo page with the whole pipeline (#157) #551

Workflow file for this run

name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
# Manual re-run from the Actions tab / `gh workflow run`. Left blank it runs lint
# + test only; set `force_version` to (re)publish that exact version — use it to
# release a version a failed/blocked run skipped (e.g. the master-ruleset reject).
workflow_dispatch:
inputs:
force_version:
description: "Force a release at this exact version (e.g. 0.3.0). Blank = lint+test only, no publish."
required: false
default: ""
# Cancel superseded PR runs, but never interrupt a master run (it may publish).
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
CARGO_TERM_COLOR: always
# Keep target/ small enough to cache and restore: no incremental artifacts
# (useless for one-shot CI builds, gigabytes on disk) and no debuginfo in
# dev/test binaries (the bulk of a debug target; tests behave the same,
# backtraces just lose line numbers). Without this the cached target/ grew
# past 6 GB compressed (~20 GB unpacked) and cache *restore* started dying
# with ENOSPC on the runner disk — which is also why every cache key below
# carries a `v2` generation: the old oversized entries must never match.
CARGO_INCREMENTAL: "0"
CARGO_PROFILE_DEV_DEBUG: "0"
CARGO_PROFILE_TEST_DEBUG: "0"
# Rust version for the style/lint gates. Pinned on purpose: rustfmt and clippy
# change their output/lints between releases, so a floating `stable` would let
# a new toolchain fail CI on otherwise-unrelated commits. Bump this manually
# (and fix any new findings in the same PR) when you want to adopt a newer one.
LINT_TOOLCHAIN: "1.96.0"
jobs:
lint:
name: fmt · clippy (pinned)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
# rustup is preinstalled on GitHub-hosted runners; use it directly rather
# than a marketplace toolchain action (the docling-project org allowlists
# only GitHub-owned / vetted actions).
- name: Install Rust ${{ env.LINT_TOOLCHAIN }}
run: |
rustup toolchain install ${{ env.LINT_TOOLCHAIN }} --profile minimal --component rustfmt --component clippy
rustup default ${{ env.LINT_TOOLCHAIN }}
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
key: ${{ runner.os }}-cargo-v2-lint-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-v2-lint-
- name: Format
run: cargo fmt --all --check
- name: Clippy
run: cargo clippy --workspace --all-targets -- -D warnings
# actions/cache saves target/ in its post-step, i.e. after this runs:
# drop what is cheap to recreate (incremental data shouldn't exist with
# CARGO_INCREMENTAL=0; examples and final bin-crate executables relink
# in seconds — the compiled library deps are what's worth keeping), so
# the cache stays well under the runner's disk headroom.
- name: Trim target before cache save
if: always()
shell: bash
run: |
rm -rf target/debug/examples target/debug/incremental \
target/release/examples target/release/incremental
find target/debug target/release -maxdepth 1 -type f ! -name '*.d' -size +5M -exec rm -f {} + 2>/dev/null || true
test:
name: test (stable)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
# Tests stay on the current stable so they catch real compiler breakage.
- name: Install Rust (stable)
run: |
rustup toolchain install stable --profile minimal
rustup default stable
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
key: ${{ runner.os }}-cargo-v2-test-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-v2-test-
# Pure-Rust unit tests + the output-regression suite. The PDF/image/METS
# snapshot tests need pdfium + the ONNX models, so they are NOT run here —
# this job downloads no ML models and stays fast. --locked doubles as the
# guard that keeps the committed Cargo.lock in sync with the manifests
# (a Cargo.toml change without its lock update fails here).
- name: Test
run: cargo test --workspace --locked
# actions/cache saves target/ in its post-step, i.e. after this runs:
# drop what is cheap to recreate (incremental data shouldn't exist with
# CARGO_INCREMENTAL=0; examples and final bin-crate executables relink
# in seconds — the compiled library deps are what's worth keeping), so
# the cache stays well under the runner's disk headroom.
- name: Trim target before cache save
if: always()
shell: bash
run: |
rm -rf target/debug/examples target/debug/incremental \
target/release/examples target/release/incremental
find target/debug target/release -maxdepth 1 -type f ! -name '*.d' -size +5M -exec rm -f {} + 2>/dev/null || true
# Validate the declared `rust-version` floors against the committed
# Cargo.lock (rust-lang.org/2023/08/29/committing-lockfiles: a committed
# lock is what makes MSRV checkable at all). Two tiers: docling-core keeps a
# lower floor (no ort/pdfium), the rest of the workspace sits at the ort
# floor. Keep these toolchains in sync with the `rust-version` values in the
# manifests — a weekly dependency-update PR that pulls a dep with a higher
# floor turns this job red, forcing a conscious pin or floor bump.
msrv:
name: msrv (1.85 core · 1.88 workspace)
runs-on: ubuntu-latest
env:
MSRV_CORE: "1.85"
MSRV_WORKSPACE: "1.88"
steps:
- uses: actions/checkout@v7
- name: Install Rust ${{ env.MSRV_CORE }} + ${{ env.MSRV_WORKSPACE }}
run: rustup toolchain install ${{ env.MSRV_CORE }} ${{ env.MSRV_WORKSPACE }} --profile minimal
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
key: ${{ runner.os }}-cargo-v2-msrv-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-v2-msrv-
- name: Check docling-core on ${{ env.MSRV_CORE }}
run: cargo +${{ env.MSRV_CORE }} check -p docling-core --locked
- name: Check workspace on ${{ env.MSRV_WORKSPACE }}
run: cargo +${{ env.MSRV_WORKSPACE }} check --workspace --locked
# actions/cache saves target/ in its post-step, i.e. after this runs:
# drop what is cheap to recreate (incremental data shouldn't exist with
# CARGO_INCREMENTAL=0; examples and final bin-crate executables relink
# in seconds — the compiled library deps are what's worth keeping), so
# the cache stays well under the runner's disk headroom.
- name: Trim target before cache save
if: always()
shell: bash
run: |
rm -rf target/debug/examples target/debug/incremental \
target/release/examples target/release/incremental
find target/debug target/release -maxdepth 1 -type f ! -name '*.d' -size +5M -exec rm -f {} + 2>/dev/null || true
# Build-verify the GPU execution-provider features (#74) without GPU
# runners: `cargo check` proves the feature plumbing and the EP registration
# code compile against the matching `ort` feature set. Each provider is
# checked on the platform its ONNX Runtime binaries exist for — actually
# *running* on GPU is out of scope for CI (no GPU hardware here).
ep-features:
name: check (${{ matrix.features }}, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# `slug` is the features list with `-` instead of `,` — actions/cache
# rejects keys containing commas.
include:
- os: ubuntu-latest
features: cuda
slug: cuda
- os: ubuntu-latest
features: cuda,tensorrt
slug: cuda-tensorrt
- os: macos-latest
features: coreml
slug: coreml
- os: windows-latest
features: directml
slug: directml
steps:
- uses: actions/checkout@v7
- name: Install Rust (stable)
run: |
rustup toolchain install stable --profile minimal
rustup default stable
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
key: ${{ runner.os }}-cargo-v2-ep-${{ matrix.slug }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-v2-ep-${{ matrix.slug }}-
- name: Check docling-cli with GPU features
run: cargo check -p docling-cli --features "${{ matrix.features }}"
# docling-rag mirrors the same EP features (ingest conversion + the
# onnx-embed embedder session route through docling-pdf's ep module).
- name: Check docling-rag with GPU features
run: cargo check -p docling-rag --features "${{ matrix.features }},onnx-embed"
- name: Test docling-pdf EP selection with GPU features
run: 'cargo test -p docling-pdf --lib --features "${{ matrix.features }}" ep::'
# actions/cache saves target/ in its post-step, i.e. after this runs:
# drop what is cheap to recreate (incremental data shouldn't exist with
# CARGO_INCREMENTAL=0; examples and final bin-crate executables relink
# in seconds — the compiled library deps are what's worth keeping), so
# the cache stays well under the runner's disk headroom.
- name: Trim target before cache save
if: always()
shell: bash
run: |
rm -rf target/debug/examples target/debug/incremental \
target/release/examples target/release/incremental
find target/debug target/release -maxdepth 1 -type f ! -name '*.d' -size +5M -exec rm -f {} + 2>/dev/null || true
# The declarative converters compile for the browser (#79): check the
# feature-sliced `docling` on wasm32, build the wasm-bindgen crate, and log
# the gzipped module size (the number docs/README quote — ~1.9 MB).
wasm:
name: wasm32 (declarative converters)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Install Rust (stable + wasm32 target)
run: |
rustup toolchain install stable --profile minimal
rustup default stable
rustup target add wasm32-unknown-unknown
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
key: ${{ runner.os }}-cargo-v2-wasm-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-v2-wasm-
- name: Check docling (no default features) on wasm32
run: cargo check -p docling --no-default-features --target wasm32-unknown-unknown --locked
- name: Check docling (pdf-text) on wasm32
run: cargo check -p docling --no-default-features --features pdf-text --target wasm32-unknown-unknown --locked
- name: Build docling-wasm (release)
run: cargo build -p docling-wasm --target wasm32-unknown-unknown --release --locked
- name: Host-side tests
run: cargo test -p docling-wasm --locked
- name: Module size
run: |
wasm=target/wasm32-unknown-unknown/release/docling_wasm.wasm
gzip -9 -c "$wasm" | wc -c | xargs -I{} echo "docling_wasm.wasm: $(stat -c%s "$wasm") bytes raw, {} bytes gzipped"
# actions/cache saves target/ in its post-step, i.e. after this runs:
# drop what is cheap to recreate (incremental data shouldn't exist with
# CARGO_INCREMENTAL=0; examples and final bin-crate executables relink
# in seconds — the compiled library deps are what's worth keeping), so
# the cache stays well under the runner's disk headroom.
- name: Trim target before cache save
if: always()
shell: bash
run: |
rm -rf target/debug/examples target/debug/incremental \
target/release/examples target/release/incremental
find target/debug target/release -maxdepth 1 -type f ! -name '*.d' -size +5M -exec rm -f {} + 2>/dev/null || true
publish:
name: release & publish
needs: [lint, test]
if: >-
(github.event_name == 'push' && github.ref == 'refs/heads/master') ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.force_version != '')
runs-on: ubuntu-latest
permissions:
contents: write # push the version-bump commit + tag
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0 # full history + tags, for the semantic version bump
# Push the release commit + tag as an admin PAT so it satisfies the
# `master` ruleset; the default GITHUB_TOKEN can't be granted a bypass.
token: ${{ secrets.RELEASE_PAT }}
- name: Install Rust (stable)
run: |
rustup toolchain install stable --profile minimal
rustup default stable
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
target
key: ${{ runner.os }}-cargo-v2-publish-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-v2-publish-
# Decide the next version from conventional commits since the last tag (or
# use force_version on a manual dispatch); if there is one, bump + commit +
# tag it, push to master, and publish the crates (in dependency order,
# skipping any already on crates.io). A no-op when no release-worthy commit
# landed. The release commit is pushed with RELEASE_PAT (an admin token, so
# it satisfies the master ruleset) and carries [skip ci], so it does not
# re-trigger CI.
- name: Release & publish
id: release
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
FORCE_VERSION: ${{ github.event.inputs.force_version }}
run: bash scripts/ci/release.sh
# Cut a GitHub Release for the tag release.sh just pushed, using the commit
# list it assembled as the description. Skipped when the release step was a
# no-op (no release-worthy commits). Idempotent: if a release for the tag
# already exists (e.g. a forced re-publish), refresh its notes instead of
# failing.
- name: Create GitHub Release
if: steps.release.outputs.released == 'true'
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT }}
run: |
tag="v${{ steps.release.outputs.version }}"
notes="${{ steps.release.outputs.notes_file }}"
if gh release view "$tag" >/dev/null 2>&1; then
gh release edit "$tag" --title "$tag" --notes-file "$notes"
else
gh release create "$tag" --title "$tag" --notes-file "$notes" --verify-tag
fi
# actions/cache saves target/ in its post-step, i.e. after this runs:
# drop what is cheap to recreate (incremental data shouldn't exist with
# CARGO_INCREMENTAL=0; examples and final bin-crate executables relink
# in seconds — the compiled library deps are what's worth keeping), so
# the cache stays well under the runner's disk headroom.
- name: Trim target before cache save
if: always()
shell: bash
run: |
rm -rf target/debug/examples target/debug/incremental \
target/release/examples target/release/incremental
find target/debug target/release -maxdepth 1 -type f ! -name '*.d' -size +5M -exec rm -f {} + 2>/dev/null || true