Skip to content

Commit 17c69a1

Browse files
committed
Merge origin/main into nicosuave/yardstick-parity
2 parents e893ce9 + 18e752c commit 17c69a1

158 files changed

Lines changed: 38751 additions & 621 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.git
2+
.github
3+
.venv
4+
.mypy_cache
5+
.pytest_cache
6+
.ruff_cache
7+
__pycache__
8+
*.egg-info
9+
*.pyc
10+
11+
# Rust/DuckDB extension builds (not needed for Python image)
12+
sidemantic-rs/
13+
sidemantic-duckdb/
14+
15+
# Docs and examples not needed at runtime
16+
docs/
17+
examples/superset_demo/
18+
examples/rill_demo/
19+
examples/cube_demo/
20+
21+
# Dev/test artifacts
22+
Dockerfile.test
23+
docker-compose.yml
24+
*.md
25+
!README.md
26+
.context/

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ jobs:
126126

127127
- name: Run cargo test
128128
run: cargo test
129+
env:
130+
RUST_MIN_STACK: 16777216
129131

130132
duckdb-extension:
131133
name: DuckDB Extension

.github/workflows/docker.yml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: Docker
2+
3+
on:
4+
workflow_dispatch:
5+
workflow_run:
6+
workflows: ["Publish to PyPI"]
7+
types: [completed]
8+
9+
env:
10+
IMAGE: sidequery/sidemantic
11+
12+
jobs:
13+
docker:
14+
name: Docker build, test, and push
15+
runs-on: ubuntu-latest
16+
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
with:
21+
ref: main
22+
23+
- name: Set up Docker Buildx
24+
uses: docker/setup-buildx-action@v3
25+
26+
- name: Login to Docker Hub
27+
uses: docker/login-action@v3
28+
with:
29+
username: ${{ secrets.DOCKERHUB_USERNAME }}
30+
password: ${{ secrets.DOCKERHUB_TOKEN }}
31+
32+
- name: Extract version
33+
id: version
34+
run: |
35+
VERSION=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/')
36+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
37+
echo "Version: $VERSION"
38+
39+
- name: Build image
40+
uses: docker/build-push-action@v6
41+
with:
42+
context: .
43+
load: true
44+
tags: ${{ env.IMAGE }}:test
45+
cache-from: type=gha
46+
cache-to: type=gha,mode=max
47+
48+
- name: Start server (demo mode)
49+
run: |
50+
docker run -d --name sidemantic-test -p 5433:5433 ${{ env.IMAGE }}:test --demo
51+
for i in $(seq 1 30); do
52+
if docker logs sidemantic-test 2>&1 | grep -q "Listening on"; then
53+
echo "Server ready"
54+
break
55+
fi
56+
echo "Waiting for server... ($i/30)"
57+
sleep 1
58+
done
59+
60+
- name: Verify server logs
61+
run: docker logs sidemantic-test 2>&1
62+
63+
- name: Install psql
64+
run: sudo apt-get update && sudo apt-get install -y postgresql-client
65+
66+
- name: Test connection
67+
run: PGPASSWORD=any psql -h localhost -p 5433 -U any -d sidemantic -c "SELECT 1 AS test"
68+
69+
- name: Test metric aggregation (products)
70+
run: |
71+
PGPASSWORD=any psql -h localhost -p 5433 -U any -d sidemantic -c \
72+
"SELECT category, product_count, avg_price, total_catalog_value FROM semantic_layer.products GROUP BY category ORDER BY category"
73+
74+
- name: Test metric aggregation (customers)
75+
run: |
76+
PGPASSWORD=any psql -h localhost -p 5433 -U any -d sidemantic -c \
77+
"SELECT region, customer_count FROM semantic_layer.customers GROUP BY region ORDER BY region"
78+
79+
- name: Stop server
80+
if: always()
81+
run: docker stop sidemantic-test || true
82+
83+
- name: Push to Docker Hub
84+
uses: docker/build-push-action@v6
85+
with:
86+
context: .
87+
push: true
88+
tags: |
89+
${{ env.IMAGE }}:latest
90+
${{ env.IMAGE }}:${{ steps.version.outputs.version }}
91+
cache-from: type=gha

