From f8c695f5d06bf6108ce5b7dcc7fa750408e22883 Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Mon, 12 Jan 2026 17:59:00 +0100 Subject: [PATCH 01/11] add a duckdb docker image --- .github/workflows/ci.yml | 29 +++++++++++++++++++++++--- Dockerfile | 38 ++++++++++++++++++++++++++++++++-- scripts/install-duckdb-odbc.sh | 18 ++++++++++++++++ scripts/setup-sqlpage-user.sh | 10 +++++++++ 4 files changed, 90 insertions(+), 5 deletions(-) create mode 100755 scripts/install-duckdb-odbc.sh create mode 100755 scripts/setup-sqlpage-user.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54b783b0..153ae58e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,12 +121,26 @@ jobs: - linux/amd64 - linux/arm/v7 - linux/arm64 + variant: + - name: default + target: minimal + suffix: "" + - name: duckdb + target: duckdb + suffix: -duckdb + exclude: + # DuckDB ODBC is not available for armv7 + - platform: linux/arm/v7 + variant: + name: duckdb + target: duckdb + suffix: -duckdb steps: - name: Checkout uses: actions/checkout@v4 - id: suffix name: Cache name suffix - run: echo "suffix=-$(tr '/' '-' <<< ${{ matrix.platform }})" >> "$GITHUB_OUTPUT" + run: echo "suffix=-$(tr '/' '-' <<< ${{ matrix.platform }})${{ matrix.variant.suffix }}" >> "$GITHUB_OUTPUT" - name: Docker meta id: meta uses: docker/metadata-action@v5 @@ -149,6 +163,7 @@ jobs: with: context: . platforms: ${{ matrix.platform }} + target: ${{ matrix.variant.target }} labels: ${{ steps.meta.outputs.labels }} push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} @@ -179,11 +194,18 @@ jobs: if: github.event_name != 'pull_request' needs: - docker_build + strategy: + matrix: + variant: + - name: default + suffix: "" + - name: duckdb + suffix: -duckdb steps: - name: Download digests uses: actions/download-artifact@v4 with: - pattern: digests* + pattern: digests*${{ matrix.variant.suffix }} merge-multiple: true path: /tmp/digests - name: Set up Docker Buildx @@ -193,6 +215,7 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY_IMAGE }} + flavor: suffix=${{ matrix.variant.suffix }} - name: Login to Docker Hub uses: docker/login-action@v3 with: @@ -205,4 +228,4 @@ jobs: $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) - name: Inspect image run: | - docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} + docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}${{ matrix.variant.suffix }} diff --git a/Dockerfile b/Dockerfile index fb79c864..a6ceb323 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,8 @@ RUN /usr/local/bin/build-dependencies.sh COPY . . RUN /usr/local/bin/build-project.sh -FROM busybox:glibc +# Default minimal image (busybox-based) +FROM busybox:glibc AS minimal RUN addgroup --gid 1000 --system sqlpage && \ adduser --uid 1000 --system --no-create-home --ingroup sqlpage sqlpage && \ mkdir -p /etc/sqlpage && \ @@ -30,4 +31,37 @@ COPY --from=builder /tmp/sqlpage-libs/* /lib/ USER sqlpage COPY --from=builder --chown=sqlpage:sqlpage /usr/src/sqlpage/sqlpage/sqlpage.db sqlpage/sqlpage.db EXPOSE 8080 -CMD ["/usr/local/bin/sqlpage"] \ No newline at end of file +CMD ["/usr/local/bin/sqlpage"] + +# DuckDB ODBC image (debian-based with DuckDB ODBC driver) +FROM debian:trixie-slim AS duckdb + +ARG TARGETARCH +ENV SQLPAGE_WEB_ROOT=/var/www +ENV SQLPAGE_CONFIGURATION_DIRECTORY=/etc/sqlpage +ENV DATABASE_URL="Driver=/opt/duckdb_odbc/libduckdb_odbc.so;Database=/var/lib/sqlpage/duckdb.db" + +COPY scripts/install-duckdb-odbc.sh scripts/setup-sqlpage-user.sh /usr/local/bin/ + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + unzip \ + adduser \ + odbcinst \ + unixodbc \ + && /usr/local/bin/install-duckdb-odbc.sh "$TARGETARCH" \ + && apt-get purge -y --auto-remove curl unzip \ + && rm -rf /var/lib/apt/lists/* + +RUN /usr/local/bin/setup-sqlpage-user.sh + +COPY --from=builder /usr/src/sqlpage/sqlpage.bin /usr/local/bin/sqlpage + +USER sqlpage +WORKDIR /var/www +EXPOSE 8080 +CMD ["/usr/local/bin/sqlpage"] + +# Default stage +FROM minimal diff --git a/scripts/install-duckdb-odbc.sh b/scripts/install-duckdb-odbc.sh new file mode 100755 index 00000000..1b3f1fa6 --- /dev/null +++ b/scripts/install-duckdb-odbc.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -eux + +TARGETARCH="${1:-amd64}" +DUCKDB_VERSION="${2:-v1.4.3.0}" + +# Determine the correct DuckDB ODBC package for the architecture +case "$TARGETARCH" in + amd64) odbc_zip="duckdb_odbc-linux-amd64.zip" ;; + arm64) odbc_zip="duckdb_odbc-linux-arm64.zip" ;; + *) echo "Unsupported TARGETARCH: $TARGETARCH" >&2; exit 1 ;; +esac + +# Download and install DuckDB ODBC driver +curl -fsSL -o /tmp/duckdb_odbc.zip "https://github.com/duckdb/duckdb-odbc/releases/download/${DUCKDB_VERSION}/${odbc_zip}" +mkdir -p /opt/duckdb_odbc +unzip /tmp/duckdb_odbc.zip -d /opt/duckdb_odbc +rm /tmp/duckdb_odbc.zip diff --git a/scripts/setup-sqlpage-user.sh b/scripts/setup-sqlpage-user.sh new file mode 100755 index 00000000..2977adf6 --- /dev/null +++ b/scripts/setup-sqlpage-user.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -eux + +# Create sqlpage user and group +addgroup --gid 1000 --system sqlpage +adduser --uid 1000 --system --no-create-home --ingroup sqlpage sqlpage + +# Create and configure directories +mkdir -p /etc/sqlpage /var/lib/sqlpage /var/www +chown -R sqlpage:sqlpage /etc/sqlpage /var/lib/sqlpage /var/www From caedc02d5ffd1c66650038525149b8216c8d9aed Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Mon, 12 Jan 2026 18:02:53 +0100 Subject: [PATCH 02/11] Add DuckDB ODBC driver installation and configuration Installs DuckDB ODBC driver and configures system-wide ODBC settings to enable DuckDB database connectivity for SQLPage. --- scripts/install-duckdb-odbc.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/scripts/install-duckdb-odbc.sh b/scripts/install-duckdb-odbc.sh index 1b3f1fa6..19a6225c 100755 --- a/scripts/install-duckdb-odbc.sh +++ b/scripts/install-duckdb-odbc.sh @@ -16,3 +16,21 @@ curl -fsSL -o /tmp/duckdb_odbc.zip "https://github.com/duckdb/duckdb-odbc/releas mkdir -p /opt/duckdb_odbc unzip /tmp/duckdb_odbc.zip -d /opt/duckdb_odbc rm /tmp/duckdb_odbc.zip + +# Configure ODBC driver in odbcinst.ini +cat >> /etc/odbcinst.ini << EOF + +[DuckDB] +Description=DuckDB ODBC Driver +Driver=/opt/duckdb_odbc/libduckdb_odbc.so +Setup=/opt/duckdb_odbc/libduckdb_odbc.so +UsageCount=1 +EOF + +# Configure default DuckDB data source in odbc.ini +cat >> /etc/odbc.ini << EOF + +[DuckDB] +Driver=DuckDB +Database=/var/lib/sqlpage/duckdb.db +EOF From f35bc6d3ffacf927be2b5d37a0d8329d7f1573fe Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Mon, 12 Jan 2026 18:22:04 +0100 Subject: [PATCH 03/11] add duckdb odbc info to readme --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 66fcf064..b534bc8d 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,17 @@ To run on a server, you can use [the docker image](https://hub.docker.com/r/lova We provide compiled binaries only for the x86_64 architecture, but provide docker images for other architectures, including arm64 and armv7. If you want to run SQLPage on a Raspberry Pi or a cheaper ARM cloud instance, using the docker image is the easiest way to do it. +#### DuckDB ODBC Docker Image + +A DuckDB-enabled variant is available with pre-installed DuckDB ODBC drivers: + +- Use the `-duckdb` suffix: `lovasoa/sqlpage:main-duckdb` or `lovasoa/sqlpage:latest-duckdb` +- Comes pre-configured to connect to DuckDB at `/var/lib/sqlpage/duckdb.db` +- To use a custom database location, set `DATABASE_URL`: + - `docker run -e DATABASE_URL="Driver=DuckDB;Database=/path/to/your.db" -p 8080:8080 lovasoa/sqlpage:main-duckdb` +- To persist your DuckDB database, mount a volume: + - `docker run -v ./data:/var/lib/sqlpage lovasoa/sqlpage:main-duckdb` + ### On Mac OS, with homebrew An alternative for Mac OS users is to use [SQLPage's homebrew package](https://formulae.brew.sh/formula/sqlpage). From adeb67831ba77f13e2ff469abfec1d6ab5836c21 Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 14 Jan 2026 14:16:18 +0100 Subject: [PATCH 04/11] Fix CI digest artifact matching --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 153ae58e..ffaea667 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -184,7 +184,7 @@ jobs: uses: actions/upload-artifact@v4 if: github.event_name != 'pull_request' with: - name: digests${{ steps.suffix.outputs.suffix }} + name: digests-${{ matrix.variant.name }}${{ steps.suffix.outputs.suffix }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 @@ -205,7 +205,7 @@ jobs: - name: Download digests uses: actions/download-artifact@v4 with: - pattern: digests*${{ matrix.variant.suffix }} + pattern: digests-${{ matrix.variant.name }}* merge-multiple: true path: /tmp/digests - name: Set up Docker Buildx From db9a4600b5647541b5ce53256466dd309656ee1e Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 14 Jan 2026 14:17:34 +0100 Subject: [PATCH 05/11] Temporarily publish docker images on branch --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ffaea667..378a7aea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,7 @@ env: CARGO_TERM_COLOR: always REGISTRY_USERNAME: lovasoa REGISTRY_IMAGE: lovasoa/sqlpage + PUBLISH_DOCKER: ${{ github.event_name != 'pull_request' || github.ref == 'refs/heads/duckdb-odbc-docker' }} jobs: compile_and_lint: @@ -165,12 +166,12 @@ jobs: platforms: ${{ matrix.platform }} target: ${{ matrix.variant.target }} labels: ${{ steps.meta.outputs.labels }} - push: ${{ github.event_name != 'pull_request' }} + push: ${{ env.PUBLISH_DOCKER == 'true' }} tags: ${{ steps.meta.outputs.tags }} cache-from: type=registry,ref=${{ env.REGISTRY_IMAGE }}:main${{ steps.suffix.outputs.suffix }} # don't save cache on prs cache-to: > - ${{ github.event_name != 'pull_request' + ${{ env.PUBLISH_DOCKER == 'true' && format('type=registry,ref={0}:main{1},compression=zstd,mode=max', env.REGISTRY_IMAGE, steps.suffix.outputs.suffix) || '' }} @@ -191,7 +192,7 @@ jobs: docker_push: runs-on: ubuntu-latest - if: github.event_name != 'pull_request' + if: env.PUBLISH_DOCKER == 'true' needs: - docker_build strategy: From 7919d8b90b7d3c8c05b6a4010515d8f5fe43d579 Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 14 Jan 2026 14:20:06 +0100 Subject: [PATCH 06/11] Allow docker publish on branch (fix) --- .github/workflows/ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 378a7aea..5768286c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,6 @@ env: CARGO_TERM_COLOR: always REGISTRY_USERNAME: lovasoa REGISTRY_IMAGE: lovasoa/sqlpage - PUBLISH_DOCKER: ${{ github.event_name != 'pull_request' || github.ref == 'refs/heads/duckdb-odbc-docker' }} jobs: compile_and_lint: @@ -166,12 +165,12 @@ jobs: platforms: ${{ matrix.platform }} target: ${{ matrix.variant.target }} labels: ${{ steps.meta.outputs.labels }} - push: ${{ env.PUBLISH_DOCKER == 'true' }} + push: ${{ github.event_name != 'pull_request' || github.ref == 'refs/heads/duckdb-odbc-docker' }} tags: ${{ steps.meta.outputs.tags }} cache-from: type=registry,ref=${{ env.REGISTRY_IMAGE }}:main${{ steps.suffix.outputs.suffix }} # don't save cache on prs cache-to: > - ${{ env.PUBLISH_DOCKER == 'true' + ${{ (github.event_name != 'pull_request' || github.ref == 'refs/heads/duckdb-odbc-docker') && format('type=registry,ref={0}:main{1},compression=zstd,mode=max', env.REGISTRY_IMAGE, steps.suffix.outputs.suffix) || '' }} @@ -192,7 +191,7 @@ jobs: docker_push: runs-on: ubuntu-latest - if: env.PUBLISH_DOCKER == 'true' + if: github.event_name != 'pull_request' || github.ref == 'refs/heads/duckdb-odbc-docker' needs: - docker_build strategy: From cec9db14445fff72ad8ee37973cd29a2af733f9a Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 14 Jan 2026 14:24:02 +0100 Subject: [PATCH 07/11] Temporarily run CI on branch pushes --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5768286c..9cfdd7a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 branches: - "main" + - "duckdb-odbc-docker" paths-ignore: - "docs/**" - "README.md" From b3cf7a6473f8d8cfb5d774db5e686c20bd06b520 Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 14 Jan 2026 14:51:09 +0100 Subject: [PATCH 08/11] Disable branch publish; fix duckdb inspect tag --- .github/workflows/ci.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9cfdd7a0..2d40cbf5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,6 @@ on: - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 branches: - "main" - - "duckdb-odbc-docker" paths-ignore: - "docs/**" - "README.md" @@ -166,12 +165,12 @@ jobs: platforms: ${{ matrix.platform }} target: ${{ matrix.variant.target }} labels: ${{ steps.meta.outputs.labels }} - push: ${{ github.event_name != 'pull_request' || github.ref == 'refs/heads/duckdb-odbc-docker' }} + push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} cache-from: type=registry,ref=${{ env.REGISTRY_IMAGE }}:main${{ steps.suffix.outputs.suffix }} # don't save cache on prs cache-to: > - ${{ (github.event_name != 'pull_request' || github.ref == 'refs/heads/duckdb-odbc-docker') + ${{ github.event_name != 'pull_request' && format('type=registry,ref={0}:main{1},compression=zstd,mode=max', env.REGISTRY_IMAGE, steps.suffix.outputs.suffix) || '' }} @@ -192,7 +191,7 @@ jobs: docker_push: runs-on: ubuntu-latest - if: github.event_name != 'pull_request' || github.ref == 'refs/heads/duckdb-odbc-docker' + if: github.event_name != 'pull_request' needs: - docker_build strategy: @@ -229,4 +228,4 @@ jobs: $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) - name: Inspect image run: | - docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}${{ matrix.variant.suffix }} + docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} From 19895771f39f17c1822a5c5ce6658f215e82f820 Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 14 Jan 2026 16:24:17 +0100 Subject: [PATCH 09/11] Temporarily enable CI push on branch --- .github/workflows/ci.yml | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d40cbf5..a80396a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,7 @@ on: - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 branches: - "main" + - "duckdb-odbc-docker" paths-ignore: - "docs/**" - "README.md" @@ -122,25 +123,23 @@ jobs: - linux/arm/v7 - linux/arm64 variant: - - name: default - target: minimal - suffix: "" - - name: duckdb - target: duckdb - suffix: -duckdb + - minimal + - duckdb exclude: # DuckDB ODBC is not available for armv7 - platform: linux/arm/v7 - variant: - name: duckdb - target: duckdb - suffix: -duckdb + variant: duckdb steps: - name: Checkout uses: actions/checkout@v4 - id: suffix name: Cache name suffix - run: echo "suffix=-$(tr '/' '-' <<< ${{ matrix.platform }})${{ matrix.variant.suffix }}" >> "$GITHUB_OUTPUT" + run: | + suffix="-$(tr '/' '-' <<< "${{ matrix.platform }}")" + if [[ "${{ matrix.variant }}" != "minimal" ]]; then + suffix="${suffix}-${{ matrix.variant }}" + fi + echo "suffix=${suffix}" >> "$GITHUB_OUTPUT" - name: Docker meta id: meta uses: docker/metadata-action@v5 @@ -163,7 +162,7 @@ jobs: with: context: . platforms: ${{ matrix.platform }} - target: ${{ matrix.variant.target }} + target: ${{ matrix.variant }} labels: ${{ steps.meta.outputs.labels }} push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} @@ -184,7 +183,7 @@ jobs: uses: actions/upload-artifact@v4 if: github.event_name != 'pull_request' with: - name: digests-${{ matrix.variant.name }}${{ steps.suffix.outputs.suffix }} + name: digests-${{ matrix.variant }}${{ steps.suffix.outputs.suffix }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 @@ -197,15 +196,13 @@ jobs: strategy: matrix: variant: - - name: default - suffix: "" - - name: duckdb - suffix: -duckdb + - minimal + - duckdb steps: - name: Download digests uses: actions/download-artifact@v4 with: - pattern: digests-${{ matrix.variant.name }}* + pattern: digests-${{ matrix.variant }}* merge-multiple: true path: /tmp/digests - name: Set up Docker Buildx @@ -215,7 +212,7 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY_IMAGE }} - flavor: suffix=${{ matrix.variant.suffix }} + flavor: suffix=${{ matrix.variant == 'minimal' && '' || format('-{0}', matrix.variant) }} - name: Login to Docker Hub uses: docker/login-action@v3 with: From 509fffb303559142547010ca0d22fdcfe76e2d58 Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 14 Jan 2026 18:03:26 +0100 Subject: [PATCH 10/11] Fix minimal tag suffix in docker_push --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a80396a4..34a9bec5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -212,7 +212,7 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY_IMAGE }} - flavor: suffix=${{ matrix.variant == 'minimal' && '' || format('-{0}', matrix.variant) }} + flavor: suffix=${{ matrix.variant != 'minimal' && format('-{0}', matrix.variant) || '' }} - name: Login to Docker Hub uses: docker/login-action@v3 with: From beaa555b306c1d66c94ec4f170da398541a49355 Mon Sep 17 00:00:00 2001 From: Ophir Lojkine Date: Wed, 14 Jan 2026 18:52:32 +0100 Subject: [PATCH 11/11] Disable temporary branch CI push --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34a9bec5..4fd62de8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,6 @@ on: - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 branches: - "main" - - "duckdb-odbc-docker" paths-ignore: - "docs/**" - "README.md"