Skip to content

Add --preview flag to snow streamlit deploy for personal-workspace deploys#3002

Draft
sfc-gh-svishnu wants to merge 1 commit into
mainfrom
svishnu-streamlit-preview
Draft

Add --preview flag to snow streamlit deploy for personal-workspace deploys#3002
sfc-gh-svishnu wants to merge 1 commit into
mainfrom
svishnu-streamlit-preview

Conversation

@sfc-gh-svishnu
Copy link
Copy Markdown
Contributor

Pre-review checklist

  • I've confirmed that instructions included in README.md are still correct after my changes in the codebase.
  • I've added or updated automated unit tests to verify correctness of my new code.
  • I've added or updated integration tests to verify correctness of my new code.
  • I've confirmed that my changes are working by executing CLI's commands manually on MacOS.
  • I've confirmed that my changes are working by executing CLI's commands manually on Windows.
  • I've confirmed that my changes are up-to-date with the target branch.
  • I've described my changes in the release notes.
  • I've described my changes in the section below.
  • I've described my changes in the documentation.

Changes description

Context

Streamlit developers iterating on a SiS app today have to deploy to a real database and stage in their account every time they want to preview a change. That requires picking a target DB/schema, owning a stage, and (for SPCS apps) uploading artifacts to a per-app versioned stage and running ALTER STREAMLIT … ADD LIVE VERSION FROM LAST. For quick "is this change working?" iterations against the SPCS container runtime, that's heavier than it needs to be — and on accounts where the developer doesn't own a database it's outright blocking.