Dockerfile

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
FROM python:3.12-slim AS builder
2+
3+
# Install build deps for riffq (Rust/maturin) and other native extensions
4+
RUN apt-get update && apt-get install -y --no-install-recommends \
5+
build-essential \
6+
cmake \
7+
pkg-config \
8+
libssl-dev \
9+
&& rm -rf /var/lib/apt/lists/*
10+
11+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
12+
13+
WORKDIR /app
14+
15+
COPY pyproject.toml README.md LICENSE ./
16+
COPY sidemantic/ sidemantic/
17+
COPY examples/ examples/
18+
19+
RUN uv pip install --system --no-cache ".[serve,mcp,all-databases]"
20+
21+
# --- Runtime stage (no build tools) ---
22+
FROM python:3.12-slim
23+
24+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
25+
26+
# Copy installed packages from builder
27+
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
28+
COPY --from=builder /usr/local/bin/sidemantic /usr/local/bin/sidemantic
29+
30+
WORKDIR /app
31+
32+
COPY docker-entrypoint.sh /docker-entrypoint.sh
33+
RUN chmod +x /docker-entrypoint.sh
34+
35+
RUN mkdir -p /app/models
36+
WORKDIR /app/models
37+
38+
EXPOSE 5433
39+
40+
ENTRYPOINT ["/docker-entrypoint.sh"]
41+
# Mode is controlled by SIDEMANTIC_MODE env var (serve, mcp, both)

README.md

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
# Sidemantic
22

3-
SQL-first semantic layer for consistent metrics across your data stack. Compatible with many other semantic model formats.
3+
The universal metrics layer for consistent metrics across your data stack. Compatible with 15+ semantic model formats.
44

5-
- **Formats:** Sidemantic, Cube, MetricFlow (dbt), LookML, Hex, Rill, Superset, Omni, BSL, GoodData LDM, Snowflake Cortex, Malloy, OSI, AtScale SML, ThoughtSpot TML
6-
- **Databases:** DuckDB, MotherDuck, PostgreSQL, BigQuery, Snowflake, ClickHouse, Databricks, Spark SQL
5+
- **Supported Formats:** Sidemantic (YAML, Python or SQL), Cube, dbt MetricFlow, LookML, Hex, Rill, Superset, Omni, BSL, GoodData LDM, Snowflake Cortex, Malloy, OSI, AtScale SML, ThoughtSpot TML
6+
- **Databases:** DuckDB, MotherDuck, PostgreSQL, BigQuery, Snowflake, ClickHouse, Databricks, Spark SQL (also via ADBC)
77

8-
[Documentation](https://sidemantic.com) | [GitHub](https://github.com/sidequery/sidemantic) | [Discord](https://discord.com/invite/7MZ4UgSVvF) | [Demo](https://sidemantic.com/demo) (50+ MB download)
8+
[Documentation](https://sidemantic.com) | [GitHub](https://github.com/sidequery/sidemantic) | [Docker Hub](https://hub.docker.com/repository/docker/sidequery/sidemantic) | [Discord](https://discord.com/invite/7MZ4UgSVvF) | [Demo](https://sidemantic.com/demo) (50+ MB data download, runs in your browser with Pyodide + DuckDB)
99

1010
![Jupyter Widget Preview](preview.png)
1111

12-
## Should I use Sidemantic
13-
14-
Sidemantic is a very ambitious and young semantic layer project. You may encounter rough patches, especially with the more exotic features like converting between semantic model formats.
15-
16-
Issue reports are much appreciated if you try out Sidemantic and hit a snag 🫡
12+
The installer downloads the skill to `~/.agents/skills/sidemantic-modeler` and symlinks it into `~/.claude/skills/`.
1713

1814
## Quickstart
1915

@@ -252,6 +248,44 @@ load_from_directory(layer, "my_models/") # Auto-detects formats
252248
| Databricks || `uv add sidemantic[databricks]` |
253249
| Spark SQL || `uv add sidemantic[spark]` |
254250

251+
## Docker
252+
253+
The published image is [`sidequery/sidemantic`](https://hub.docker.com/r/sidequery/sidemantic) on Docker Hub. Mount your models directory as a volume at `/app/models`:
254+
255+
```bash
256+
docker run -p 5433:5433 -v ./models:/app/models sidequery/sidemantic
257+
```
258+
259+
Demo mode (built-in sample data, no volume needed):
260+
261+
```bash
262+
docker run -p 5433:5433 sidequery/sidemantic --demo
263+
```
264+
265+
See [`examples/docker/`](examples/docker/) for MCP mode, env vars, building from source, and integration test services.
266+
267+
## Agent Skill
268+
269+
Sidemantic ships an [agent skill](skills/sidemantic-modeler/) that teaches Claude Code, Codex, and other `SKILL.md`-compatible agents to build, validate, and query semantic models.
270+
271+
**One-liner install (no clone required):**
272+
273+
```bash
274+
curl -fsSL https://raw.githubusercontent.com/sidequery/sidemantic/main/skills/install.sh | bash
275+
```
276+
277+
**npx / bunx:**
278+
279+
```bash
280+
npx skills add https://github.com/sidequery/sidemantic --skill sidemantic-modeler
281+
# or
282+
bunx skills add https://github.com/sidequery/sidemantic --skill sidemantic-modeler
283+
```
284+
285+
## How mature is Sidemantic?
286+
287+
Sidemantic is an ambitious but young semantic layer project. You could encounter rough patches, especially with the more exotic features like converting between semantic model formats or serving semantic layers via the included Postgres protocol server.
288+
255289
## Testing
256290

257291
```bash

docker-entrypoint.sh

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#!/bin/sh
2+
set -e
3+
4+
# SIDEMANTIC_MODE: "serve" (default), "mcp", or "both"
5+
MODE="${SIDEMANTIC_MODE:-serve}"
6+
7+
# Build arg arrays for each command.
8+
# serve accepts: --connection, --db, --host, --port, --username, --password
9+
# mcp-serve accepts: --db only
10+
11+
# Serve args
12+
SERVE_ARGS="--host 0.0.0.0"
13+
if [ -n "$SIDEMANTIC_CONNECTION" ]; then
14+
SERVE_ARGS="$SERVE_ARGS --connection \"$SIDEMANTIC_CONNECTION\""
15+
fi
16+
if [ -n "$SIDEMANTIC_DB" ]; then
17+
SERVE_ARGS="$SERVE_ARGS --db \"$SIDEMANTIC_DB\""
18+
fi
19+
if [ -n "$SIDEMANTIC_USERNAME" ]; then
20+
SERVE_ARGS="$SERVE_ARGS --username \"$SIDEMANTIC_USERNAME\""
21+
fi
22+
if [ -n "$SIDEMANTIC_PASSWORD" ]; then
23+
SERVE_ARGS="$SERVE_ARGS --password \"$SIDEMANTIC_PASSWORD\""
24+
fi
25+
if [ -n "$SIDEMANTIC_PORT" ]; then
26+
SERVE_ARGS="$SERVE_ARGS --port \"$SIDEMANTIC_PORT\""
27+
fi
28+
29+
# MCP args (only --db is supported)
30+
MCP_ARGS=""
31+
if [ -n "$SIDEMANTIC_DB" ]; then
32+
MCP_ARGS="$MCP_ARGS --db \"$SIDEMANTIC_DB\""
33+
fi
34+
35+
case "$MODE" in
36+
serve)
37+
eval exec sidemantic serve $SERVE_ARGS "$@"
38+
;;
39+
mcp)
40+
eval exec sidemantic mcp-serve $MCP_ARGS "$@"
41+
;;
42+
both)
43+
eval sidemantic serve $SERVE_ARGS &
44+
SERVE_PID=$!
45+
trap "kill $SERVE_PID 2>/dev/null" EXIT
46+
eval exec sidemantic mcp-serve $MCP_ARGS "$@"
47+
;;
48+
*)
49+
echo "Unknown SIDEMANTIC_MODE: $MODE (use serve, mcp, or both)" >&2
50+
exit 1
51+
;;
52+
esac

examples/docker/README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Docker
2+
3+
The published image is [`sidequery/sidemantic`](https://hub.docker.com/repository/docker/sidequery/sidemantic) on Docker Hub. It includes all database drivers, the PostgreSQL wire-protocol server, and the MCP server.
4+
5+
## Mounting your models
6+
7+
The container looks for model files (YAML, SQL, etc.) in `/app/models`. Use a volume mount (`-v`) to point it at your local models directory:
8+
9+
```bash
10+
# If your models are in ~/my-project/models/
11+
docker run -p 5433:5433 -v ~/my-project/models:/app/models sidequery/sidemantic
12+
13+
# Or from the current directory
14+
docker run -p 5433:5433 -v $(pwd)/models:/app/models sidequery/sidemantic
15+
```
16+
17+
The `-v local/path:/app/models` flag maps a folder on your machine into the container. Any `.yml`, `.sql`, or other semantic model files in that folder will be auto-detected and loaded.
18+
19+
## PostgreSQL server (default)
20+
21+
Mount your models directory and expose port 5433:
22+
23+
```bash
24+
docker run -p 5433:5433 -v ./models:/app/models sidequery/sidemantic
25+
```
26+
27+
Connect with any PostgreSQL client:
28+
29+
```bash
30+
psql -h localhost -p 5433 -U any -d sidemantic
31+
```
32+
33+
With a backend database connection:
34+
35+
```bash
36+
docker run -p 5433:5433 \
37+
-v ./models:/app/models \
38+
-e SIDEMANTIC_CONNECTION="postgres://user:pass@host:5432/db" \
39+
sidequery/sidemantic
40+
```
41+
42+
## MCP server
43+
44+
```bash
45+
docker run -v ./models:/app/models -e SIDEMANTIC_MODE=mcp sidequery/sidemantic
46+
```
47+
48+
## Both servers simultaneously
49+
50+
Runs the PG server in the background and MCP on stdio:
51+
52+
```bash
53+
docker run -p 5433:5433 -v ./models:/app/models -e SIDEMANTIC_MODE=both sidequery/sidemantic
54+
```
55+
56+
## Demo mode
57+
58+
```bash
59+
docker run -p 5433:5433 sidequery/sidemantic --demo
60+
```
61+
62+
## Baking models into the image
63+
64+
```dockerfile
65+
FROM sidequery/sidemantic
66+
COPY my_models/ /app/models/
67+
```
68+
69+
## Environment variables
70+
71+
| Variable | Description |
72+
|----------|-------------|
73+
| `SIDEMANTIC_MODE` | `serve` (default), `mcp`, or `both` |
74+
| `SIDEMANTIC_CONNECTION` | Database connection string |
75+
| `SIDEMANTIC_DB` | Path to DuckDB file (inside container) |
76+
| `SIDEMANTIC_USERNAME` | PG server auth username |
77+
| `SIDEMANTIC_PASSWORD` | PG server auth password |
78+
| `SIDEMANTIC_PORT` | PG server port (default 5433) |
79+
80+
## Building from source
81+
82+
From the repo root:
83+
84+
```bash
85+
docker build -t sidemantic .
86+
```
87+
88+
## Integration test services (docker-compose)
89+
90+
The `docker-compose.yml` in this directory spins up Postgres, BigQuery emulator, Spark, and ClickHouse for local integration testing:
91+
92+
```bash
93+
docker compose -f examples/docker/docker-compose.yml up
94+
```

0 commit comments

Comments
 (0)