diff --git a/docs/source/developers/apps/app_catalog.rst b/docs/source/developers/apps/app_catalog.rst index c27929dbbc..92b47f8578 100644 --- a/docs/source/developers/apps/app_catalog.rst +++ b/docs/source/developers/apps/app_catalog.rst @@ -10,9 +10,9 @@ The catalog The list of apps SEPAL runs lives in a dedicated repository, `dfguerrerom/sepal-apps-catalog `__. For docker apps the catalog pins the exact commit SEPAL will run; for jupyter -and shiny apps it records the ``branch`` and SEPAL runs its tip. Either way, -adding or updating an app means opening a pull request against this catalog, -where a maintainer reviews the change before it goes live. +apps it records the ``branch`` and SEPAL runs its tip. Either way, adding or +updating an app means opening a pull request against this catalog, where a +maintainer reviews the change before it goes live. The catalog ships two files: @@ -55,8 +55,9 @@ Identification and routing - Display name shown on the apps dashboard. * - ``endpoint`` - always - - How SEPAL runs the app: ``jupyter``, ``shiny``, ``rstudio`` or - ``docker``. See :ref:`developers_apps_types`. + - How SEPAL runs the app: ``jupyter`` or ``docker`` for new apps — + ``shiny`` is legacy and ``rstudio`` is reserved for the built-in + tool. See :ref:`developers_apps_types`. * - ``repository`` - always - ``https://github.com//`` of the app source. SEPAL clones @@ -69,10 +70,9 @@ Identification and routing - Entry point the app-launcher routes to. For jupyter, the Voila URL of the notebook (e.g. ``/sandbox/jupyter/voila/render/shared/apps//ui.ipynb``); for - shiny, the sandbox path of the Shiny project; for docker, **must - equal** ``/api/app-launcher/`` exactly — the schema enforces the - prefix and ``check-docker-rules.js`` enforces that ```` matches - the entry's ``id``. + docker, **must equal** ``/api/app-launcher/`` exactly — the + schema enforces the prefix and ``check-docker-rules.js`` enforces + that ```` matches the entry's ``id``. Docker-only fields ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/source/developers/apps/app_types.rst b/docs/source/developers/apps/app_types.rst index 7b9f2af6db..ac52173c0b 100644 --- a/docs/source/developers/apps/app_types.rst +++ b/docs/source/developers/apps/app_types.rst @@ -2,12 +2,17 @@ Docker vs. Jupyter apps ======================= -*Two ways an app runs on SEPAL, with different lifecycles* +*The two endpoints you'll choose between, and where each one fits* Every catalog entry declares an ``endpoint`` that tells SEPAL how to run the -app. The two you will meet most often are ``docker`` and ``jupyter`` (``shiny`` -and ``rstudio`` behave like ``jupyter`` for lifecycle purposes). The choice -affects how the app is isolated, how it is versioned, and how it is updated. +app. For new apps the practical choice is between ``docker`` and ``jupyter``. + +.. note:: + + ``shiny`` is a legacy endpoint and is no longer recommended for new apps — + package the same UX as a Jupyter/Voila app instead. ``rstudio`` is + reserved for the built-in RStudio tool. Both still work, but neither + should be picked for anything new. At a glance ----------- @@ -21,30 +26,37 @@ At a glance - Jupyter app * - ``endpoint`` - ``docker`` - - ``jupyter`` (also ``shiny``, ``rstudio``) - * - Runs in - - Its own container, on its own port - - The shared user sandbox, served through Voila + - ``jupyter`` + * - Where it runs + - Its own container, on its own port, on the shared SEPAL server + - Inside the user's own SEPAL instance (sandbox), served through Voila + * - Where compute happens + - On the shared SEPAL server — all users hit the same instance + - On the user's own SEPAL instance, at the size they have provisioned * - Versioning - Pinned to a commit SHA (required) - - Tracks its branch; no commit pin + - Tracks the catalog's ``branch``; no commit pin * - Code location - - Cloned from ``repository`` - - ``shared/apps//ui.ipynb`` in the sandbox + - Cloned from ``repository`` into the container + - Cloned from ``repository`` into the shared sandbox at + ``/home/sepal-user/shared/apps//`` * - Isolation - Strong — separate container and dependencies - - Shares the sandbox's Python/system stack (plus an - optional packaged kernel) - * - Lifecycle - - Independent; redeploys on commit change - - Tied to the sandbox and its app-manager-built kernel + - Own micromamba kernel built from ``sepal_environment.yml`` + (see :ref:`developers_apps_kernels`) + * - Update trigger + - Catalog PR bumps ``commit`` → app-launcher checks out that commit on + the next launch + - app-manager refreshes the clone to the branch tip and rebuilds the + kernel if the env file changed Docker apps ----------- A docker app lives in its own repository and runs in its own container with a -dedicated ``port``. Because it is fully isolated, the catalog schema requires -``repository``, ``branch``, and ``commit`` for every ``endpoint: docker`` entry. +dedicated ``port`` on the shared SEPAL server. Because it is fully isolated, +the catalog schema requires ``repository``, ``branch``, ``commit``, ``port`` +and ``path`` for every ``endpoint: docker`` entry. When SEPAL launches the app it runs the equivalent of: @@ -54,31 +66,69 @@ When SEPAL launches the app it runs the equivalent of: git checkout --detach That is why the pinned ``commit`` must be reachable from ``branch`` (see -:ref:`developers_apps_releases`). The app's dependencies are baked into its container, so -its lifecycle is independent of the rest of the platform — it changes only when -you bump its commit in the catalog. - -Use a docker app when your tool needs strong isolation, its own service, a -specific port, or a runtime that differs from the SEPAL sandbox. - -Jupyter (and shiny, rstudio) apps ---------------------------------- +:ref:`developers_apps_releases`). The app's dependencies are baked into its +container, so its update cycle is independent of the rest of the platform — it +changes only when you bump its commit in the catalog. + +Because the docker app runs on the shared SEPAL server, every user hits the +same instance. That makes docker a good fit for **lightweight, low-compute +apps that act as a single service for many users** — interfaces over a small +API, lookups, light visualizations, dashboards. It is *not* a good fit for +heavy per-user compute: there is only one container backing all users. + +Jupyter apps +------------ + +A jupyter app does not get its own container. Its code is cloned from the +``repository`` into the shared sandbox under +``/home/sepal-user/shared/apps//``, and its ``ui.ipynb`` notebook is +rendered with `Voila `__ (hence catalog paths +such as ``/sandbox/jupyter/voila/render/shared/apps//ui.ipynb``). +Crucially, it runs **inside each user's own SEPAL instance** — so the compute +budget is whatever the user has provisioned for themselves. + +Versioning is by branch, not by commit: the app-manager keeps the cloned +``shared/apps//`` working tree on the tip of the catalog's ``branch`` and +rebuilds the app's kernel whenever ``sepal_environment.yml`` (or +``requirements.txt``) changes — see :ref:`developers_apps_kernels`. Pushing to +that branch is enough to ship a new version; no catalog PR is required (the +catalog PR is only needed to *list* the app in the first place). + +Because compute lives on the user's instance, a jupyter app is the right +choice for **heavy, scalable, per-user workloads**: anything where the user +needs to be able to size their machine to the job — training pipelines, large +geospatial analyses, model inference, parallel processing. + +When to use which +----------------- + +A simple rule of thumb: -A jupyter app does not get its own container. Its code is checked out into the -shared user sandbox under ``shared/apps//`` and its ``ui.ipynb`` notebook is -rendered with `Voila `__ (hence catalog paths such -as ``/sandbox/jupyter/voila/render/shared/apps//ui.ipynb``). It runs against -the sandbox's Python and system libraries rather than a private container, so it -does not carry a pinned ``commit`` — its lifecycle follows the sandbox and the -kernel built for it by the app-manager. - -That kernel is where a jupyter app declares the environment it needs. Packaging -that environment correctly — so it is built once and reused — is the subject of -:ref:`developers_apps_kernels`. +.. list-table:: + :header-rows: 1 + :widths: 24 38 38 -Use a jupyter app when your tool is a notebook-based UI that fits the SEPAL -sandbox and benefits from being close to the user's files and the shared -geospatial stack. + * - + - Use ``docker`` + - Use ``jupyter`` + * - Compute profile + - Light; same load no matter who is using it + - Heavy or variable; each user should be able to scale their own + instance + * - Usage pattern + - Single backend service serving many users + - Per-user notebook session, isolated per user + * - Versioning needs + - You want strict, audited control of *exactly* which commit is live + - Push-to-branch is a fine release model + * - Runtime needs + - Custom system stack, services, ports, or a non-Python runtime + - Standard SEPAL geospatial / scientific Python stack (extended via + ``sepal_environment.yml``) + +If the answer to most of these points the same way, that's your endpoint. If +the app is genuinely a notebook UI doing heavy compute, jupyter is almost +always the right pick — that is the most common pattern on SEPAL. .. graphviz:: @@ -87,9 +137,9 @@ geospatial stack. node [shape=box, fontname="sans-serif"]; catalog [label="Catalog entry\n(endpoint)"]; - docker [label="Docker app\nprivate container + port\npinned commit"]; - jupyter [label="Jupyter app\nshared sandbox\nVoila + kernel"]; + docker [label="Docker app\nshared SEPAL server\nprivate container + port\npinned commit"]; + jupyter [label="Jupyter app\nuser's own instance\nVoila + kernel\nbranch tip"]; catalog -> docker [label="docker"]; - catalog -> jupyter [label="jupyter / shiny / rstudio"]; + catalog -> jupyter [label="jupyter"]; } diff --git a/docs/source/developers/apps/index.rst b/docs/source/developers/apps/index.rst index 5392ad8724..ae742e6045 100644 --- a/docs/source/developers/apps/index.rst +++ b/docs/source/developers/apps/index.rst @@ -15,7 +15,7 @@ workflow. `dfguerrerom/sepal-apps-catalog `__ repository. Getting an app onto SEPAL — or updating one — means opening a pull request there. For docker apps, the catalog also pins the *exact - commit* SEPAL will run; for jupyter and shiny apps, the catalog records the + commit* SEPAL will run; for jupyter apps, the catalog records the ``branch`` and SEPAL still runs the tip of that branch. The pages below walk through both flows. diff --git a/docs/source/developers/apps/kernels.rst b/docs/source/developers/apps/kernels.rst index 85d97394df..be092ff456 100644 --- a/docs/source/developers/apps/kernels.rst +++ b/docs/source/developers/apps/kernels.rst @@ -5,10 +5,11 @@ Request a new kernel *Package an environment once and share it with every SEPAL user as a named Jupyter kernel* A "kernel" in the SEPAL catalog is really just a special-purpose jupyter app: -the same catalog entry, the same PR flow, the same release lifecycle. The only -difference is *intent* — the deliverable is the environment, not a notebook UI. -If you have read :ref:`developers_apps_catalog`, you already know 90% of how to -ship one. +the same catalog entry, the same :ref:`PR flow `, +the same :ref:`release lifecycle `. The only +difference is *intent* — the deliverable is the environment (and, often, +training notebooks and data alongside it), not an app UI. If you have read +:ref:`developers_apps_catalog`, you already know 90% of how to ship one. Why request a kernel -------------------- @@ -74,6 +75,39 @@ A packaged kernel usually has no dashboard UI of its own — it exists to back the environment and registers the kernel without showing a tile in the apps dashboard. +.. _developers_apps_kernels_content: + +Ship notebooks and data alongside the kernel +--------------------------------------------- + +The same clone the app-manager uses to build the kernel lands at +``/home/sepal-user/shared/apps//`` **inside every user's sandbox**. +Anything you put in the repository is therefore reachable from every user's +JupyterLab — not just the ``sepal_environment.yml``. + +This makes the same mechanism useful for a second job: shipping **training +notebooks and supporting data** to a cohort of users. For a workshop or a +training, you can put the curriculum in the repo alongside the environment +file. Each participant gets the notebooks already on their instance under +``shared/apps//``, with the matching ``(venv) `` +kernel pre-built and ready to select — no per-user clone, no per-user pip +install, no "it works on my machine". + +Practical conventions for that case: + +- Keep notebook files at the top level (or in a clearly named subdirectory) + so participants can find them via ``shared/apps//``. +- Keep sample inputs in the repo only if they are small. For larger + datasets, point notebooks at a shared SEPAL location or an external URL + so the clone stays light. +- Tag a ``release`` branch for each cohort you run, so an in-flight push to + ``main`` does not surprise a session that is already underway. See + :ref:`developers_apps_releases_branch`. + +The catalog entry itself stays the same — ``"hidden": true``, ``"endpoint": +"jupyter"``, ``sepal_environment.yml`` in the repo. The notebooks just ride +along in the same clone. + Worked example: ``sepal-sam`` ------------------------------ diff --git a/docs/source/developers/apps/releases.rst b/docs/source/developers/apps/releases.rst index 9b414a2f4a..bf214337cb 100644 --- a/docs/source/developers/apps/releases.rst +++ b/docs/source/developers/apps/releases.rst @@ -6,15 +6,13 @@ Create a release Once your app is in the catalog (see :ref:`developers_apps_catalog`), the way you ship a new version depends on the app's ``endpoint`` (see -:ref:`developers_apps_types`). Docker apps and jupyter/shiny apps (including -kernels) use different release mechanisms: - -- **Jupyter and Shiny apps** (and :ref:`kernels `, - which are just hidden jupyter apps) are not pinned to a commit. The - app-manager checks out the tip of the catalog's ``branch`` (defaulting to - ``HEAD``) every time the sandbox refreshes the app, so a release is just a - push to that branch in your source repository — no catalog change is - needed. +:ref:`developers_apps_types`): + +- **Jupyter apps** (and :ref:`kernels `, which are + just hidden jupyter apps) are not pinned to a commit. The app-manager + checks out the tip of the catalog's ``branch`` (defaulting to ``HEAD``) + every time the sandbox refreshes the app, so a release is just a push to + that branch in your source repository — no catalog change is needed. - **Docker apps** are pinned in the catalog to an exact commit SHA. SEPAL keeps running the old commit until that ``commit`` field is advanced, so a release is, in practice, a pull request against the catalog. @@ -23,8 +21,8 @@ The rest of this page describes each flow in turn. .. _developers_apps_releases_jupyter: -Jupyter, Shiny and kernel apps ------------------------------- +Jupyter and kernel apps +----------------------- These endpoints don't take a ``commit`` field in the catalog — only ``branch`` (which defaults to ``HEAD``). On every sandbox refresh the @@ -38,6 +36,8 @@ Push the change to the branch the catalog tracks for your app. The next time the user's sandbox refreshes the app, the new tip is picked up automatically. No catalog PR is required. +.. _developers_apps_releases_branch: + The release branch convention ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~