Skip to content

Commit 7626e89

Browse files
author
sqlopt
committed
refactor: rebrand sqlopt → dbopt (closes #6) + installers, gates, repo hygiene
Brand the whole product dbopt — no more sqlopt: - Binaries: dbopt, dbopt-backend, dbopt-sentinel, dbopt-eval. - Env vars SQLOPT_* → DBOPT_*; data dir ~/.sqlopt → ~/.dbopt; localStorage namespace sqlopt.* → dbopt.*. Non-breaking migration shims for the two that hold live data: localStorage reads through to the old keys once; the sentinel data dir falls back to ~/.sqlopt if ~/.dbopt doesn't exist yet. Installers (closes part of #7): - One-line install.sh (Linux + macOS Apple Silicon) and install.ps1 (Windows), served from dbopt.org. README/landing lead with downloads (.msi/.dmg/.AppImage), not git clone. Dropped Intel macOS (untested). Best-effort Windows MSI in CI. CI gates: added `cargo test --workspace` alongside the eval F1 ≥ 0.95 gate. Repo hygiene: removed committed binaries (release tarball + staging/) and added a .gitignore so build/release artifacts are never committed again. Docs: new docs/USAGE.md (detailed how-to). Verified end-to-end: web + cargo build, cargo test, eval F1 = 1.000, backend boots on :3690, and the data-dir shim keeps the existing ~/.sqlopt monitoring data.
1 parent 02a2d5b commit 7626e89

48 files changed

Lines changed: 415 additions & 170 deletions

Some content is hidden

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

.github/workflows/ci.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ jobs:
2121
continue-on-error: true
2222
- name: build
2323
run: cargo build --workspace --release
24-
- name: eval (target F1 ≥ 0.95)
25-
run: ./target/release/sqlopt-eval --target 0.95
24+
- name: test (unit + HTTP integration) · gate
25+
run: cargo test --workspace --release
26+
- name: eval (F1 ≥ 0.95) · gate
27+
run: ./target/release/dbopt-eval --target 0.95
2628

2729
web:
2830
runs-on: ubuntu-latest

.github/workflows/release.yml

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ jobs:
4949
matrix:
5050
include:
5151
- { label: linux-x86_64, os: ubuntu-latest, target: x86_64-unknown-linux-gnu, archive: tar.gz, binsuffix: '' }
52-
- { label: macos-x86_64, os: macos-13, target: x86_64-apple-darwin, archive: tar.gz, binsuffix: '' }
5352
- { label: macos-arm64, os: macos-latest, target: aarch64-apple-darwin, archive: tar.gz, binsuffix: '' }
5453
- { label: windows-x86_64, os: windows-latest, target: x86_64-pc-windows-msvc, archive: zip, binsuffix: '.exe' }
5554
steps:
@@ -69,82 +68,84 @@ jobs:
6968
shell: bash
7069
run: |
7170
mkdir -p staging
72-
cp target/${{ matrix.target }}/release/sqlopt-backend${{ matrix.binsuffix }} staging/sqlopt${{ matrix.binsuffix }}
71+
cp target/${{ matrix.target }}/release/dbopt-backend${{ matrix.binsuffix }} staging/dbopt${{ matrix.binsuffix }}
7372
cp README.md staging/ 2>/dev/null || true
7473
cp samples/bad.sql staging/sample.sql 2>/dev/null || true
7574
- name: archive
7675
shell: bash
7776
run: |
7877
cd staging
7978
if [ "${{ matrix.archive }}" = "zip" ]; then
80-
7z a ../sqlopt-${{ matrix.label }}.zip ./*
79+
7z a ../dbopt-${{ matrix.label }}.zip ./*
8180
else
82-
tar czf ../sqlopt-${{ matrix.label }}.tar.gz ./*
81+
tar czf ../dbopt-${{ matrix.label }}.tar.gz ./*
8382
fi
8483
- name: macos dmg
8584
if: startsWith(matrix.label, 'macos-')
8685
run: |
8786
VER="${{ github.event.inputs.tag || github.ref_name }}"
8887
VER="${VER#v}" # strip leading v from e.g. v0.2.0
8988
VER="${VER:-0.0.0}" # fall back if somehow empty
90-
mkdir -p sqlopt-${{ matrix.label }}-app/sqlopt.app/Contents/MacOS
91-
cp staging/sqlopt sqlopt-${{ matrix.label }}-app/sqlopt.app/Contents/MacOS/sqlopt
92-
chmod +x sqlopt-${{ matrix.label }}-app/sqlopt.app/Contents/MacOS/sqlopt
93-
cat > sqlopt-${{ matrix.label }}-app/sqlopt.app/Contents/Info.plist <<EOF
89+
mkdir -p dbopt-${{ matrix.label }}-app/dbopt.app/Contents/MacOS
90+
cp staging/dbopt dbopt-${{ matrix.label }}-app/dbopt.app/Contents/MacOS/dbopt
91+
chmod +x dbopt-${{ matrix.label }}-app/dbopt.app/Contents/MacOS/dbopt
92+
cat > dbopt-${{ matrix.label }}-app/dbopt.app/Contents/Info.plist <<EOF
9493
<?xml version="1.0" encoding="UTF-8"?>
9594
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
9695
<plist version="1.0"><dict>
97-
<key>CFBundleExecutable</key><string>sqlopt</string>
98-
<key>CFBundleIdentifier</key><string>dev.sqlopt.observatory</string>
99-
<key>CFBundleName</key><string>sqlopt</string>
96+
<key>CFBundleExecutable</key><string>dbopt</string>
97+
<key>CFBundleIdentifier</key><string>dev.dbopt.observatory</string>
98+
<key>CFBundleName</key><string>dbopt</string>
10099
<key>CFBundlePackageType</key><string>APPL</string>
101100
<key>CFBundleShortVersionString</key><string>${VER}</string>
102101
<key>CFBundleVersion</key><string>1</string>
103102
<key>NSHighResolutionCapable</key><true/>
104103
</dict></plist>
105104
EOF
106-
hdiutil create -volname sqlopt -srcfolder sqlopt-${{ matrix.label }}-app -ov -format UDZO sqlopt-${{ matrix.label }}.dmg
107-
- name: windows installer (NSIS-free zip)
105+
hdiutil create -volname dbopt -srcfolder dbopt-${{ matrix.label }}-app -ov -format UDZO dbopt-${{ matrix.label }}.dmg
106+
- name: windows MSI (best-effort)
108107
if: matrix.label == 'windows-x86_64'
109-
shell: bash
108+
continue-on-error: true
109+
shell: pwsh
110110
run: |
111-
# The .exe IS the installer: double-click runs the server + opens the browser.
112-
# No actual installation step needed because there are no extra files.
113-
echo "windows artifact is the zipped portable .exe"
111+
cargo install cargo-wix --locked
112+
cargo wix init -p backend --force
113+
cargo wix -p backend --no-build --target ${{ matrix.target }} --nocapture --output dbopt-windows-x86_64.msi
114114
- name: linux AppImage (best-effort)
115115
if: matrix.label == 'linux-x86_64'
116116
run: |
117-
mkdir -p sqlopt.AppDir/usr/bin
118-
cp staging/sqlopt sqlopt.AppDir/usr/bin/sqlopt
119-
chmod +x sqlopt.AppDir/usr/bin/sqlopt
120-
cat > sqlopt.AppDir/sqlopt.desktop <<EOF
117+
mkdir -p dbopt.AppDir/usr/bin
118+
cp staging/dbopt dbopt.AppDir/usr/bin/dbopt
119+
chmod +x dbopt.AppDir/usr/bin/dbopt
120+
cat > dbopt.AppDir/dbopt.desktop <<EOF
121121
[Desktop Entry]
122122
Type=Application
123-
Name=sqlopt
124-
Exec=sqlopt
125-
Icon=sqlopt
123+
Name=dbopt
124+
Exec=dbopt
125+
Icon=dbopt
126126
Categories=Development;Database;
127127
Terminal=true
128128
EOF
129-
touch sqlopt.AppDir/sqlopt.png
130-
cat > sqlopt.AppDir/AppRun <<'EOF'
129+
touch dbopt.AppDir/dbopt.png
130+
cat > dbopt.AppDir/AppRun <<'EOF'
131131
#!/bin/sh
132132
HERE="$(dirname "$(readlink -f "${0}")")"
133-
exec "${HERE}/usr/bin/sqlopt" "$@"
133+
exec "${HERE}/usr/bin/dbopt" "$@"
134134
EOF
135-
chmod +x sqlopt.AppDir/AppRun
135+
chmod +x dbopt.AppDir/AppRun
136136
curl -L -o appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage || true
137137
chmod +x appimagetool || true
138-
ARCH=x86_64 ./appimagetool sqlopt.AppDir sqlopt-linux-x86_64.AppImage || \
138+
ARCH=x86_64 ./appimagetool dbopt.AppDir dbopt-linux-x86_64.AppImage || \
139139
echo "AppImage build skipped (appimagetool unavailable)"
140140
- uses: actions/upload-artifact@v4
141141
with:
142-
name: sqlopt-${{ matrix.label }}
142+
name: dbopt-${{ matrix.label }}
143143
path: |
144-
sqlopt-${{ matrix.label }}.tar.gz
145-
sqlopt-${{ matrix.label }}.zip
146-
sqlopt-${{ matrix.label }}.dmg
147-
sqlopt-linux-x86_64.AppImage
144+
dbopt-${{ matrix.label }}.tar.gz
145+
dbopt-${{ matrix.label }}.zip
146+
dbopt-${{ matrix.label }}.dmg
147+
dbopt-windows-x86_64.msi
148+
dbopt-linux-x86_64.AppImage
148149
if-no-files-found: ignore
149150

150151
release:
@@ -159,7 +160,7 @@ jobs:
159160
- name: stage flat
160161
run: |
161162
mkdir -p out
162-
find artifacts -type f \( -name '*.tar.gz' -o -name '*.zip' -o -name '*.dmg' -o -name '*.AppImage' \) -exec cp {} out/ \;
163+
find artifacts -type f \( -name '*.tar.gz' -o -name '*.zip' -o -name '*.dmg' -o -name '*.msi' -o -name '*.AppImage' \) -exec cp {} out/ \;
163164
(cd out && sha256sum * > SHA256SUMS)
164165
ls -la out
165166
- name: gh release

.gitignore

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
1-
target/
1+
# Rust
2+
/target/
3+
4+
# Node / web
25
node_modules/
3-
dist/
6+
web/dist/
47
web/src/wasm/
5-
*.log
6-
.env
8+
9+
# Build & release artifacts — publish via GitHub Releases, never commit
10+
*.tar.gz
11+
*.zip
12+
*.dmg
13+
*.AppImage
14+
*.msi
15+
16+
# Local scratch / render previews
17+
web/_*.mjs
18+
web/_*.png
19+
web/public/_*
20+
21+
# OS / editor
722
.DS_Store
823

9-
# Local Claude Code session data
10-
.claude/
11-
web/tsconfig.tsbuildinfo
24+
# TS build cache
25+
*.tsbuildinfo

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,6 @@ positive and a negative scenario.
8484

8585
- Keep new code in the style of the surrounding file (the analyzer is
8686
deliberately dependency-light and token-level).
87-
- Commits in this repo use a PII-free signature (`sqlopt <sqlopt@localhost>`).
87+
- Commits in this repo use a PII-free signature (`dbopt <dbopt@localhost>`).
8888
- See `docs/ROADMAP.md` for the multi-engine direction before adding
8989
engine-specific work.

README.md

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919

2020
<p align="center">
2121
<a href="https://dbopt.org"><b>dbopt.org</b></a>
22+
&nbsp;·&nbsp; <a href="docs/USAGE.md">Usage guide</a>
2223
&nbsp;·&nbsp; <a href="docs/WHO-IS-DBOPT-FOR.md">Who it's for</a>
23-
&nbsp;·&nbsp; <a href="docs/ACCESS.md">Access &amp; permissions</a>
24+
&nbsp;·&nbsp; <a href="docs/ACCESS.md">Access</a>
2425
&nbsp;·&nbsp; <a href="docs/ROADMAP-TO-COMPLETE.md">Roadmap</a>
2526
</p>
2627

@@ -43,16 +44,26 @@ cloud model.
4344

4445
## Download
4546

46-
Prebuilt — **no toolchain needed**. Grab the installer for your OS:
47+
**Install in one line** (no toolchain needed):
48+
49+
```bash
50+
# Linux & macOS (Apple Silicon)
51+
curl -fsSL https://dbopt.org/install.sh | sh
52+
```
53+
```powershell
54+
# Windows (PowerShell)
55+
irm https://dbopt.org/install.ps1 | iex
56+
```
57+
58+
…or grab an installer directly:
4759

4860
| Platform | Download |
4961
|---|---|
50-
| **Windows** (x64) | **[portable `.zip`](https://github.com/singhpratech/dbopt/releases/latest/download/sqlopt-windows-x86_64.zip)** |
51-
| **macOS** (Apple Silicon) | **[`.dmg`](https://github.com/singhpratech/dbopt/releases/latest/download/sqlopt-macos-arm64.dmg)** |
52-
| **macOS** (Intel) | **[`.dmg`](https://github.com/singhpratech/dbopt/releases/latest/download/sqlopt-macos-x86_64.dmg)** |
53-
| **Linux** (x64) | **[`.AppImage`](https://github.com/singhpratech/dbopt/releases/latest/download/sqlopt-linux-x86_64.AppImage)** · [`.tar.gz`](https://github.com/singhpratech/dbopt/releases/latest/download/sqlopt-linux-x86_64.tar.gz) |
62+
| **Windows** (x64) | **[`.msi`](https://github.com/singhpratech/dbopt/releases/latest/download/dbopt-windows-x86_64.msi)** · [portable `.zip`](https://github.com/singhpratech/dbopt/releases/latest/download/dbopt-windows-x86_64.zip) |
63+
| **macOS** (Apple Silicon) | **[`.dmg`](https://github.com/singhpratech/dbopt/releases/latest/download/dbopt-macos-arm64.dmg)** |
64+
| **Linux** (x64) | **[`.AppImage`](https://github.com/singhpratech/dbopt/releases/latest/download/dbopt-linux-x86_64.AppImage)** · [`.tar.gz`](https://github.com/singhpratech/dbopt/releases/latest/download/dbopt-linux-x86_64.tar.gz) |
5465

55-
Each is a **single self-contained binary** (the web UI is embedded) — run it, then open `http://127.0.0.1:3690`. Checksums and all builds on the [releases page](https://github.com/singhpratech/dbopt/releases).
66+
Each is a **single self-contained binary** (the web UI is embedded) — run it, then open `http://127.0.0.1:3690`. New to dbopt? See the **[Usage guide](docs/USAGE.md)**. Checksums and all builds are on the [releases page](https://github.com/singhpratech/dbopt/releases).
5667

5768
## Why it exists
5869

@@ -114,14 +125,14 @@ cd web && npm install && npm run build && cd ..
114125
cargo build --release
115126

116127
# 1) Analyze a script statically — no DB connection needed
117-
./target/release/sqlopt path/to/query.sql
128+
./target/release/dbopt path/to/query.sql
118129

119130
# 2) Run the web observatory (UI + API on :3690)
120-
./target/release/sqlopt-backend # then open http://127.0.0.1:3690
131+
./target/release/dbopt-backend # then open http://127.0.0.1:3690
121132

122133
# 3) Continuous monitoring (SQL auth via env)
123-
SQLOPT_SERVER="host,1433" SQLOPT_USER="..." SQLOPT_PASSWORD="..." \
124-
./target/release/sqlopt-sentinel run
134+
DBOPT_SERVER="host,1433" DBOPT_USER="..." DBOPT_PASSWORD="..." \
135+
./target/release/dbopt-sentinel run
125136
```
126137

127138
For UI development: `cd web && npm install && npm run dev` (proxies the API to the backend on :3690).
@@ -147,13 +158,13 @@ A Rust workspace plus a React / Vite / TypeScript front end:
147158
|---|---|
148159
| `analyzer-core` | the rule engine + tokenizer + plan/DMV models |
149160
| `analyzer-wasm` | WebAssembly bindings for in-browser analysis |
150-
| `analyzer-cli` | `sqlopt` — analyze a `.sql` / `.sqlplan` / bundle from the shell |
151-
| `backend` | `sqlopt-backend` — axum API + embedded web UI, LLM proxy, durable logs |
152-
| `sentinel` | `sqlopt-sentinel` — continuous DMV poller → SQLite → pain report |
161+
| `analyzer-cli` | `dbopt` — analyze a `.sql` / `.sqlplan` / bundle from the shell |
162+
| `backend` | `dbopt-backend` — axum API + embedded web UI, LLM proxy, durable logs |
163+
| `sentinel` | `dbopt-sentinel` — continuous DMV poller → SQLite → pain report |
153164
| `eval` | the rule-quality harness (precision / recall / F1 + HTML report) |
154165
| `web/` | the "observatory" UI (analysis, plans, charts, AI, monitoring) |
155166

156-
Storage and config live under `~/.sqlopt/` (override with `SQLOPT_DATA_DIR`). No external services required.
167+
Storage and config live under `~/.dbopt/` (override with `DBOPT_DATA_DIR`). No external services required.
157168

158169
## Roadmap — one brand, every engine
159170

crates/analyzer-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edition.workspace = true
55
license.workspace = true
66

77
[[bin]]
8-
name = "sqlopt"
8+
name = "dbopt"
99
path = "src/main.rs"
1010

1111
[dependencies]

crates/analyzer-cli/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fn main() -> anyhow::Result<()> {
2020
input.sql = Some(body);
2121
}
2222
} else {
23-
eprintln!("usage: sqlopt <file.sql | file.sqlplan | bundle.json> | sqlopt --stdin");
23+
eprintln!("usage: dbopt <file.sql | file.sqlplan | bundle.json> | dbopt --stdin");
2424
std::process::exit(2);
2525
}
2626

crates/backend/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edition.workspace = true
55
license.workspace = true
66

77
[[bin]]
8-
name = "sqlopt-backend"
8+
name = "dbopt-backend"
99
path = "src/main.rs"
1010

1111
[dependencies]

crates/backend/src/logs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Durable log endpoints (AI interactions + analysis history).
22
//!
33
//! Storage shares the same SQLite file the sentinel daemon writes to
4-
//! (`~/.sqlopt/sentinel.db`), so logs survive backend restarts and browser
4+
//! (`~/.dbopt/sentinel.db`), so logs survive backend restarts and browser
55
//! cache clears. The schema lives in sentinel/migrations/0003_logs.sql.
66
77
use axum::{extract::Query, http::StatusCode, response::IntoResponse, Json};

crates/backend/src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ async fn main() -> anyhow::Result<()> {
5151

5252
// Open the user's browser (best effort) once the listener is ready.
5353
let url_for_open = url.clone();
54-
let no_open = std::env::var("SQLOPT_NO_OPEN").is_ok();
54+
let no_open = std::env::var("DBOPT_NO_OPEN").is_ok();
5555
tokio::spawn(async move {
5656
// Tiny delay so axum is definitely accepting before we hand the URL to the browser.
5757
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
@@ -80,12 +80,12 @@ fn print_banner(url: &str, port: u16) {
8080
let green = "\x1b[38;2;212;255;78m";
8181
let reset = "\x1b[0m";
8282
eprintln!();
83-
eprintln!("{green}{bold}▣ sqlopt{reset} {dim}/ observatory{reset}");
83+
eprintln!("{green}{bold}▣ dbopt{reset} {dim}/ observatory{reset}");
8484
eprintln!("{dim} SQL Server static + plan + DMV analyzer · Rust + WASM{reset}");
8585
eprintln!();
8686
eprintln!(" {bold}{green} → {url} {reset}");
8787
eprintln!();
88-
eprintln!("{dim} port {port} · set SQLOPT_NO_OPEN=1 to skip auto-browser{reset}");
88+
eprintln!("{dim} port {port} · set DBOPT_NO_OPEN=1 to skip auto-browser{reset}");
8989
eprintln!("{dim} press ctrl-c to stop{reset}");
9090
eprintln!();
9191
}

0 commit comments

Comments
 (0)