Skip to content

Commit bbe587b

Browse files
authored
move cli to module (#379)
1 parent 2041d7f commit bbe587b

14 files changed

Lines changed: 260 additions & 100 deletions

File tree

.github/workflows/release-packages.yml

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ jobs:
6262
uses: ./.github/workflows/actions/sign-files
6363
with:
6464
paths: |
65-
./src/target/release/packager/*.exe
66-
./src/target/release/packager/*.msi
65+
./ggsql-cli/target/release/packager/*.exe
66+
./ggsql-cli/target/release/packager/*.msi
6767
env:
6868
# environment variables required to sign with signtool
6969
SM_HOST: ${{ secrets.SM_HOST }}
@@ -76,14 +76,14 @@ jobs:
7676
uses: actions/upload-artifact@v4
7777
with:
7878
name: ggsql-windows-nsis
79-
path: src/target/release/packager/*.exe
79+
path: ggsql-cli/target/release/packager/*.exe
8080
retention-days: 30
8181

8282
- name: Upload MSI installer
8383
uses: actions/upload-artifact@v4
8484
with:
8585
name: ggsql-windows-msi
86-
path: src/target/release/packager/*.msi
86+
path: ggsql-cli/target/release/packager/*.msi
8787
retention-days: 30
8888

8989
build-macos-x86_64:
@@ -162,10 +162,10 @@ jobs:
162162
xargs -0 -I{} codesign --force --options runtime --timestamp --sign "$SIGN_ID" "{}"
163163
# Then sign the executables with hardened runtime + entitlements
164164
codesign --force --options runtime --timestamp \
165-
--entitlements src/entitlements.plist \
165+
--entitlements entitlements.plist \
166166
--sign "$SIGN_ID" target/release/ggsql
167167
codesign --force --options runtime --timestamp \
168-
--entitlements src/entitlements.plist \
168+
--entitlements entitlements.plist \
169169
--sign "$SIGN_ID" target/release/ggsql-jupyter
170170
171171
- name: Build and notarize PKG installer (x86_64)
@@ -285,10 +285,10 @@ jobs:
285285
xargs -0 -I{} codesign --force --options runtime --timestamp --sign "$SIGN_ID" "{}"
286286
# Then sign the executables with hardened runtime + entitlements
287287
codesign --force --options runtime --timestamp \
288-
--entitlements src/entitlements.plist \
288+
--entitlements entitlements.plist \
289289
--sign "$SIGN_ID" target/release/ggsql
290290
codesign --force --options runtime --timestamp \
291-
--entitlements src/entitlements.plist \
291+
--entitlements entitlements.plist \
292292
--sign "$SIGN_ID" target/release/ggsql-jupyter
293293
294294
- name: Build and notarize PKG installer (aarch64)
@@ -377,7 +377,7 @@ jobs:
377377
uses: actions/upload-artifact@v4
378378
with:
379379
name: ggsql-linux-deb-x86_64
380-
path: src/target/release/packager/*.deb
380+
path: ggsql-cli/target/release/packager/*.deb
381381
retention-days: 30
382382

383383
build-linux-aarch64:
@@ -428,7 +428,7 @@ jobs:
428428
uses: actions/upload-artifact@v4
429429
with:
430430
name: ggsql-linux-deb-aarch64
431-
path: src/target/release/packager/*.deb
431+
path: ggsql-cli/target/release/packager/*.deb
432432
retention-days: 30
433433

434434
build-cargo:
@@ -485,6 +485,11 @@ jobs:
485485
env:
486486
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
487487

488+
- name: Publish ggsql-cli
489+
run: cargo publish --package ggsql-cli
490+
env:
491+
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
492+
488493
build-wasm:
489494
name: Build Wasm Package (wasm32)
490495
runs-on: ubuntu-latest

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ shapes (#368)
2525
before registering them back to the backend. We now keep the data purely on the
2626
backend until the layer query as was always intended (#363)
2727
- Simplified internal approach to DataFrame with DuckDB reader (#365)
28+
- Moved the CLI to its own module rather than be part of the main crate (#379)
2829
- Restructured CLAUDE.md to better deal with the rising complexity of the project (#382)
2930

3031
### Removed

CLAUDE.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ The user-facing site is at <https://ggsql.org>. The README at [`README.md`](READ
2121

2222
| Folder | Role | Type | Per-folder CLAUDE.md |
2323
| --- | --- | --- | --- |
24-
| [`src/`](src/) | Core Rust library + `ggsql` CLI | Cargo workspace member | [`src/CLAUDE.md`](src/CLAUDE.md) |
24+
| [`src/`](src/) | Core Rust library (crate `ggsql`) | Cargo workspace member | [`src/CLAUDE.md`](src/CLAUDE.md) |
25+
| [`ggsql-cli/`](ggsql-cli/) | `ggsql` command-line binary | Cargo workspace member | [`ggsql-cli/CLAUDE.md`](ggsql-cli/CLAUDE.md) |
2526
| [`tree-sitter-ggsql/`](tree-sitter-ggsql/) | Tree-sitter grammar + multi-language bindings | Cargo workspace member (also npm + PyPI) | [`tree-sitter-ggsql/CLAUDE.md`](tree-sitter-ggsql/CLAUDE.md) |
2627
| [`ggsql-jupyter/`](ggsql-jupyter/) | Jupyter kernel | Cargo workspace member (also PyPI via maturin) | [`ggsql-jupyter/CLAUDE.md`](ggsql-jupyter/CLAUDE.md) |
2728
| [`ggsql-wasm/`](ggsql-wasm/) | WebAssembly bindings + browser playground | Cargo workspace member | [`ggsql-wasm/CLAUDE.md`](ggsql-wasm/CLAUDE.md) |
2829
| [`ggsql-vscode/`](ggsql-vscode/) | VS Code / Positron extension | Standalone TypeScript / npm | [`ggsql-vscode/CLAUDE.md`](ggsql-vscode/CLAUDE.md) |
2930
| [`doc/`](doc/) | Quarto documentation site (ggsql.org) | Quarto project | [`doc/CLAUDE.md`](doc/CLAUDE.md) |
3031

31-
The Cargo workspace (`/Cargo.toml`) has four members: `tree-sitter-ggsql`, `src`, `ggsql-jupyter`, `ggsql-wasm`. Default workspace members exclude `ggsql-wasm` (it needs the wasm32 target and is built separately).
32+
The Cargo workspace (`/Cargo.toml`) has five members: `tree-sitter-ggsql`, `src`, `ggsql-cli`, `ggsql-jupyter`, `ggsql-wasm`. Default workspace members exclude `ggsql-wasm` (it needs the wasm32 target and is built separately).
3233

3334
## High-level pipeline
3435

@@ -48,13 +49,16 @@ For details — module layout, traits, where extension points live — see [`src
4849
## Building
4950

5051
```sh
51-
# Rust workspace (default members: tree-sitter-ggsql, src, ggsql-jupyter)
52+
# Rust workspace (default members: tree-sitter-ggsql, src, ggsql-cli, ggsql-jupyter)
5253
cargo build --workspace
5354
cargo build --release --workspace
5455

55-
# Just the CLI / library
56+
# Just the library
5657
cargo build --package ggsql
5758

59+
# Just the CLI binary
60+
cargo build --package ggsql-cli
61+
5862
# Wasm build (separate, not in default workspace members)
5963
cd ggsql-wasm && ./build-wasm.sh
6064

@@ -89,6 +93,7 @@ Per-folder CLAUDE.md files cover component-specific test guidance.
8993
## Where to ask which question
9094

9195
- *What does clause/layer/scale X do?*[`doc/syntax/`](doc/syntax/).
96+
- *How does the `ggsql` CLI work? Where do its subcommands live?*[`ggsql-cli/CLAUDE.md`](ggsql-cli/CLAUDE.md).
9297
- *How does the parser work? How is a `Plot` built?*[`src/CLAUDE.md`](src/CLAUDE.md), then `src/parser/`.
9398
- *How do I add a new geom / scale type / coord?*[`src/plot/CLAUDE.md`](src/plot/CLAUDE.md).
9499
- *How does Vega-Lite output get assembled?*[`src/writer/vegalite/CLAUDE.md`](src/writer/vegalite/CLAUDE.md).

Cargo.lock

Lines changed: 13 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
members = [
33
"tree-sitter-ggsql",
44
"src",
5+
"ggsql-cli",
56
"ggsql-jupyter",
67
"ggsql-wasm"
78
]
89
default-members = [
910
"tree-sitter-ggsql",
1011
"src",
12+
"ggsql-cli",
1113
"ggsql-jupyter"
1214
]
1315
resolver = "2"

INSTALLERS.md

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,69 @@
11
# Building Cross-Platform Installers
22

3-
ggsql uses [cargo-packager](https://github.com/crabnebula-dev/cargo-packager) to create native installers for Windows, macOS, and Linux.
3+
ggsql ships native installers for Windows, macOS, and Linux. Windows (NSIS / MSI) and Linux (Deb) installers are built via [cargo-packager](https://github.com/crabnebula-dev/cargo-packager); macOS installers are built directly with Apple's `pkgbuild`, then code-signed and notarized.
44

55
## Quick Start
66

77
### Prerequisites
88

9-
1. **Install cargo-packager**:
9+
1. **For Windows / Linux installers — install cargo-packager**:
1010

1111
```bash
1212
cargo install cargo-packager --locked
1313
```
1414

1515
2. **Platform-specific requirements**:
1616
- **Windows**: No additional requirements (uses built-in NSIS, optionally WiX if installed)
17-
- **macOS**: Xcode Command Line Tools
17+
- **macOS**: Xcode Command Line Tools, plus [`dylibbundler`](https://github.com/auriamg/macdylibbundler) (`brew install dylibbundler`) for bundling Arrow / DuckDB dynamic libraries
1818
- **Linux**: `sudo apt-get install libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf`
1919

2020
### Build Installers Locally
2121

22-
From the `src/` directory:
23-
2422
```bash
2523
# Windows
26-
cd src
24+
cd ggsql-cli
2725
cargo packager --release --formats nsis # Creates .exe installer (NSIS)
2826
cargo packager --release --formats wix # Creates .msi installer (WiX)
2927

30-
# macOS
31-
cd src
32-
cargo packager --release --formats dmg # Creates .dmg disk image
33-
3428
# Linux
35-
cd src
29+
cd ggsql-cli
3630
cargo packager --release --formats deb # Creates .deb package (Debian/Ubuntu)
3731
```
3832

39-
Output location: `src/target/release/packager/`
33+
Output for cargo-packager: `ggsql-cli/target/release/packager/`.
34+
35+
**macOS** uses a separate `pkgbuild` flow (matches what CI ships). From the workspace root:
36+
37+
```bash
38+
# Build the binaries
39+
cargo build --release --bin ggsql --bin ggsql-jupyter
40+
41+
# Bundle dylibs alongside the binaries
42+
dylibbundler -cd -of -b -x target/release/ggsql -d ./libs/ -p "@executable_path/../lib/ggsql/"
43+
dylibbundler -cd -of -b -x target/release/ggsql-jupyter -d ./libs/ -p "@executable_path/../lib/ggsql/"
44+
45+
# Stage payload and build an unsigned .pkg
46+
mkdir -p pkg-payload/usr/local/bin pkg-payload/usr/local/lib/ggsql
47+
cp target/release/ggsql target/release/ggsql-jupyter pkg-payload/usr/local/bin/
48+
cp -R ./libs/. pkg-payload/usr/local/lib/ggsql/
49+
pkgbuild \
50+
--root ./pkg-payload \
51+
--install-location / \
52+
--identifier co.posit.ggsql \
53+
--version 0.0.0-dev \
54+
ggsql-dev.pkg
55+
```
56+
57+
CI additionally codesigns the binaries with `entitlements.plist` (Developer ID Application), signs the `.pkg` (Developer ID Installer), and notarizes via `xcrun notarytool` — see [`.github/workflows/release-packages.yml`](.github/workflows/release-packages.yml). Local builds without those creds produce an unsigned `.pkg` that's fine for testing but will be Gatekeeper-blocked on other machines.
4058

4159
## Available Formats
4260

43-
| Platform | Format | Command | Output |
44-
| ----------- | ---------- | ---------------- | ---------------------------------- |
45-
| **Windows** | NSIS | `--formats nsis` | `ggsql_0.1.0_x64-setup.exe` (12MB) |
46-
| **Windows** | MSI | `--formats wix` | `ggsql_0.1.0_x64_en-US.msi` (15MB) |
47-
| **macOS** | DMG | `--formats dmg` | `ggsql_0.1.0_x64.dmg` |
48-
| **macOS** | App Bundle | `--formats app` | `ggsql.app` |
49-
| **Linux** | Debian | `--formats deb` | `ggsql_0.1.0_amd64.deb` |
61+
| Platform | Format | Tool | Output |
62+
| ----------- | ------ | ---------------- | ------------------------------- |
63+
| **Windows** | NSIS | `cargo packager --formats nsis` | `ggsql_0.1.0_x64-setup.exe` |
64+
| **Windows** | MSI | `cargo packager --formats wix` | `ggsql_0.1.0_x64_en-US.msi` |
65+
| **macOS** | PKG | `pkgbuild` (see above) | `ggsql_0.1.0_x86_64.pkg`, `ggsql_0.1.0_aarch64.pkg` |
66+
| **Linux** | Debian | `cargo packager --formats deb` | `ggsql_0.1.0_amd64.deb` |
5067

5168
## What Gets Packaged
5269

@@ -57,18 +74,19 @@ The installers include:
5774

5875
## Configuration
5976

60-
Installer configuration is in `src/Cargo.toml` under `[package.metadata.packager]`:
77+
Windows / Linux installer configuration is in `ggsql-cli/Cargo.toml` under `[package.metadata.packager]` (the macOS `.pkg` build is driven entirely by the `pkgbuild` invocation in the workflow, not by this metadata):
6178

6279
```toml
6380
[package.metadata.packager]
6481
product-name = "ggsql"
6582
identifier = "com.ggsql.app"
6683
category = "DeveloperTool"
6784
publisher = "ggsql Team"
68-
icons = ["../doc/assets/icon.svg", "../doc/assets/logo.png"]
85+
icons = ["../doc/assets/logo.png"]
6986
license-file = "../LICENSE.md"
7087
binaries = [
7188
{ path = "ggsql", main = true },
89+
{ path = "ggsql-jupyter", main = false },
7290
]
7391
```
7492

@@ -81,9 +99,9 @@ git tag v0.1.0
8199
git push origin v0.1.0
82100
```
83101

84-
The workflow (`.github/workflows/release-installers.yml`) will:
102+
The workflow (`.github/workflows/release-packages.yml`) will:
85103

86-
1. **Build installers** for Windows (NSIS + MSI), macOS (DMG), and Linux (Deb)
104+
1. **Build installers** for Windows (NSIS + MSI), macOS (PKG, x86_64 + aarch64, signed and notarized), and Linux (Deb, x86_64 + aarch64)
87105
2. **Create a GitHub Release** with all installers attached
88106
3. **Generate release notes** automatically
89107

@@ -106,11 +124,11 @@ You can also trigger builds manually from the Actions tab.
106124

107125
### macOS
108126

109-
**DMG Installer**
127+
**PKG Installer**
110128

111-
- Open `ggsql_0.1.0_x64.dmg`
112-
- Drag `ggsql.app` to Applications folder
113-
- Or copy binaries directly to `/usr/local/bin`
129+
- Double-click `ggsql_0.1.0_x86_64.pkg` (Intel) or `ggsql_0.1.0_aarch64.pkg` (Apple Silicon)
130+
- Installs `ggsql` and `ggsql-jupyter` into `/usr/local/bin/` and bundled dylibs into `/usr/local/lib/ggsql<version>/`
131+
- CLI command-line install: `sudo installer -pkg ggsql_0.1.0_aarch64.pkg -target /`
114132

115133
### Linux
116134

@@ -141,11 +159,15 @@ ggsql --version
141159

142160
```bash
143161
# Install
144-
hdiutil attach ggsql_0.1.0_x64.dmg
145-
cp -r /Volumes/ggsql/ggsql.app /Applications/
162+
sudo installer -pkg ggsql_0.1.0_aarch64.pkg -target /
146163

147164
# Verify
148-
/Applications/ggsql.app/Contents/MacOS/ggsql --version
165+
ggsql --version
166+
ggsql-jupyter --version
167+
168+
# Uninstall
169+
sudo rm /usr/local/bin/ggsql /usr/local/bin/ggsql-jupyter
170+
sudo rm -rf /usr/local/lib/ggsql*
149171
```
150172

151173
### Linux
@@ -165,14 +187,16 @@ sudo apt-get remove ggsql
165187

166188
This happens with unsigned installers. Click "More info" → "Run anyway". For production, sign the installer with a code signing certificate.
167189

168-
### macOS: "ggsql.app is damaged"
190+
### macOS: ".pkg can't be opened" / "unidentified developer"
169191

170-
This happens with unsigned apps. Run:
192+
Locally-built `.pkg` files are unsigned and Gatekeeper will block double-clicking them. Either install from the command line:
171193

172194
```bash
173-
xattr -cr /Applications/ggsql.app
195+
sudo installer -pkg ggsql-dev.pkg -target /
174196
```
175197

198+
…or right-click → Open to bypass Gatekeeper for that file. Official releases from GitHub are signed with the Developer ID Installer certificate and notarized, so they install without warnings.
199+
176200
### Linux: Missing dependencies
177201

178202
If the Deb/RPM package fails to install, ensure you have the required system libraries:
@@ -191,7 +215,9 @@ Build for different architectures:
191215
```bash
192216
# macOS: Build for Apple Silicon
193217
rustup target add aarch64-apple-darwin
194-
cargo packager --release --target aarch64-apple-darwin --formats dmg
218+
cargo build --release --target aarch64-apple-darwin --bin ggsql --bin ggsql-jupyter
219+
# Then run the dylibbundler + pkgbuild flow shown above against
220+
# target/aarch64-apple-darwin/release/ instead of target/release/.
195221

196222
# Linux: Build for ARM64
197223
rustup target add aarch64-unknown-linux-gnu

doc/index.qmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ uv tool install ggsql-jupyter
8888
ggsql-jupyter --install
8989

9090
# CLI (crates.io)
91-
cargo install ggsql
91+
cargo install ggsql-cli
9292
```
9393
:::
9494
:::

0 commit comments

Comments
 (0)