This PR adds a --preview flag to snow streamlit deploy that targets the user's personal database (user$.public.<name>) and sources the app from the user's default workspace live version (snow://workspace/USER$.PUBLIC.DEFAULT$/versions/live). Local artifacts are still bundled from snowflake.yml and uploaded to a per-entity subfolder under the workspace; the streamlit object is created with CREATE_CODE_STAGE = FALSE and FROM '<workspace_uri>'. SPCS container runtime fields (RUNTIME_NAME, COMPUTE_POOL) are honored when present in snowflake.yml. No ADD LIVE VERSION, no separate stage to manage.

What this enables

  • One-flag personal previewsnow streamlit deploy --preview deploys to user$.public.<name>, scoped to the current user. No need to choose or own a database.
  • Workspace live version as the source — files go into a per-entity subfolder under snow://workspace/USER$.PUBLIC.DEFAULT$/versions/live/<entity_id>/, and the streamlit reads from the workspace live URI (no ALTER STREAMLIT … ADD LIVE VERSION round-trip).
  • SPCS v2 runtime support — when runtime_name: SYSTEM$ST_CONTAINER_RUNTIME_PY3_11 and compute_pool: … are set in snowflake.yml, the emitted SQL includes the matching RUNTIME_NAME / COMPUTE_POOL clauses. Warehouse-runtime previews drop those clauses but still emit CREATE_CODE_STAGE = FALSE.
  • Snowsight URL with the resolved personal DBuser$ is a server-side alias resolved at SQL execution time; the CLI resolves it client-side via SELECT 'USER$' || CURRENT_USER() so the Snowsight URL points at USER$<USERNAME>.PUBLIC.<NAME> (not the literal user%24).
  • Safe defaults for the shared workspace — the workspace live version is shared across an account user's preview deploys, so --prune is rejected up-front (it would prune unrelated files), and force_overwrite=True is scoped to the per-entity subfolder. grants: from snowflake.yml are skipped with a console warning because they target the YAML FQN, not the preview FQN — applying them in preview mode would either fail or silently grant on a different streamlit object.
  • Clean failure modes--preview --legacy and --preview --prune error out with CliError before any work happens. Re-deploying without --replace errors with a clear "already exists" message.

Sample emitted SQL

CREATE OR REPLACE STREAMLIT user$.public.testapp_vnext_private
FROM 'snow://workspace/USER$.PUBLIC.DEFAULT$/versions/live'
MAIN_FILE = 'streamlittestapp/streamlit_app.py'
QUERY_WAREHOUSE = REGRESS
CREATE_CODE_STAGE = FALSE
RUNTIME_NAME = 'SYSTEM$ST_CONTAINER_RUNTIME_PY3_11'
COMPUTE_POOL = 'MYPOOL';

(Warehouse-runtime previews omit the RUNTIME_NAME / COMPUTE_POOL lines.)

How it works (user-facing flow)

  1. The user runs snow streamlit deploy --preview (optionally with --replace) from a project with snowflake.yml.
  2. The CLI fail-fasts on --preview --legacy and --preview --prune before any project work.
  3. v1 → v2 conversion runs as usual.
  4. Local artifacts are bundled from snowflake.yml (bundle()).
  5. The CLI checks whether user$.public.<name> already exists via DESCRIBE STREAMLIT; without --replace, this aborts with ClickException.
  6. Bundled artifacts are uploaded under snow://workspace/USER$.PUBLIC.DEFAULT$/versions/live/<entity_id>/. prune=False and force_overwrite=True are forced to keep other workspace files untouched while still updating this preview's files.
  7. CREATE [OR REPLACE] STREAMLIT user$.public.<name> FROM '…/versions/live' … is executed (with SPCS clauses if applicable).
  8. grants: from snowflake.yml are skipped with a console warning if present.
  9. The Snowsight URL is built using the resolved personal database name.

Flow diagram

flowchart TD
    A["snow streamlit deploy --preview [--replace]"] --> B{"--preview --legacy<br/>or --preview --prune?"}
    B -- Yes --> Z[CliError: incompatible flags]
    B -- No --> C[v1 to v2 conversion if needed]
    C --> D["bundle() local artifacts"]
    D --> E["DESCRIBE STREAMLIT user$.public.&lt;name&gt;"]
    E --> F{Exists & no --replace?}
    F -- Yes --> Y[ClickException: already exists]
    F -- No --> G["sync_deploy_root_with_stage<br/>to .../versions/live/&lt;entity_id&gt;/<br/>(prune=False, force_overwrite=True)"]
    G --> H{SPCS runtime in yaml?}
    H -- Yes --> I["CREATE STREAMLIT ... FROM workspace<br/>+ RUNTIME_NAME + COMPUTE_POOL<br/>+ CREATE_CODE_STAGE = FALSE"]
    H -- No --> J["CREATE STREAMLIT ... FROM workspace<br/>+ CREATE_CODE_STAGE = FALSE"]
    I --> K{Has grants in yaml?}
    J --> K
    K -- Yes --> L["console.warning:<br/>Skipping grants in --preview mode"]
    K -- No --> M["StreamlitManager.grant_privileges()"]
    L --> N["Resolve personal DB:<br/>SELECT 'USER$' || CURRENT_USER()"]
    M --> N
    N --> O["Snowsight URL:<br/>USER$<USER>.PUBLIC.<NAME>"]
Loading

Files added/modified

src/snowflake/cli/_plugins/streamlit/
    ├── commands.py                 # modified — `PreviewOption`, `--preview` flag, fail-fast on --legacy/--prune
    ├── streamlit_entity.py         # modified — preview branch in `deploy()`, `_deploy_preview`,
    │                               #   `get_preview_deploy_sql`, `_preview_object_exists`,
    │                               #   `_get_preview_url`, `_preview_identifier` helper
    └── streamlit_entity_model.py   # modified — `PREVIEW_DATABASE`, `PREVIEW_SCHEMA`,
                                    #   `PREVIEW_WORKSPACE_STAGE_URI` constants

tests/streamlit/
    ├── test_streamlit_commands.py  # modified — 3 CLI-level tests (SPCS runtime, --legacy/--prune errors)
    └── test_streamlit_entity.py    # modified — 11 unit tests (SQL builder shape, upload destination,
                                    #   skip-grants behavior, mutual-exclusion errors, no ADD LIVE VERSION)

RELEASE-NOTES.md                    # modified — bullet under "New additions"

No new files; preview is layered onto the existing _deploy_legacy / _deploy_versioned dispatch as a third sibling _deploy_preview.

Testing

  • 102 streamlit unit + command tests pass (hatch run pytest tests/streamlit/).
  • Manual MacOS verification against snowhouse_connection end-to-end: hatch run snow streamlit deploy --preview --replace --connection snowhouse_connection --project … --format json deploys, files land at snow://workspace/USER$.PUBLIC.DEFAULT$/versions/live/<entity_id>/, the streamlit is created at user$.public.<name> with the SPCS runtime + compute pool clauses, and the returned URL correctly uses the resolved USER$<USERNAME>.PUBLIC.<NAME> identifier.

Out-of-scope follow-ups

  • pyproject.toml requirement for SPCSv2 apps — the SiS container runtime requires a pyproject.toml for dependency installation, but the existing in-tree fixture (tests_integration/test_data/projects/streamlit_spcs_v2/) ships only requirements.txt. Affects both preview and non-preview SPCS deploys — worth a follow-up to auto-generate a minimal pyproject.toml from requirements.txt when runtime_name indicates SPCSv2.
  • snow streamlit describe / drop for preview-deployed apps — both subcommands resolve via the YAML FQN, not user$.public.<name>. Out of scope here; worth a follow-up if real users hit it.

Made with Cursor

…ploys

Co-authored-by: Cursor <cursoragent@cursor.com>
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