9f97f3abbe
CI - Python Bindings / sdist (push) Failing after 1s
CI - Python Bindings / Build x86_64-unknown-linux-musl (push) Failing after 1s
CI / fmt (push) Failing after 1s
E2E Output Validation / compare-outputs (push) Failing after 1s
Sync Docs to Developer Hub / sync-docs (push) Failing after 1s
CI - Python Bindings / Build x86_64-unknown-linux-gnu (push) Failing after 1s
CI - WASM Bindings / Build WASM (push) Failing after 0s
CI / clippy (push) Failing after 1s
CI / build-and-test (ubuntu-latest) (push) Failing after 0s
CI - WASM Bindings / Edge runtime PDF parse test (push) Has been skipped
CI - WASM Bindings / Browser PDF parse test (push) Has been skipped
Deploy Demo to GitHub Pages / deploy (push) Failing after 1s
CI / build-docker-image (push) Failing after 3s
CI - Node Bindings / Build darwin-x64 (push) Has been cancelled
CI - Node Bindings / Test win32-x64-msvc (push) Has been cancelled
CI - Node Bindings / Build linux-arm64-gnu (push) Has been cancelled
CI - Node Bindings / Build linux-x64-gnu (push) Has been cancelled
CI - Node Bindings / Build linux-x64-musl (push) Has been cancelled
CI - Node Bindings / Build win32-arm64-msvc (push) Has been cancelled
CI - Node Bindings / Build win32-x64-msvc (push) Has been cancelled
CI - Node Bindings / Test darwin-arm64 (push) Has been cancelled
CI - Node Bindings / Test darwin-x64 (push) Has been cancelled
CI - Node Bindings / Test linux-x64-gnu (push) Has been cancelled
CI - Node Bindings / Test linux-x64-musl (push) Has been cancelled
CI - Node Bindings / Test win32-arm64-msvc (push) Has been cancelled
CI - Python Bindings / Build aarch64-pc-windows-msvc (push) Has been cancelled
CI - Python Bindings / Build x86_64-pc-windows-msvc (push) Has been cancelled
CI - Python Bindings / Build x86_64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Build aarch64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Build aarch64-unknown-linux-gnu (push) Has been cancelled
CI - Node Bindings / Build darwin-arm64 (push) Has been cancelled
CI - Python Bindings / Test x86_64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Test aarch64-apple-darwin (push) Has been cancelled
CI - Python Bindings / Test x86_64-unknown-linux-gnu (push) Has been cancelled
CI - Python Bindings / Test x86_64-unknown-linux-musl (push) Has been cancelled
CI - Python Bindings / Test aarch64-pc-windows-msvc (push) Has been cancelled
CI - Python Bindings / Test x86_64-pc-windows-msvc (push) Has been cancelled
CI / build-and-test (macos-26-intel) (push) Has been cancelled
CI / build-and-test (macos-latest) (push) Has been cancelled
CI / build-and-test (windows-11-arm) (push) Has been cancelled
CI / build-and-test (windows-latest) (push) Has been cancelled
E2E Output Validation / upload-dataset (push) Has been cancelled
343 lines
12 KiB
YAML
343 lines
12 KiB
YAML
name: CI - Python Bindings
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- "crates/**"
|
|
- "packages/python/**"
|
|
- ".github/workflows/ci-python.yml"
|
|
pull_request:
|
|
branches: [main]
|
|
paths:
|
|
- "crates/**"
|
|
- "packages/python/**"
|
|
- ".github/workflows/ci-python.yml"
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
|
|
jobs:
|
|
build:
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- os: macos-latest
|
|
target: aarch64-apple-darwin
|
|
|
|
- os: macos-26-intel
|
|
target: x86_64-apple-darwin
|
|
|
|
- os: ubuntu-latest
|
|
target: x86_64-unknown-linux-gnu
|
|
|
|
- os: ubuntu-24.04-arm
|
|
target: aarch64-unknown-linux-gnu
|
|
|
|
- os: ubuntu-latest
|
|
target: x86_64-unknown-linux-musl
|
|
musl: true
|
|
|
|
- os: windows-latest
|
|
target: x86_64-pc-windows-msvc
|
|
|
|
- os: windows-11-arm
|
|
target: aarch64-pc-windows-msvc
|
|
|
|
runs-on: ${{ matrix.os }}
|
|
name: Build ${{ matrix.target }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- uses: dtolnay/rust-toolchain@1.95.0
|
|
with:
|
|
targets: ${{ matrix.target }}
|
|
|
|
- name: Install system dependencies (Linux)
|
|
if: runner.os == 'Linux' && !matrix.musl
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y libtesseract-dev libleptonica-dev
|
|
|
|
- name: Install system dependencies (macOS)
|
|
if: runner.os == 'macOS'
|
|
run: brew install tesseract leptonica
|
|
|
|
- name: Force Ninja generator (Windows)
|
|
if: runner.os == 'Windows'
|
|
run: echo "CMAKE_GENERATOR=Ninja" >> $GITHUB_ENV
|
|
shell: bash
|
|
|
|
- uses: actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
~/.cache/pdfium-rs
|
|
~/Library/Caches/pdfium-rs
|
|
target
|
|
key: ${{ runner.os }}-${{ matrix.target }}-cargo-python-${{ hashFiles('**/Cargo.lock') }}
|
|
|
|
- name: Build wheels (glibc)
|
|
if: ${{ !matrix.musl }}
|
|
uses: PyO3/maturin-action@v1
|
|
with:
|
|
target: ${{ matrix.target }}
|
|
args: --release --out dist --find-interpreter
|
|
manylinux: 2_28
|
|
working-directory: packages/python
|
|
before-script-linux: |
|
|
if command -v yum &> /dev/null; then
|
|
yum install -y tesseract-devel leptonica-devel openssl-devel pkgconfig perl-IPC-Cmd || true
|
|
elif command -v apt-get &> /dev/null; then
|
|
apt-get update && apt-get install -y libtesseract-dev libleptonica-dev libssl-dev pkg-config || true
|
|
elif command -v apk &> /dev/null; then
|
|
apk add tesseract-ocr-dev leptonica-dev openssl-dev pkgconf || true
|
|
fi
|
|
|
|
- name: Build wheels (musl container)
|
|
if: ${{ matrix.musl }}
|
|
# Same pattern as the node musl build: run inside a vanilla
|
|
# python:3-alpine with clang+libc++ from apk. Bypasses maturin-action's
|
|
# musllinux_1_2 cross image, which causes FORTIFY_SOURCE issues with
|
|
# tesseract's C++ build and broken host-openssl for build-deps.
|
|
run: |
|
|
chmod +x scripts/build-musl-py.sh
|
|
docker run --rm \
|
|
-v "$GITHUB_WORKSPACE":/work \
|
|
-v "$HOME/.cargo/registry":/root/.cargo/registry \
|
|
-v "$HOME/.cargo/git":/root/.cargo/git \
|
|
-v "$HOME/.cache/pdfium-rs":/root/.cache/pdfium-rs \
|
|
-w /work \
|
|
python:3.12-alpine \
|
|
/work/scripts/build-musl-py.sh
|
|
# The container runs as root; fix perms so subsequent steps can
|
|
# read/upload the produced wheel.
|
|
sudo chmod -R a+rw packages/python/dist/ || true
|
|
|
|
- name: Copy pdfium dylib into wheel (Unix)
|
|
if: runner.os != 'Windows' && !matrix.musl
|
|
working-directory: packages/python
|
|
run: |
|
|
# Fix permissions from Docker/maturin build
|
|
sudo chmod -R a+rw dist/ || true
|
|
# Find pdfium in target/ (mounted from Docker) or cache
|
|
PDFIUM_LIB=$(find ../../target -name "libpdfium.so" -type f 2>/dev/null | head -1 || true)
|
|
if [ -z "$PDFIUM_LIB" ]; then
|
|
PDFIUM_LIB=$(find ~/.cache/pdfium-rs -name "libpdfium.so" -type f 2>/dev/null | head -1 || true)
|
|
fi
|
|
if [ -n "$PDFIUM_LIB" ]; then
|
|
export PDFIUM_LIB_PATH="$(dirname "$PDFIUM_LIB")"
|
|
fi
|
|
bash scripts/copy-pdfium.sh
|
|
for whl in dist/*.whl; do
|
|
python -c "
|
|
import zipfile, os, sys
|
|
whl = sys.argv[1]
|
|
if sys.platform == 'darwin':
|
|
dylib = 'libpdfium.dylib'
|
|
else:
|
|
dylib = 'libpdfium.so'
|
|
dylib_path = os.path.join('liteparse', dylib)
|
|
if not os.path.exists(dylib_path):
|
|
print(f'Warning: {dylib_path} not found, skipping')
|
|
sys.exit(0)
|
|
with zipfile.ZipFile(whl, 'a') as z:
|
|
z.write(dylib_path, f'liteparse/{dylib}')
|
|
print(f'Added {dylib} to {whl}')
|
|
" "$whl"
|
|
done
|
|
|
|
- name: Copy pdfium dylib into wheel (Windows)
|
|
if: runner.os == 'Windows'
|
|
working-directory: packages/python
|
|
shell: pwsh
|
|
run: |
|
|
$cacheBase = "$env:LOCALAPPDATA\pdfium-rs"
|
|
if (-not (Test-Path $cacheBase)) {
|
|
$cacheBase = "$env:USERPROFILE\.cache\pdfium-rs"
|
|
}
|
|
$dll = Get-ChildItem -Path $cacheBase -Recurse -Filter "pdfium.dll" | Select-Object -First 1
|
|
if (-not $dll) {
|
|
$dll = Get-ChildItem -Path "..\..\target" -Recurse -Filter "pdfium.dll" | Select-Object -First 1
|
|
}
|
|
if ($dll) {
|
|
Copy-Item $dll.FullName -Destination "liteparse\pdfium.dll"
|
|
Write-Host "Copied $($dll.FullName) -> liteparse\pdfium.dll"
|
|
foreach ($whl in Get-ChildItem dist\*.whl) {
|
|
python -c @"
|
|
import zipfile, sys, os
|
|
whl = sys.argv[1]
|
|
dll = os.path.join('liteparse', 'pdfium.dll')
|
|
if os.path.exists(dll):
|
|
with zipfile.ZipFile(whl, 'a') as z:
|
|
z.write(dll, 'liteparse/pdfium.dll')
|
|
print(f'Added pdfium.dll to {whl}')
|
|
"@ $whl.FullName
|
|
}
|
|
} else {
|
|
Write-Error "Could not find pdfium.dll"
|
|
exit 1
|
|
}
|
|
|
|
- name: Upload wheels
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: wheels-${{ matrix.target }}
|
|
path: packages/python/dist/*.whl
|
|
if-no-files-found: error
|
|
|
|
sdist:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Build sdist
|
|
uses: PyO3/maturin-action@v1
|
|
with:
|
|
command: sdist
|
|
args: --out dist
|
|
working-directory: packages/python
|
|
|
|
- name: Upload sdist
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: sdist
|
|
path: packages/python/dist/*.tar.gz
|
|
if-no-files-found: error
|
|
|
|
test:
|
|
needs: build
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- os: macos-latest
|
|
target: aarch64-apple-darwin
|
|
- os: macos-26-intel
|
|
target: x86_64-apple-darwin
|
|
- os: ubuntu-latest
|
|
target: x86_64-unknown-linux-gnu
|
|
- os: ubuntu-latest
|
|
target: x86_64-unknown-linux-musl
|
|
use-container: true
|
|
- os: windows-latest
|
|
target: x86_64-pc-windows-msvc
|
|
- os: windows-11-arm
|
|
target: aarch64-pc-windows-msvc
|
|
runs-on: ${{ matrix.os }}
|
|
name: Test ${{ matrix.target }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Download wheels
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: wheels-${{ matrix.target }}
|
|
path: dist
|
|
|
|
- name: Install wheel
|
|
if: ${{ !matrix.use-container }}
|
|
run: pip install --no-index --find-links dist liteparse
|
|
|
|
- name: Smoke test
|
|
if: ${{ !matrix.use-container }}
|
|
run: |
|
|
python -c "
|
|
from liteparse import LiteParse, ParseResult, ParsedPage, TextItem, ScreenshotResult
|
|
parser = LiteParse(ocr_enabled=False, quiet=True)
|
|
print('LiteParse loaded successfully')
|
|
print(repr(parser))
|
|
"
|
|
|
|
- name: Parse test
|
|
if: ${{ !matrix.use-container }}
|
|
run: |
|
|
python -c "
|
|
from liteparse import LiteParse
|
|
parser = LiteParse(ocr_enabled=False, quiet=True, max_pages=2)
|
|
result = parser.parse('demo/docs/apple-10k-2024.pdf')
|
|
assert result.num_pages == 2, f'Expected 2 pages, got {result.num_pages}'
|
|
assert len(result.text) > 0, 'Expected non-empty text'
|
|
page = result.get_page(1)
|
|
assert page is not None, 'Expected page 1'
|
|
assert len(page.text_items) > 0, 'Expected text items'
|
|
print(f'Parsed {result.num_pages} pages, {len(result.text)} chars')
|
|
print('Parse test passed')
|
|
"
|
|
|
|
- name: Bytes parse test
|
|
if: ${{ !matrix.use-container }}
|
|
run: |
|
|
python -c "
|
|
from liteparse import LiteParse
|
|
parser = LiteParse(ocr_enabled=False, quiet=True, max_pages=1)
|
|
with open('demo/docs/apple-10k-2024.pdf', 'rb') as f:
|
|
data = f.read()
|
|
result = parser.parse(data)
|
|
assert result.num_pages == 1
|
|
assert len(result.text) > 0
|
|
print('Bytes parse test passed')
|
|
"
|
|
|
|
- name: Screenshot test
|
|
if: ${{ !matrix.use-container }}
|
|
run: |
|
|
python -c "
|
|
from liteparse import LiteParse
|
|
parser = LiteParse(ocr_enabled=False, quiet=True)
|
|
screenshots = parser.screenshot('demo/docs/apple-10k-2024.pdf', page_numbers=[1])
|
|
assert len(screenshots) == 1
|
|
assert screenshots[0].page_num == 1
|
|
assert len(screenshots[0].image_bytes) > 0
|
|
print(f'Screenshot: {screenshots[0].width}x{screenshots[0].height}, {len(screenshots[0].image_bytes)} bytes')
|
|
print('Screenshot test passed')
|
|
"
|
|
|
|
- name: CLI test
|
|
if: ${{ !matrix.use-container }}
|
|
run: |
|
|
lit --version
|
|
lit parse demo/docs/apple-10k-2024.pdf --no-ocr --max-pages 1 -q | head -5
|
|
echo "CLI test passed"
|
|
|
|
- name: Test in musl container
|
|
if: ${{ matrix.use-container }}
|
|
run: |
|
|
docker run --rm \
|
|
-v "$GITHUB_WORKSPACE":/work \
|
|
-w /work \
|
|
python:3.12-alpine \
|
|
sh -c '
|
|
pip install --no-index --find-links dist liteparse
|
|
python -c "
|
|
from liteparse import LiteParse, ParseResult, ParsedPage, TextItem, ScreenshotResult
|
|
parser = LiteParse(ocr_enabled=False, quiet=True)
|
|
print(repr(parser))
|
|
print(\"LiteParse loaded successfully\")
|
|
"
|
|
python -c "
|
|
from liteparse import LiteParse
|
|
parser = LiteParse(ocr_enabled=False, quiet=True, max_pages=2)
|
|
result = parser.parse(\"demo/docs/apple-10k-2024.pdf\")
|
|
assert result.num_pages == 2, f\"Expected 2 pages, got {result.num_pages}\"
|
|
assert len(result.text) > 0, \"Expected non-empty text\"
|
|
print(f\"Parsed {result.num_pages} pages, {len(result.text)} chars\")
|
|
print(\"Parse test passed\")
|
|
"
|
|
lit --version
|
|
lit parse demo/docs/apple-10k-2024.pdf --no-ocr --max-pages 1 -q | head -5
|
|
echo "CLI test passed"
|
|
'
|