Skip to content

fix: improve CLI app serving and publishing - #755

Merged
ilyabo merged 9 commits into
mainfrom
cli-for-publishing-fixes
Jun 22, 2026
Merged

ilyabo merged 9 commits into
mainfrom
cli-for-publishing-fixes

Conversation

@ilyabo

@ilyabo ilyabo commented Jun 22, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Serve the SPA index with Cache-Control: no-store so the browser does not cache the entry shell.
  • Resolve built static files directly and return them from the launcher instead of relying on StaticFiles.
  • Redirect stale Vite entry asset requests to the current hashed JS/CSS file, and return a 404 for missing non-entry assets.
  • Add launcher tests covering index caching, static asset serving, stale entry redirects, and 404 handling.

Testing

  • Unit tests added for the launcher response paths described above.
  • Not run (not requested).

Summary by CodeRabbit

Release Notes

  • New Features
    • Added --debug to the CLI for more verbose logging.
    • Added publish-cli and publish-cli:check for releasing packages.
  • Bug Fixes
    • Disabled browser caching for UI entry/asset redirects and improved UI static routing (including stale hashed asset redirects) with clearer 404/JSON errors for missing UI/API routes.
  • UI/UX Changes
    • Removed prompt suggestions above the chat composer.
  • Behavioral Change
    • CLI default HTTP port is now 3000; database selection is now required unless --db-path is provided.
  • Tests & Documentation
    • Updated launcher/CLI tests and refreshed local dev/smoke-test documentation and default UI/port guidance.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces the StaticFiles(html=True) mount in SqlroomsHttpServer with explicit GET / and GET /{full_path:path} FastAPI route handlers that enforce no-store caching and redirect stale hashed Vite entry assets. Makes database path required in the CLI (either positional or via --db-path) with validation and error messaging. Adds a --debug flag that propagates through the stack to control logging verbosity. Coordinates development and default HTTP ports across the codebase (CLI port 3000, UI dev port 3100, API proxy 4273). Adds a new Node.js publishing script and npm scripts to orchestrate release of Python packages. Removes prompt suggestions UI from the chat composer and adds local smoke testing documentation.

Changes

SPA Routing Overhaul

