A GitHub Actions workflow that builds DuckDB WASM wheels for Pyodide since DuckDB dropped their pyodide build (see duckdb/duckdb-pyodide#7). Original support for Pyodide was added in duckdb/duckdb#11531.
Edit the matrix.include list in .github/workflows/build.yml — only exact version tags from duckdb/duckdb-python are supported (e.g. v1.4.4), not branch names. Each pyodide version requires a specific Python version; check the pyodide changelog when adding new entries.
matrix:
include:
- duckdb_version: 'v1.5.0'
pyodide_version: '0.29.3'
python_version: '3.13'A single build workflow runs on every push (except README-only changes) and on manual dispatch:
- Non-main branches — builds, runs tests, and uploads wheels as artifacts (90-day retention).
mainbranch — builds, runs tests, and publishes wheels as GitHub releases taggedduckdb-vx.x.x-pyodide-x.x.x.
The build takes ~20 minutes per combination since it compiles DuckDB from source with Emscripten. Extension downloading does not work in the Pyodide runtime; built-in extensions (json, parquet, icu, core_functions) are bundled.
Verifies core functionality (platform detection, table CRUD, pandas integration). Must pass for the build to succeed. Taken over from pyodide-recipes.
uv run -m http.serverNow open http://localhost:8000/test-smoke.html.
wasm-dist/ must contain the built wheel.
Runs ~180 test files from duckdb/duckdb-python tests/fast/ via pytest inside Pyodide. Results are informational only and do not block the build.
uv run -m http.serverNow open http://localhost:8000/test-official.html.
wasm-dist/ must contain the built wheel (only 1 wheel).
test-official.html: 2490 passed, 307 skipped, 8 xfailed, 3 xpassed.
Tests that are excluded from the run (incompatible with Pyodide):
| Category | Reason |
|---|---|
Threading (test_6584, test_parallel, test_alex_multithread, test_multithread, test_query_progress) |
can't start new thread — no threading in Pyodide |
ADBC (test_adbc) |
ADBC driver not available in wasm |
Spark (test_spark) |
PySpark not available in Pyodide |
fsspec / httpfs (test_fsspec, test_read_csv_httpfs) |
fsspec not available in Pyodide; httpfs extension not bundled |
Subprocess (test_startup, test_connection_interruption, test_arrow_stream_scan) |
emscripten does not support processes |
PyTorch / TensorFlow (test_torch, test_tf) |
Not available in Pyodide |
psutil (test_query_profiler) |
Not available in Pyodide |
Profiler (test_profiler) |
ZeroDivisionError in HTML rendering on wasm |
Incompatible pytest API (test_json_logging) |
Uses pytest.raises(check=...) added in pytest 8.4; Pyodide ships an older version |
Windows-only (test_windows_path) |
N/A in wasm |
The test files in tests/ are copied from duckdb/duckdb-python. To update them, copy the tests/conftest.py and tests/fast/ directory from the matching duckdb-python tag. Tests that are incompatible with Pyodide (threading, filesystem, ADBC, Spark, fsspec, pytorch, tensorflow) are excluded in the TEST_FILES list in test-official.html.
The build workflow clones duckdb/duckdb-python with its duckdb submodule, applies a few patches, then runs pyodide build --exports=whole_archive.
-
Missing cross-build env URL — the new
duckdb-pythonrepo is missing the pyodide xbuildenv URL that was added to the old repo via duckdb/duckdb#18183 but never carried over. Passed via theDEFAULT_CROSS_BUILD_ENV_URLenvironment variable at build time. -
wheel<0.44required —auditwheel-emscripten(pulled in bypyodide-build) callswheel.cliwhich was removed in wheel 0.44. -
Full git clone required —
setuptools_scmneeds git tags to produce a valid version string. Shallow clones fall back to0.0.1.dev1, which duckdb's custom version scheme rejects withValueError: Invalid version format. -
CMakeLists.txtlinker flag incompatibility — theelseif(UNIX AND NOT APPLE)branch passes--export-dynamic-symbolto the linker, which is a GNU ld flag unknown towasm-ld. Since Emscripten setsUNIX=1, this branch fires. Patched viascripts/patch_cmake.pyto add anelseif(EMSCRIPTEN)no-op guard before it. -
Dirty working tree breaks versioning — patching
CMakeLists.txtmarks tracked files as modified, sosetuptools_scmseesdistance=0, dirty=Trueand falls through to_bump_dev_version, which rejects a distance of 0. Fixed by settingOVERRIDE_GIT_DESCRIBEto the git tag (e.g.v1.4.4), which duckdb's own version scheme uses to bypasssetuptools_scmdetection entirely. Only exact version tags are supported asduckdb_version; branch names likemainwill not produce a valid version.
This repo was developed with Claude Code (claude-sonnet-4-6).