docs(readme): document Claude/MCP one-click connector + correct app size #267
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main] | |
| jobs: | |
| frontend: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: npm | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Install | |
| run: npm --prefix frontend ci | |
| - name: Type-check + build | |
| run: npm --prefix frontend run build | |
| rust: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-24.04, windows-latest, macos-14] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # The full workspace (engine + duckle-runner + duckle-mcp + apps/desktop | |
| # with two embedded binaries) plus test artifacts can exceed the ~14 GB | |
| # free on a GitHub Linux runner ("No space left on device"). Drop large | |
| # preinstalled toolchains we never use to reclaim ~20 GB. | |
| - name: Free disk space (Linux) | |
| if: runner.os == 'Linux' | |
| shell: bash | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \ | |
| /opt/hostedtoolcache/CodeQL /usr/local/.ghcup /usr/local/share/boost \ | |
| /usr/local/share/powershell /usr/share/swift || true | |
| sudo docker image prune --all --force || true | |
| df -h / | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy, rustfmt | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install Linux WebView deps (Tauri) | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libwebkit2gtk-4.1-dev libgtk-3-dev \ | |
| libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev | |
| - name: Install DuckDB CLI (for engine tests) | |
| shell: bash | |
| run: | | |
| set -e | |
| ver=1.5.3 | |
| case "$RUNNER_OS" in | |
| Linux) asset=duckdb_cli-linux-amd64.zip; exe=duckdb ;; | |
| Windows) asset=duckdb_cli-windows-amd64.zip; exe=duckdb.exe ;; | |
| macOS) asset=duckdb_cli-osx-universal.zip; exe=duckdb ;; | |
| esac | |
| # Use RUNNER_TEMP (a native drive-letter path on Windows) so the | |
| # binary path resolves for the Rust test process; $HOME is an | |
| # MSYS path (/c/...) on the Windows runner and Rust cannot find it. | |
| dest="$RUNNER_TEMP/duckdb" | |
| mkdir -p "$dest" | |
| curl -L -o duckdb.zip "https://github.com/duckdb/duckdb/releases/download/v$ver/$asset" | |
| unzip -o duckdb.zip -d "$dest" | |
| bin="$dest/$exe" | |
| if [ "$RUNNER_OS" = "Windows" ]; then bin="$(cygpath -w "$bin")"; fi | |
| echo "using duckdb at: $bin" | |
| echo "DUCKLE_DUCKDB_BIN=$bin" >> "$GITHUB_ENV" | |
| - name: Pre-install DuckDB extensions | |
| shell: bash | |
| # Install once up front so the per-stage LOAD doesn't race with | |
| # parallel test threads downloading the same extension into the | |
| # shared ~/.duckdb cache. | |
| run: | | |
| "$DUCKLE_DUCKDB_BIN" :memory: -c "INSTALL postgres; INSTALL mysql; INSTALL sqlite; INSTALL excel; INSTALL httpfs; INSTALL iceberg; INSTALL delta; INSTALL ducklake; INSTALL vss; INSTALL fts; INSTALL inet;" | |
| # The desktop crate embeds duckle-runner AND duckle-mcp at compile time | |
| # via build.rs include_bytes!, so both binaries must exist before anything | |
| # that compiles apps/desktop. Build them in the dev profile so the engine | |
| # compilation is shared with the cargo test below. | |
| - name: Build + stage duckle-runner + duckle-mcp (embedded into the desktop app) | |
| shell: bash | |
| run: | | |
| set -e | |
| cargo build -p duckle-runner -p duckle-mcp | |
| mkdir -p apps/desktop/bin | |
| if [ "$RUNNER_OS" = "Windows" ]; then | |
| cp target/debug/duckle-runner.exe apps/desktop/bin/duckle-runner.exe | |
| cp target/debug/duckle-mcp.exe apps/desktop/bin/duckle-mcp.exe | |
| else | |
| cp target/debug/duckle-runner apps/desktop/bin/duckle-runner | |
| cp target/debug/duckle-mcp apps/desktop/bin/duckle-mcp | |
| fi | |
| - name: Test | |
| run: cargo test --workspace | |
| # Style checks are informational - they report problems without | |
| # failing the build. | |
| - name: Format check | |
| continue-on-error: true | |
| run: cargo fmt --all --check | |
| - name: Clippy | |
| continue-on-error: true | |
| run: cargo clippy --workspace --all-targets | |
| # Smoke build of the desktop release binary - the exact same command | |
| # the release workflow runs. Catches feature-flag regressions like | |
| # the v0.0.7 bug where `cargo build --release` (without | |
| # `--features custom-protocol`) silently shipped a binary that loaded | |
| # `http://localhost:5173` inside the Tauri WebView instead of the | |
| # embedded frontend. `cargo test --workspace` never exercises this | |
| # path, so the bug only surfaces when someone downloads + launches | |
| # the published artifact. | |
| desktop-release-build: | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Free disk space | |
| shell: bash | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \ | |
| /opt/hostedtoolcache/CodeQL /usr/local/.ghcup /usr/local/share/boost \ | |
| /usr/local/share/powershell /usr/share/swift || true | |
| sudo docker image prune --all --force || true | |
| df -h / | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: npm | |
| cache-dependency-path: frontend/package-lock.json | |
| - name: Install frontend deps | |
| run: npm --prefix frontend ci | |
| - name: Build frontend (embedded into the Rust binary) | |
| run: npm --prefix frontend run build | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install Linux WebView deps (Tauri) | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| libwebkit2gtk-4.1-dev libgtk-3-dev \ | |
| libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev | |
| # Build + stage duckle-runner + duckle-mcp first: the desktop crate embeds | |
| # both at compile time (build.rs include_bytes!). Release profile so the | |
| # engine build is shared with the desktop release build below. | |
| - name: Build + stage duckle-runner + duckle-mcp (embedded into the desktop app) | |
| run: | | |
| set -e | |
| cargo build --release -p duckle-runner -p duckle-mcp | |
| mkdir -p apps/desktop/bin | |
| cp target/release/duckle-runner apps/desktop/bin/duckle-runner | |
| cp target/release/duckle-mcp apps/desktop/bin/duckle-mcp | |
| - name: Build raw Duckle binary (release workflow command) | |
| run: cargo build --release --manifest-path apps/desktop/Cargo.toml --features custom-protocol | |
| - name: Verify embedded frontend (not devUrl) is in binary | |
| # Grep the built binary for the frontend's main JS chunk filename. | |
| # If tauri-codegen embedded devUrl instead, this string will be | |
| # absent and the check fails - catching the v0.0.7-class bug. | |
| run: | | |
| set -e | |
| asset=$(ls frontend/dist/assets/index-*.js | head -1 | xargs basename) | |
| if grep -q "$asset" target/release/duckle; then | |
| echo "OK: embedded frontend asset '$asset' is present in the binary" | |
| else | |
| echo "FAIL: frontend asset '$asset' NOT found in binary - tauri-codegen likely embedded devUrl" | |
| exit 1 | |
| fi | |
| # Live PostgreSQL integration tests. Runs against a real postgres | |
| # service container; engine tests that don't see DUCKLE_PG_HOST in the | |
| # main matrix job skip cleanly, so this is the only place they execute. | |
| postgres-integration: | |
| runs-on: ubuntu-latest | |
| services: | |
| postgres: | |
| image: pgvector/pgvector:pg16 | |
| env: | |
| POSTGRES_PASSWORD: pgtest | |
| POSTGRES_DB: postgres | |
| ports: ['5432:5432'] | |
| options: >- | |
| --health-cmd "pg_isready -U postgres" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 20 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install DuckDB CLI | |
| run: | | |
| set -e | |
| curl -L -o duckdb.zip https://github.com/duckdb/duckdb/releases/download/v1.5.3/duckdb_cli-linux-amd64.zip | |
| mkdir -p "$RUNNER_TEMP/duckdb" | |
| unzip -o duckdb.zip -d "$RUNNER_TEMP/duckdb" | |
| echo "DUCKLE_DUCKDB_BIN=$RUNNER_TEMP/duckdb/duckdb" >> "$GITHUB_ENV" | |
| - name: Pre-install postgres extension | |
| run: | | |
| "$DUCKLE_DUCKDB_BIN" :memory: -c "INSTALL postgres;" | |
| - name: Run PostgreSQL integration tests | |
| env: | |
| DUCKLE_PG_HOST: localhost | |
| DUCKLE_PG_PORT: '5432' | |
| DUCKLE_PG_DB: postgres | |
| DUCKLE_PG_USER: postgres | |
| DUCKLE_PG_PASS: pgtest | |
| run: cargo test -p duckle-duckdb-engine --test execution -- pg_ | |
| # Live MySQL integration tests (also covers MariaDB, same wire). | |
| mysql-integration: | |
| runs-on: ubuntu-latest | |
| services: | |
| mysql: | |
| image: mysql:8.0 | |
| env: | |
| MYSQL_ROOT_PASSWORD: mytest | |
| MYSQL_DATABASE: ducktest | |
| ports: ['3306:3306'] | |
| options: >- | |
| --health-cmd "mysqladmin ping -h localhost -p$$MYSQL_ROOT_PASSWORD" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 30 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Install DuckDB CLI | |
| run: | | |
| set -e | |
| curl -L -o duckdb.zip https://github.com/duckdb/duckdb/releases/download/v1.5.3/duckdb_cli-linux-amd64.zip | |
| mkdir -p "$RUNNER_TEMP/duckdb" | |
| unzip -o duckdb.zip -d "$RUNNER_TEMP/duckdb" | |
| echo "DUCKLE_DUCKDB_BIN=$RUNNER_TEMP/duckdb/duckdb" >> "$GITHUB_ENV" | |
| - name: Pre-install mysql extension | |
| run: | | |
| "$DUCKLE_DUCKDB_BIN" :memory: -c "INSTALL mysql;" | |
| - name: Run MySQL integration tests | |
| env: | |
| DUCKLE_MYSQL_HOST: 127.0.0.1 | |
| DUCKLE_MYSQL_PORT: '3306' | |
| DUCKLE_MYSQL_DB: ducktest | |
| DUCKLE_MYSQL_USER: root | |
| DUCKLE_MYSQL_PASS: mytest | |
| run: cargo test -p duckle-duckdb-engine --test execution -- mysql_ | |
| # Live MinIO integration tests (covers all S3-compatible: MinIO, R2, B2). | |
| # MinIO needs `server /data` as the container command; GitHub Actions | |
| # service containers can't override CMD, so we run it as a step. | |
| minio-integration: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - name: Start MinIO + create bucket | |
| run: | | |
| set -e | |
| docker run -d --name minio -p 9000:9000 -p 9001:9001 \ | |
| -e MINIO_ROOT_USER=minioadmin -e MINIO_ROOT_PASSWORD=minioadmin \ | |
| minio/minio:latest server /data --console-address ":9001" | |
| for i in $(seq 1 30); do | |
| if curl -fs http://localhost:9000/minio/health/live >/dev/null 2>&1; then | |
| echo "MinIO is up"; break | |
| fi | |
| sleep 2 | |
| done | |
| # mc isn't in the minio server image, so use the standalone client. | |
| curl -fsSL https://dl.min.io/client/mc/release/linux-amd64/mc -o /usr/local/bin/mc | |
| chmod +x /usr/local/bin/mc | |
| mc alias set local http://localhost:9000 minioadmin minioadmin | |
| mc mb local/duckle-test | |
| - name: Install DuckDB CLI | |
| run: | | |
| set -e | |
| curl -L -o duckdb.zip https://github.com/duckdb/duckdb/releases/download/v1.5.3/duckdb_cli-linux-amd64.zip | |
| mkdir -p "$RUNNER_TEMP/duckdb" | |
| unzip -o duckdb.zip -d "$RUNNER_TEMP/duckdb" | |
| echo "DUCKLE_DUCKDB_BIN=$RUNNER_TEMP/duckdb/duckdb" >> "$GITHUB_ENV" | |
| - name: Seed MinIO with a test object | |
| run: | | |
| set -e | |
| # Upload a small parquet file to s3://duckle-test/orders.parquet | |
| # via the DuckDB CLI itself, so the source test reads it back. | |
| "$DUCKLE_DUCKDB_BIN" :memory: -c " | |
| INSTALL httpfs; LOAD httpfs; | |
| CREATE OR REPLACE SECRET seed ( | |
| TYPE S3, | |
| KEY_ID 'minioadmin', | |
| SECRET 'minioadmin', | |
| REGION 'us-east-1', | |
| ENDPOINT 'localhost:9000', | |
| URL_STYLE 'path', | |
| USE_SSL false | |
| ); | |
| COPY (SELECT * FROM (VALUES (1,'alice'),(2,'bob'),(3,'carol')) t(id,name)) | |
| TO 's3://duckle-test/orders.parquet' (FORMAT PARQUET); | |
| " | |
| - name: Run MinIO integration tests | |
| env: | |
| DUCKLE_MINIO_HOST: localhost | |
| DUCKLE_MINIO_PORT: '9000' | |
| DUCKLE_MINIO_BUCKET: duckle-test | |
| DUCKLE_MINIO_ACCESS_KEY: minioadmin | |
| DUCKLE_MINIO_SECRET_KEY: minioadmin | |
| run: cargo test -p duckle-duckdb-engine --test execution -- minio_ |