Layer / File(s) Summary
Cache constant and static-file helper methods
python/sqlrooms/sqlrooms/web/launcher.py
Reformats the fastapi.responses import, adds NO_STORE_HEADERS constant, and introduces three private methods: _index_response() returns index.html with no-store headers, _resolve_static_file() safely resolves paths under static_dir with traversal protection and index.html exclusion, and _stale_entry_asset_redirect() detects mismatched Vite entry assets and returns a 302 redirect to the canonical asset.
Explicit SPA route handlers
python/sqlrooms/sqlrooms/web/launcher.py
Replaces the StaticFiles mount with GET / (returns index.html with no-store) and GET /{full_path:path} (delegates to helpers for static files and stale-asset redirects, returns JSON 404 for missing assets/* and unknown api/* paths, falls back to index.html when available).
Comprehensive UI routing tests
python/sqlrooms/tests/test_launcher.py
Tests verify GET / and HEAD / return 200 with cache-control: no-store, existing assets under /assets/ return 200 with correct content, stale entry asset requests return 302 redirects with no-store, missing non-entry assets and unknown API routes return JSON 404.

CLI Database Path Validation

Layer / File(s) Summary
CLI option definition and early validation
python/sqlrooms/sqlrooms/cli.py
The --db-path/-d option changes from defaulting to :memory: to defaulting to None with updated help text; main callback validates resolved_db_path early from positional db_path or --db-path, printing an error message with example and exiting with code 1 if both are missing; redundant later reassignment is removed.
Documentation and tests for required database path
python/sqlrooms/README.md, python/sqlrooms/tests/test_cli.py
README clarifies that DB_PATH is required unless --db-path is provided and documents --db-path as accepting either a filepath or :memory: for a temporary session; test_cli_help verifies the example appears in help output; new test_cli_requires_database_path ensures missing args exit with code 1 and emit the error message.

Debug & Logging Control

Layer / File(s) Summary
CLI debug option and logging configuration
python/sqlrooms/sqlrooms/cli.py
Adds _configure_logging() helper to adjust root logger level based on debug flag; adds --debug Typer option; calls _configure_logging() early in main; switches connector and AI runtime config logging from info to debug; passes debug flag to SqlroomsHttpServer.
Server-side debug flag and conditional logging
python/sqlrooms-server/sqlrooms/server/server.py
server() gains keyword-only log_startup_message parameter (default True); startup message at app.listen() is now conditionally printed; query completion and websocket close logging downgrade from info to debug.
SqlroomsHttpServer debug integration
python/sqlrooms/sqlrooms/web/launcher.py
SqlroomsHttpServer.__init__ gains debug parameter; stores as self.debug; meta DB startup logging switches to debug based on status; Uvicorn log_level depends on debug, access_log set from debug; log_startup_message=self.debug passed to DuckDB websocket backend.

Development and Default Port Coordination

Layer / File(s) Summary
CLI port defaults and configuration
python/sqlrooms/sqlrooms/cli.py, python/sqlrooms/scripts/dev.mjs
Updates DEFAULT_HTTP_PORT from 4173 to 3000; updates dev.mjs fallback to use --port 4273 instead of --port 4173 for Python API port when no explicit port is provided.
UI development server port configuration
apps/sqlrooms-cli-ui/package.json, apps/sqlrooms-cli-ui/vite.config.ts, scripts/dev.mjs
Dev script port changes from 4174 to 3100; Vite server.port changes from 4174 to 3100, preview.port from 4175 to 3101; CLI_DEV_UI_DEFAULT_PORT constant updated from 4174 to 3100 for port allocation.
API proxy target and port coordination
apps/sqlrooms-cli-ui/vite.config.ts
Vite dev server apiProxyTarget default updated from http://localhost:4173 to http://localhost:4273 for correct API routing.
Deployment and example port updates
scripts/deploy-sqlrooms-cli-to-sprite.sh
LOCAL_HTTP_PORT default changed from 4173 to 3000; help text updated to reflect the new default.
Documentation updates reflecting port changes
python/sqlrooms/README.md, apps/sqlrooms-cli-ui/README.md
Quick-start examples updated to reference port 3000 for CLI UI; developer setup docs updated to show port 3100 for dev UI and sqlrooms-3100.db for dev database naming; Postgres/Snowflake examples use port 3000; dev mode documentation reflects Vite port 3100 and API proxy target 4273.
CLI port resolution tests
python/sqlrooms/tests/test_cli.py
Test mocks updated to use _pick_free_port value of 3001 (reflecting new default base of 3000); assertions updated for resolved port values and websocket port reservations.

Python Package Publishing Infrastructure

Layer / File(s) Summary
Package publishing orchestration script
scripts/publish-sqlrooms-cli.mjs
New Node.js CLI script defines target packages (sqlrooms-server, sqlrooms), parses command-line arguments (--check-only, --skip-checks, --help), provides a run() helper for executing pnpm commands via spawnSync with inherited stdio, and orchestrates the release flow: running prerelease checks unless --skip-checks is set, exiting after checks if --check-only is provided, and otherwise running the release step for each package in order.
NPM scripts for publishing
package.json
Adds two new script entries: publish-cli runs the publishing script, and publish-cli:check runs it with --check-only flag for validation without actual release.

UI and Product Updates

Layer / File(s) Summary
Removed prompt suggestions from chat
apps/sqlrooms-cli-ui/src/components/AssistantChatContainer.tsx
Chat.PromptSuggestions and its visibility toggle are removed from the composer area; only the Chat.Composer with placeholder and contextDropTarget remains.
Local CLI smoke testing documentation
apps/sqlrooms-cli-ui/README.md
New section documents pre-release smoke testing paths (workspace package with pnpm build and uv run; installed CLI on PATH) and a step-by-step UI verification checklist using cars.csv fixture with restart persistence checks.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant HttpServer
  participant _resolve_static_file
  participant _stale_entry_asset_redirect
  participant FileSystem

  Client->>HttpServer: GET /
  HttpServer->>HttpServer: _index_response()
  HttpServer-->>Client: 200 FileResponse index.html (no-store)

  Client->>HttpServer: GET /assets/index-current.js
  HttpServer->>_resolve_static_file: resolve "assets/index-current.js"
  _resolve_static_file->>FileSystem: check static_dir/assets/index-current.js
  FileSystem-->>_resolve_static_file: file exists
  _resolve_static_file-->>HttpServer: resolved Path
  HttpServer-->>Client: 200 FileResponse (asset content)

  Client->>HttpServer: GET /assets/index-stale.js
  HttpServer->>_resolve_static_file: resolve "assets/index-stale.js"
  _resolve_static_file->>FileSystem: check static_dir/assets/index-stale.js
  FileSystem-->>_resolve_static_file: not found
  _resolve_static_file-->>HttpServer: None
  HttpServer->>_stale_entry_asset_redirect: check for match
  _stale_entry_asset_redirect->>FileSystem: find index-* in assets/
  FileSystem-->>_stale_entry_asset_redirect: index-current.js
  _stale_entry_asset_redirect-->>HttpServer: redirect target
  HttpServer-->>Client: 302 RedirectResponse (no-store)

  Client->>HttpServer: GET /assets/not-built.js
  HttpServer->>_resolve_static_file: resolve "assets/not-built.js"
  _resolve_static_file->>FileSystem: check static_dir/assets/not-built.js
  FileSystem-->>_resolve_static_file: not found
  _resolve_static_file-->>HttpServer: None
  HttpServer->>_stale_entry_asset_redirect: check for match
  _stale_entry_asset_redirect->>FileSystem: find matching index-*
  FileSystem-->>_stale_entry_asset_redirect: no match found
  _stale_entry_asset_redirect-->>HttpServer: None
  HttpServer-->>Client: 404 JSONResponse

  Client->>HttpServer: GET /api/not-a-route
  HttpServer-->>Client: 404 JSONResponse error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Possibly related PRs

  • sqlrooms/sqlrooms#743: Removes Chat.PromptSuggestions from AssistantChatContainer, directly modifying the same component behavior as this PR.
  • sqlrooms/sqlrooms#754: Modifies SqlroomsHttpServer initialization and wiring in launcher.py with a separate constructor flag, overlapping at the same server integration layer.

Suggested reviewers

  • dmitriy-kostianetskiy
  • lixun910

Poem

🐇 Hoppity-hop through the asset maze,
No stale JS to cloud my gaze!
index- hashed? I'll find the right one,
no-store headers — caching, begone!
A DB path required, no guessing games,
And debug flags light the way through all the names. 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: improving CLI app serving (through no-store headers, direct static file serving, stale asset handling) and publishing (new publish-cli scripts). It is concise and clearly describes the primary improvements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cli-for-publishing-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f5d116aa0c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

async def spa_index():
return self._index_response()

@app.get("/{full_path:path}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve HEAD support for static UI assets

For HEAD requests to built assets (for example proxy/cache validation against /assets/index-*.js), this catch-all is registered only as GET; FastAPI does not add HEAD for @app.get, so those requests now return 405 even though the removed StaticFiles handler accepted both GET and HEAD. Register the static/index routes for HEAD as well or keep StaticFiles for those paths.

Useful? React with 👍 / 👎.

Comment on lines 1161 to +1162
if self.index_html.exists():
return FileResponse(self.index_html)
return self._index_response()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep API misses out of the SPA fallback

When a GET request uses a missing or wrong-method API URL such as /api/nope or GET /api/upload, it now reaches this SPA fallback and returns index.html with HTTP 200 because the path is not under assets/. That turns API errors into successful HTML responses for clients expecting JSON, so exclude API-prefixed paths from the SPA fallback before serving the index.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@python/sqlrooms/sqlrooms/web/launcher.py`:
- Around line 1146-1162: In the spa_fallback function, add a check for API paths
that do not match any explicit route. Before returning the index.html response
at the end, add a condition that checks if full_path starts with "api/". If it
does, return a JSONResponse with an error message and a 404 status code (similar
to how the "assets/" check is handled). This ensures that unknown API paths like
/api/unknown return a proper JSON 404 error instead of falling through to the
SPA index response.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fb8a2fc5-dcef-4127-a566-694ed9b33803

📥 Commits

Reviewing files that changed from the base of the PR and between 8245678 and f5d116a.

📒 Files selected for processing (2)
  • python/sqlrooms/sqlrooms/web/launcher.py
  • python/sqlrooms/tests/test_launcher.py

Comment thread python/sqlrooms/sqlrooms/web/launcher.py Outdated
@ilyabo ilyabo changed the title Handle stale UI asset requests in the Python launcher fix: CLI for publishing fixes Jun 22, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 469c51dd2b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread apps/sqlrooms-cli-ui/README.md Outdated
Comment on lines +21 to +23
uv run --project python --package sqlrooms sqlrooms \
./smoke.duckdb \
--no-open-browser

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Move CLI options before the database argument

I checked the generated Typer help, which reports Usage: root [OPTIONS] [DB_PATH] COMMAND [ARGS]...; with this command group, placing --no-open-browser after ./smoke.duckdb is parsed as a command and exits with No such command '--no-open-browser' instead of launching. The new smoke-test commands repeat this ordering, so users following the local/release smoke docs cannot run them; put --no-open-browser before the DB path.

Useful? React with 👍 / 👎.

@ilyabo ilyabo changed the title fix: CLI for publishing fixes fix: improve CLI app serving and publishing Jun 22, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c87cc2536f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

self.sync_enabled and self.duckdb_database == ":memory:"
),
local_only=True,
log_startup_message=self.debug,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Bump sqlrooms-server before using the new keyword

When a released sqlrooms is installed alongside sqlrooms-server 0.1.1 (still allowed by python/sqlrooms/pyproject.toml, and the server package version is unchanged), this call passes log_startup_message to the old server() signature, which did not accept that keyword in the parent commit. The exception is caught as a backend startup failure, so the UI starts degraded and DuckDB queries cannot run; either avoid the new keyword or bump sqlrooms-server and the dependency lower bound together.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
python/sqlrooms/sqlrooms/cli.py (1)

522-530: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate that the database path is not empty or whitespace-only.

The validation checks for None but does not verify that resolved_db_path is a non-empty string. Users could pass sqlrooms "" or --db-path "", which would bypass this check and potentially cause confusing downstream errors.

🛡️ Proposed fix to add empty string validation
 resolved_db_path = db_path if db_path is not None else db_path_option
-if resolved_db_path is None:
+if not resolved_db_path or not resolved_db_path.strip():
     typer.echo(
         "Please provide a DuckDB project file, e.g. `sqlrooms ./my-project.duckdb`, "
         "or pass `--db-path :memory:` for a temporary in-memory session.",
         err=True,
     )
     raise typer.Exit(code=1)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/sqlrooms/sqlrooms/cli.py` around lines 522 - 530, The validation check
for resolved_db_path only verifies if it is None, but does not check for empty
strings or whitespace-only strings. Modify the condition that currently checks
if resolved_db_path is None to also validate that the string is not empty after
stripping whitespace. Use Python's string methods like .strip() to ensure the
resolved_db_path contains actual non-whitespace content, so that attempts to
pass empty strings like "" or whitespace-only strings will trigger the same
error message and exit as the None case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@python/sqlrooms/sqlrooms/cli.py`:
- Around line 522-530: The validation check for resolved_db_path only verifies
if it is None, but does not check for empty strings or whitespace-only strings.
Modify the condition that currently checks if resolved_db_path is None to also
validate that the string is not empty after stripping whitespace. Use Python's
string methods like .strip() to ensure the resolved_db_path contains actual
non-whitespace content, so that attempts to pass empty strings like "" or
whitespace-only strings will trigger the same error message and exit as the None
case.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: afe58395-982c-4723-b0d0-ce53cc9423fd

📥 Commits

Reviewing files that changed from the base of the PR and between 7bc2b3c and c87cc25.

📒 Files selected for processing (9)
  • apps/sqlrooms-cli-ui/README.md
  • apps/sqlrooms-cli-ui/package.json
  • apps/sqlrooms-cli-ui/vite.config.ts
  • python/sqlrooms/README.md
  • python/sqlrooms/scripts/dev.mjs
  • python/sqlrooms/sqlrooms/cli.py
  • python/sqlrooms/tests/test_cli.py
  • scripts/deploy-sqlrooms-cli-to-sprite.sh
  • scripts/dev.mjs
✅ Files skipped from review due to trivial changes (3)
  • scripts/deploy-sqlrooms-cli-to-sprite.sh
  • python/sqlrooms/README.md
  • apps/sqlrooms-cli-ui/README.md

@ilyabo
ilyabo merged commit 7924b5a into main Jun 22, 2026
4 checks passed
@ilyabo
ilyabo deleted the cli-for-publishing-fixes branch June 22, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant