Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
218 changes: 218 additions & 0 deletions .github/workflows/release-jupyter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
name: Jupyter Kernel Release

on:
push:
tags:
- "v*"
workflow_dispatch:

permissions:
contents: read

jobs:
generate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install tree-sitter-cli
run: npm install -g tree-sitter-cli

- name: Generate parser
working-directory: tree-sitter-ggsql
run: tree-sitter generate

- uses: actions/upload-artifact@v4
with:
name: tree-sitter-generated
path: tree-sitter-ggsql/src/

linux:
needs: generate
runs-on: ubuntu-latest
strategy:
matrix:
target: [x86_64, aarch64]
env:
GGSQL_SKIP_GENERATE: "1"
steps:
- uses: actions/checkout@v4

- uses: actions/download-artifact@v4
with:
name: tree-sitter-generated
path: tree-sitter-ggsql/src/

- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist
working-directory: ggsql-jupyter
manylinux: 2_28
docker-options: -e GGSQL_SKIP_GENERATE=1

- uses: actions/upload-artifact@v4
with:
name: jupyter-wheels-linux-${{ matrix.target }}
path: ggsql-jupyter/dist

macos:
needs: generate
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- target: x86_64
runner: macos-latest
- target: aarch64
runner: macos-latest
env:
GGSQL_SKIP_GENERATE: "1"
steps:
- uses: actions/checkout@v4

- uses: actions/download-artifact@v4
with:
name: tree-sitter-generated
path: tree-sitter-ggsql/src/

- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist
working-directory: ggsql-jupyter

- uses: actions/upload-artifact@v4
with:
name: jupyter-wheels-macos-${{ matrix.target }}
path: ggsql-jupyter/dist

windows:
needs: generate
runs-on: windows-latest
env:
GGSQL_SKIP_GENERATE: "1"
steps:
- uses: actions/checkout@v4

- uses: actions/download-artifact@v4
with:
name: tree-sitter-generated
path: tree-sitter-ggsql/src/

- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: x64
args: --release --out dist
working-directory: ggsql-jupyter

- uses: actions/upload-artifact@v4
with:
name: jupyter-wheels-windows-x64
path: ggsql-jupyter/dist

sdist:
needs: generate
runs-on: ubuntu-latest
env:
GGSQL_SKIP_GENERATE: "1"
steps:
- uses: actions/checkout@v4

- uses: actions/download-artifact@v4
with:
name: tree-sitter-generated
path: tree-sitter-ggsql/src/

- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
working-directory: ggsql-jupyter

- uses: actions/upload-artifact@v4
with:
name: jupyter-wheels-sdist
path: ggsql-jupyter/dist

publish:
needs: [linux, macos, windows, sdist]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
environment: pypi-jupyter
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
pattern: jupyter-wheels-*
merge-multiple: true
path: dist

- name: List wheels
run: ls -lh dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

github-release:
needs: [linux, macos, windows]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
pattern: jupyter-wheels-*
path: artifacts

- name: Extract binaries from wheels
run: |
mkdir -p binaries

