diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 43dd092..264ea5c 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -18,7 +18,77 @@ permissions: id-token: write jobs: + build-extension: + uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v1.5.5 + with: + duckdb_version: v1.5.5 + ci_tools_version: v1.5.5 + extension_name: onager + enable_rust: true + upload_all_extensions: true + exclude_archs: "linux_amd64;linux_arm64;linux_amd64_musl;linux_arm64_musl;osx_amd64;osx_arm64;windows_amd64;windows_amd64_mingw;wasm_threads" + + build-playground: + name: Assemble playground + needs: build-extension + runs-on: ubuntu-latest + env: + DUCKDB_VERSION: v1.5.5 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download Wasm extension artifacts + uses: actions/download-artifact@v4 + with: + pattern: onager-${{ env.DUCKDB_VERSION }}-extension-wasm_* + path: wasm-extensions + + - name: Vendor DuckDB-Wasm + working-directory: web/vendor-src + run: | + set -euo pipefail + npm ci + npm run check-version -- "${DUCKDB_VERSION}" + npm run bundle + + - name: Assemble playground tree + run: | + set -euo pipefail + mkdir -p playground + cp -R web/. playground/ + rm -rf playground/vendor-src + mkdir -p playground/vendor/duckdb-wasm + cp web/vendor-src/out/duckdb-browser.mjs \ + web/vendor-src/node_modules/@duckdb/duckdb-wasm/dist/duckdb-browser-mvp.worker.js \ + web/vendor-src/node_modules/@duckdb/duckdb-wasm/dist/duckdb-mvp.wasm \ + playground/vendor/duckdb-wasm/ + printf '{"branch": "%s", "commit": "%s"}\n' \ + "${GITHUB_REF_NAME}" "${GITHUB_SHA:0:5}" > playground/build-info.json + for platform in wasm_eh wasm_mvp; do + src=$(find "wasm-extensions/onager-${DUCKDB_VERSION}-extension-${platform}" \ + -name 'onager.duckdb_extension.wasm' | head -n1) + if [ -z "$src" ]; then + echo "::error::No ${platform} onager extension found in downloaded artifacts" + exit 1 + fi + dest="playground/extensions/${DUCKDB_VERSION}/${platform}" + mkdir -p "$dest" + cp "$src" "$dest/onager.duckdb_extension.wasm" + done + echo "Assembled playground tree:" + find playground -type f | sort + + - name: Upload playground artifact + uses: actions/upload-artifact@v4 + with: + name: playground + path: playground + retention-days: 7 + if-no-files-found: error + build-docs: + needs: build-playground runs-on: ubuntu-latest steps: - name: Checkout repository @@ -43,14 +113,10 @@ jobs: source .venv/bin/activate && make docs - name: Download playground artifact - uses: dawidd6/action-download-artifact@v6 + uses: actions/download-artifact@v4 with: - workflow: web.yml - workflow_conclusion: success name: playground path: ./site/playground - if_no_artifact_found: warn - allow_forks: false - name: Upload documentation artifact uses: actions/upload-pages-artifact@v3 diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml deleted file mode 100644 index c1ed3f7..0000000 --- a/.github/workflows/web.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Publish Playground App - -on: - workflow_dispatch: - push: - branches: - - main - tags: - - 'v*' - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - build-extension: - uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v1.5.4 - with: - duckdb_version: v1.5.4 - ci_tools_version: v1.5.4 - extension_name: onager - enable_rust: true - upload_all_extensions: true - exclude_archs: "linux_amd64;linux_arm64;linux_amd64_musl;linux_arm64_musl;osx_amd64;osx_arm64;windows_amd64;windows_amd64_mingw;wasm_threads" - - assemble: - name: Assemble and upload playground - needs: build-extension - runs-on: ubuntu-latest - env: - DUCKDB_VERSION: v1.5.4 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Download Wasm extension artifacts - uses: actions/download-artifact@v4 - with: - pattern: onager-${{ env.DUCKDB_VERSION }}-extension-wasm_* - path: wasm-extensions - - - name: Vendor DuckDB-Wasm - working-directory: web/vendor-src - run: | - set -euo pipefail - npm ci - npm run bundle - - - name: Assemble playground tree - run: | - set -euo pipefail - mkdir -p playground - cp -R web/. playground/ - rm -rf playground/vendor-src - mkdir -p playground/vendor/duckdb-wasm - cp web/vendor-src/out/duckdb-browser.mjs \ - web/vendor-src/node_modules/@duckdb/duckdb-wasm/dist/duckdb-browser-mvp.worker.js \ - web/vendor-src/node_modules/@duckdb/duckdb-wasm/dist/duckdb-mvp.wasm \ - playground/vendor/duckdb-wasm/ - printf '{"branch": "%s", "commit": "%s"}\n' \ - "${GITHUB_REF_NAME}" "${GITHUB_SHA:0:5}" > playground/build-info.json - for platform in wasm_eh wasm_mvp; do - src=$(find "wasm-extensions/onager-${DUCKDB_VERSION}-extension-${platform}" \ - -name 'onager.duckdb_extension.wasm' | head -n1) - if [ -z "$src" ]; then - echo "::error::No ${platform} onager extension found in downloaded artifacts" - exit 1 - fi - dest="playground/extensions/${DUCKDB_VERSION}/${platform}" - mkdir -p "$dest" - cp "$src" "$dest/onager.duckdb_extension.wasm" - done - echo "Assembled playground tree:" - find playground -type f | sort - - - name: Upload playground artifact - uses: actions/upload-artifact@v4 - with: - name: playground - path: playground - retention-days: 7 - if-no-files-found: error diff --git a/AGENTS.md b/AGENTS.md index 495a2f0..e6fa8d2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,7 +61,7 @@ Priorities, in order: - `.github/workflows/tests.yml`: Rust tests and SQL tests in CI. - `.github/workflows/lints.yml`: Rust formatting and clippy checks in CI. - `.github/workflows/dist_pipeline.yml`: cross-platform extension packaging against DuckDB `main` and `v1.5.4`. -- `.github/workflows/docs.yml`: MkDocs site build. +- `.github/workflows/docs.yml`: MkDocs site build, playground app assembly, and GitHub Pages deployment. ## Architecture Notes diff --git a/mkdocs.yml b/mkdocs.yml index 2a2c1dd..d430e82 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,7 +9,6 @@ theme: logo: assets/logo.svg favicon: assets/logo.svg palette: - # Light mode - media: "(prefers-color-scheme: light)" scheme: default primary: deep purple @@ -17,7 +16,6 @@ theme: toggle: icon: material/weather-night name: Switch to dark mode - # Dark mode - media: "(prefers-color-scheme: dark)" scheme: slate primary: deep purple @@ -118,7 +116,6 @@ nav: - API Reference: - SQL Functions: reference/sql-functions.md - Input Formats: reference/input-formats.md - # Playground app (built by web.yml and served at /onager/playground/) - Playground: https://cogitatortech.github.io/onager/playground/ markdown_extensions: diff --git a/web/app.js b/web/app.js index d574f25..b78af38 100644 --- a/web/app.js +++ b/web/app.js @@ -5,7 +5,7 @@ // playground workflow builds from the npm package pinned in web/vendor-src. // Keep this version in sync with web/vendor-src/package.json; it is only used // for the jsdelivr fallback on local checkouts that lack the vendor folder. -const DUCKDB_WASM_VERSION = "1.33.1-dev57.0"; +const DUCKDB_WASM_VERSION = "1.33.1-dev64.0"; let duckdb = null; @@ -531,13 +531,16 @@ let ticks = 0; let graphData = { nodes: [], links: [] }; let draggedNode = null; -// Determine local vs remote extension repository URL -async function getExtensionRepository() { +// Determine local vs remote extension repository URL. The repository layout is +// ///onager.duckdb_extension.wasm, and DuckDB +// picks the version segment from the running runtime, so the probe below has to +// use the runtime's own version instead of a hardcoded one. +async function getExtensionRepository(duckdbVersion) { const localRepo = new URL("extensions", document.baseURI).href; const isLocal = window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1"; if (isLocal) { try { - const testUrl = `${localRepo}/v1.5.4/wasm_mvp/onager.duckdb_extension.wasm`; + const testUrl = `${localRepo}/${duckdbVersion}/wasm_mvp/onager.duckdb_extension.wasm`; const res = await fetch(testUrl, { method: "HEAD" }); if (res.ok) { console.log("Using local extension repository:", localRepo); @@ -642,14 +645,15 @@ async function init() { }); conn = await db.connect(); - // Load Onager - const repo = await getExtensionRepository(); + // Load Onager. The extension repository holds one directory per DuckDB + // version, so the running version decides which build gets fetched. + const duckdbVersion = await scalar("select version() as v;", "v"); + const repo = await getExtensionRepository(duckdbVersion); await conn.query(`set custom_extension_repository = '${repo}';`); await conn.query(`install onager;`); await conn.query(`load onager;`); const version = await scalar("select onager_version() as v;", "v"); - const duckdbVersion = await scalar("select version() as v;", "v"); updateFooterVersions(duckdbVersion, version, await getBuildLabel()); await conn.query(SAMPLE_EDGES); diff --git a/web/vendor-src/check-duckdb-version.mjs b/web/vendor-src/check-duckdb-version.mjs new file mode 100644 index 0000000..f95dc7b --- /dev/null +++ b/web/vendor-src/check-duckdb-version.mjs @@ -0,0 +1,44 @@ +// Asserts that the vendored DuckDB-Wasm runtime reports the DuckDB version the +// playground ships extensions for. DuckDB resolves extensions as +// ///onager.duckdb_extension.wasm, so a +// runtime that disagrees with the extension build requests a directory that does +// not exist, and the playground fails to start with an unrelated-looking error. +// +// Usage: node check-duckdb-version.mjs v1.5.5 + +import { createRequire } from "node:module"; +import path from "node:path"; + +const require = createRequire(import.meta.url); +const entry = "@duckdb/duckdb-wasm/dist/duckdb-node-blocking.cjs"; +const duckdb = require(entry); +const distDir = path.dirname(require.resolve(entry)); + +const expected = process.argv[2]; +if (!expected) { + console.error("usage: node check-duckdb-version.mjs "); + process.exit(2); +} + +const bundles = { + mvp: { mainModule: path.join(distDir, "duckdb-mvp.wasm"), mainWorker: null }, + eh: { mainModule: path.join(distDir, "duckdb-eh.wasm"), mainWorker: null }, +}; + +const db = await duckdb.createDuckDB(bundles, new duckdb.VoidLogger(), duckdb.NODE_RUNTIME); +await db.instantiate(); +const conn = db.connect(); +const actual = conn.query("select version() as v;").get(0).v; +conn.close(); + +if (actual !== expected) { + console.error( + `DuckDB version mismatch: the vendored DuckDB-Wasm runtime reports ${actual}, but the ` + + `playground ships extensions built for ${expected}. Update @duckdb/duckdb-wasm in ` + + "web/vendor-src/package.json and DUCKDB_WASM_VERSION in web/app.js to the release that " + + "matches the extension build." + ); + process.exit(1); +} + +console.log(`The vendored DuckDB-Wasm runtime reports ${actual}, matching the extension build.`); diff --git a/web/vendor-src/package-lock.json b/web/vendor-src/package-lock.json index 124205f..2fe3f0b 100644 --- a/web/vendor-src/package-lock.json +++ b/web/vendor-src/package-lock.json @@ -6,16 +6,16 @@ "": { "name": "onager-playground-vendor", "dependencies": { - "@duckdb/duckdb-wasm": "1.33.1-dev57.0" + "@duckdb/duckdb-wasm": "1.33.1-dev64.0" }, "devDependencies": { "esbuild": "0.25.5" } }, "node_modules/@duckdb/duckdb-wasm": { - "version": "1.33.1-dev57.0", - "resolved": "https://registry.npmjs.org/@duckdb/duckdb-wasm/-/duckdb-wasm-1.33.1-dev57.0.tgz", - "integrity": "sha512-ndn5l2EHq1PEMvCg8G6lRV0vjhSUcbqdM/niNBTTqH4/PyyUUVKbge0uUyCnj27b9SF/CiqBwDsgKLTqBvaxdw==", + "version": "1.33.1-dev64.0", + "resolved": "https://registry.npmjs.org/@duckdb/duckdb-wasm/-/duckdb-wasm-1.33.1-dev64.0.tgz", + "integrity": "sha512-NwJipYVG5B1lyPzvxfy7y1UldrezQDRyblKxu/DQqIVttuCU2yIsbNEJ0tvhMXyS1/7+m1vXildzhO+q2vqitg==", "license": "MIT", "dependencies": { "apache-arrow": "^17.0.0", diff --git a/web/vendor-src/package.json b/web/vendor-src/package.json index ab60f3d..006ffad 100644 --- a/web/vendor-src/package.json +++ b/web/vendor-src/package.json @@ -3,10 +3,11 @@ "private": true, "description": "Builds the self-hosted DuckDB-Wasm bundle for the playground. The version must match DUCKDB_WASM_VERSION in web/app.js.", "scripts": { - "bundle": "esbuild entry.js --bundle --minify --format=esm --outfile=out/duckdb-browser.mjs" + "bundle": "esbuild entry.js --bundle --minify --format=esm --outfile=out/duckdb-browser.mjs", + "check-version": "node check-duckdb-version.mjs" }, "dependencies": { - "@duckdb/duckdb-wasm": "1.33.1-dev57.0" + "@duckdb/duckdb-wasm": "1.33.1-dev64.0" }, "devDependencies": { "esbuild": "0.25.5"