# Linux x86_64
unzip -j artifacts/jupyter-wheels-linux-x86_64/*manylinux*x86_64*.whl \
"*.data/scripts/ggsql-jupyter" -d /tmp/extract
mv /tmp/extract/ggsql-jupyter binaries/ggsql-jupyter-linux-x64
rm -rf /tmp/extract

# Linux aarch64
unzip -j artifacts/jupyter-wheels-linux-aarch64/*manylinux*aarch64*.whl \
"*.data/scripts/ggsql-jupyter" -d /tmp/extract
mv /tmp/extract/ggsql-jupyter binaries/ggsql-jupyter-linux-arm64
rm -rf /tmp/extract

# macOS x86_64
unzip -j artifacts/jupyter-wheels-macos-x86_64/*macosx*x86_64*.whl \
"*.data/scripts/ggsql-jupyter" -d /tmp/extract
mv /tmp/extract/ggsql-jupyter binaries/ggsql-jupyter-macos-x64
rm -rf /tmp/extract

# macOS aarch64
unzip -j artifacts/jupyter-wheels-macos-aarch64/*macosx*arm64*.whl \
"*.data/scripts/ggsql-jupyter" -d /tmp/extract
mv /tmp/extract/ggsql-jupyter binaries/ggsql-jupyter-macos-arm64
rm -rf /tmp/extract

# Windows x64
unzip -j artifacts/jupyter-wheels-windows-x64/*win_amd64*.whl \
"*.data/scripts/ggsql-jupyter.exe" -d /tmp/extract
mv /tmp/extract/ggsql-jupyter.exe binaries/ggsql-jupyter-windows-x64.exe
rm -rf /tmp/extract

chmod +x binaries/ggsql-jupyter-*
ls -lh binaries/

- name: Upload to GitHub Release
uses: softprops/action-gh-release@v2
with:
files: binaries/*
File renamed without changes.
68 changes: 30 additions & 38 deletions ggsql-jupyter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,63 +19,55 @@ The ggsql Jupyter kernel enables you to run ggsql queries directly in Jupyter no
### Prerequisites

- Jupyter Lab or Notebook installed
- Python 3.8+ (for Jupyter)

### Option 1: Install from crates.io (Recommended)
### Option 1: Install from PyPI (Recommended)

If you have Rust installed:
The easiest way to install the ggsql kernel is from PyPI. This provides pre-built binaries for Linux, macOS, and Windows.

Using pip:

```bash
cargo install ggsql-jupyter
pip install ggsql-jupyter
ggsql-jupyter --install
Comment on lines +30 to 31
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a good reason why these should be separate?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The --install line creates the required kernel.json spec file for Jupyter and installs it in the right location by invoking jupyter kernelspec install.

I saw the pattern used by other kernels distributed on PyPI, does pip have a concept of a post-install script that could do the same thing?

Copy link
Collaborator

@cpsievert cpsievert Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had Claude investigate, here's what it found. I think the caveat is important enough that we just stick with the current approach?

"pip doesn't have a post-install hook for wheels — modern pip just extracts the wheel directly with no custom code execution (by design, PEP 427). And since this is a maturin bindings = "bin" project with no Python code at all, there's nothing to hook into anyway.

However, there is a clean alternative: maturin supports a .data directory mechanism that can auto-place a kernel.json into the Jupyter kernelspec directory at install time, with no second step required. You'd create:

ggsql-jupyter/
  ggsql_jupyter.data/
    data/
      share/
        jupyter/
          kernels/
            ggsql/
              kernel.json

The data/ subdirectory maps to sys.prefix on install, and Jupyter searches {sys.prefix}/share/jupyter/kernels/ by default. So pip install ggsql-jupyter would automatically register the kernel. The kernel.json argv would just reference ggsql-jupyter by name (since pip puts the binary on PATH in the venv).

Main caveat: this works well for virtualenv/conda installs (the common case), but for pip install --user, sys.prefix points to the system prefix rather than ~/.local, so the kernelspec placement may not be ideal. The --install flag would still be useful as a fallback for non-pip installs (cargo install, binary downloads) and for explicit --user/--sys-prefix targeting."

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that's useful.

Hmmm, it sounds like it would work most of the time but then be annoying when it doesn't. Yes, let's stick with this method for now, the --install argument is useful for replacing the kernel in local development too.

```

This will:

1. Download and compile the kernel
2. Install it into your current environment (respects virtualenvs, conda, uv)

### Option 2: Download Pre-built Binary from GitHub Releases
Using [uv](https://docs.astral.sh/uv/):

For users without Rust:

1. **Download the binary** for your platform from [GitHub Releases](https://github.com/georgestagg/ggsql/releases)

- Linux: `ggsql-jupyter-linux-x64`
- macOS (Intel): `ggsql-jupyter-macos-x64`
- macOS (Apple Silicon): `ggsql-jupyter-macos-arm64`
- Windows: `ggsql-jupyter-windows-x64.exe`
```bash
uv tool install ggsql-jupyter
ggsql-jupyter --install
```

2. **Rename and make executable** (Linux/macOS):
The `--install` flag registers the kernel with Jupyter. It automatically detects and respects your current environment (virtualenv, conda, uv, etc.).

```bash
mv ggsql-jupyter-linux-x64 ggsql-jupyter
chmod +x ggsql-jupyter
```
### Option 2: Download Pre-built Binary

3. **Install the kernel**:
Pre-built binaries are available from [GitHub Releases](https://github.com/georgestagg/ggsql/releases):

```bash
./ggsql-jupyter --install
```
| Platform | Binary |
| -------------------- | ---------------------------------- |
| Linux (x86_64) | `ggsql-jupyter-linux-x64` |
| Linux (ARM64) | `ggsql-jupyter-linux-arm64` |
| macOS (Intel) | `ggsql-jupyter-macos-x64` |
| macOS (Apple Silicon) | `ggsql-jupyter-macos-arm64` |
| Windows (x64) | `ggsql-jupyter-windows-x64.exe` |

On Windows (PowerShell):
After downloading, make it executable and install:

```powershell
.\ggsql-jupyter-windows-x64.exe --install
```
```bash
chmod +x ggsql-jupyter-*
./ggsql-jupyter-linux-x64 --install
```

The `--install` flag automatically:
On Windows (PowerShell):

- Creates a temporary directory with the kernel spec
- Copies the binary to the appropriate location
- Runs `jupyter kernelspec install` with the correct flags
- Respects your current environment (virtualenv, conda, etc.)
- Cleans up temporary files
```powershell
.\ggsql-jupyter-windows-x64.exe --install
```

### Option 3: Build from Source

From the workspace root:
Requires a [Rust toolchain](https://rustup.rs/). From the workspace root:

```bash
cargo build --release --package ggsql-jupyter
Expand Down
18 changes: 18 additions & 0 deletions ggsql-jupyter/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[build-system]
requires = ["maturin>=1.4"]
build-backend = "maturin"

[project]
name = "ggsql-jupyter"
version = "0.1.0"
description = "Jupyter kernel for ggsql - SQL extension for declarative data visualization"
readme = "README.md"
license = { text = "MIT" }
keywords = ["jupyter", "kernel", "sql", "visualization", "ggsql"]
classifiers = [
"Programming Language :: Rust",
"Framework :: Jupyter",
]

[tool.maturin]
bindings = "bin"
Loading