feat: first-boot compose discovery and adopt-first sidebar (#1600)

* feat: add compose discovery for setup preflight and sidebar empty state

Expose read-only compose discovery via GET /api/stacks/discovery and setup

diagnostics. Replace the blank sidebar with path-aware discovery and move

adopt into a dedicated dialog with a three-tab Create Stack flow.

* test: assert post-setup handoff via sessionStorage read-back

The Setup preflight test spied on Storage.prototype.setItem to check the
post-setup adopt handoff. When the jsdom storage probe fails and the test
harness swaps in its in-memory storage stub (which does not extend Storage),
that stub's setItem never touches Storage.prototype, so the spy records zero
calls and the assertion fails even though the component wrote the value.

Read the value back with sessionStorage.getItem instead, matching how every
other storage test in the suite asserts. This is robust to both the native
jsdom storage and the in-memory fallback.

* fix(setup): surface compose discovery as a preflight check row

Drop the Setup discovery banner and non-working Review button. Show

counts as a pass row in EnvironmentChecks (Setup only) and keep

Enter Sencho as the handoff that opens adopt when candidates exist.

* test(setup): cover zero-count discovery row omission

* fix(stacks): widen adopt scan to any yaml and rename into place

Homelab layouts often use nginx.yml or plex.yml. Surface those for
adopt (except overrides), rename to compose.yaml on move so stacks
register, and reset the confirm UI when a move fails.
This commit is contained in:
Anso
2026-07-10 08:42:52 -04:00
committed by GitHub
parent 7848ce339a
commit ba2e7bded9
26 changed files with 1490 additions and 130 deletions
+9 -6
View File
@@ -13,12 +13,13 @@ A **stack** in Sencho is a Docker Compose project: a directory inside your `COMP
Click **Create Stack** in the left sidebar to open the **New stack** dialog. Pick a source from the tabs at the top:
- **Import**: scan the compose directory for compose files you already have on disk and preview what they contain before you bring them in. See [Import existing files](#import-existing-files) below.
- **Empty**: start with a minimal `compose.yaml` skeleton. The default service uses `nginx:latest` with the host port binding commented out, so a fresh deploy never collides with an existing service on the host. Uncomment and adjust the `ports:` block when you're ready to expose the container.
- **From Git**: clone a Git repository so its compose file becomes the stack source. Future webhook pulls keep it in sync. See [Git Sources](/features/git-sources) for the full sync flow.
- **From Docker Run**: paste a `docker run` command and let Sencho convert it to compose YAML.
When you have no stacks yet, the sidebar shows a short prompt with **Import existing** and **New stack** buttons that open this dialog on the matching tab.
To bring in compose files that are already on disk but not yet stacks, use **Adopt existing files** (from the empty sidebar, Create Stack footer, or after first-boot preflight). See [Adopt existing files](#adopt-existing-files) below.
When you have no stacks yet, the sidebar shows what Sencho found under your compose directory and offers **Adopt existing files** and **New stack** when those actions apply.
<Frame>
<img src="/images/stack-management/create-stack-dialog.png" alt="New stack dialog on the Empty tab with a stack name field" />
@@ -31,11 +32,13 @@ When you have no stacks yet, the sidebar shows a short prompt with **Import exis
Sencho creates a new directory inside `COMPOSE_DIR` with the chosen seed file. You land in the editor for the new stack and can deploy it from there.
### Import existing files
### Adopt existing files
If you already have compose files on disk, the **Import** tab helps you land your first stack without reading the rest of this page. It scans your compose directory for compose files that are not yet stacks and lists them with a dry preview of each file's services, ports, volumes, and env files. Scanning is read only: it never moves, writes, or changes anything. The only change to disk is the **Move into place** action, which you trigger per file and confirm before it runs. Stacks that already sit in their own subfolder are not listed here; they appear in the sidebar.
If you already have compose files on disk that are not yet stacks, **Adopt existing files** lists them with a dry preview of each file's services, ports, volumes, and env files. Scanning is read only: it never moves, writes, or changes anything. The only change to disk is the **Move into place** action, which you trigger per file and confirm before it runs. Stacks that already sit in their own subfolder with a standard compose filename (`compose.yaml`, `compose.yml`, `docker-compose.yaml`, or `docker-compose.yml`) are not listed here; they appear in the sidebar.
Each listed file is one that will not show up as a stack on its own, because it is loose at the root of the compose directory or one folder too deep. Expand it, give the stack a name, and click **Move into place** to relocate it into its own subfolder; confirm the move, and it appears as a stack in the sidebar. This only relocates the compose file on disk: it does not adopt or capture running containers, and containers start only when you deploy the stack. Moving a loose root file relocates only that file, so anything it references by a relative path (such as a root `.env`) stays where it is. You can also arrange the file by hand and click **Rescan** instead.
Adopt surfaces any `.yml` or `.yaml` file that is loose at the compose directory root, or one folder too deep (for example `apps/plex/plex.yml`). Compose override filenames such as `compose.override.yml` are skipped. A top-level folder that only has a non-standard name like `plex/plex.yml` is neither a stack nor an adopt candidate; put a standard compose filename in that folder, or nest one level deeper (for example `apps/plex/plex.yml`). Expand a candidate, give the stack a name, and click **Move into place** to relocate it into its own subfolder; confirm the move, and it appears as a stack in the sidebar. If the file was not already named `compose.yaml`, `compose.yml`, `docker-compose.yaml`, or `docker-compose.yml`, Sencho renames it to `compose.yaml` during the move so the stack registers. This only relocates files on disk: it does not adopt or capture running containers, and containers start only when you deploy the stack. Moving a loose root file relocates only that file, so anything it references by a relative path (such as a root `.env`) stays where it is. You can also arrange the file by hand and click **Rescan** instead.
On startup, Sencho also auto-promotes every top-level `*.yml` / `*.yaml` file in `COMPOSE_DIR` into a `<name>/compose.yaml` stack directory. That migration covers loose root files on the next restart, so adopting a root file is optional if you are about to restart. Nested placements (one directory too deep) are not touched by that migration; adopt is the guided path for those.
The top of the panel shows the directory Sencho is scanning and a reminder of the path rule: each stack lives in its own subfolder, and the host mount path must match the path inside the container so relative volume paths resolve. See [Configuration](/getting-started/configuration) for the full explanation.
@@ -445,7 +448,7 @@ Items are disabled while another operation is in progress on that stack.
## Scanning for stacks
If you place Docker Compose files directly into the stacks directory (for example, via `scp`, a file manager, or the command line), you can import them without going through the full **Create Stack** flow. For a guided version that previews each file and points out anything placed where it will not register, use the [Import tab](#import-existing-files) of the **Create Stack** dialog.
If you place Docker Compose files directly into the stacks directory (for example, via `scp`, a file manager, or the command line), click **Scan stacks folder** to re-index subdirectories that already look like stacks. For compose files that are loose at the root or one folder too deep, use [Adopt existing files](#adopt-existing-files).
<Frame>
<img src="/images/stack-management/sidebar-actions.png" alt="Sidebar action row with Create Stack button, bulk mode toggle, and scan stacks folder icon button" />
+2 -2
View File
@@ -109,9 +109,9 @@ Below the stack table, **Configuration Status** summarizes notifications, alerts
On the local node, the top navigation includes **Home**, **Fleet**, **Resources**, **Security**, **App Store**, and **Logs**. Depending on license, role, and node context, it can also include **Update**, **Schedules**, **Console**, and **Audit**; hub-only views are hidden when a remote node is active. The right side of the top bar holds global search, notifications, and the profile menu entries **Settings**, **Documentation**, **Feedback**, **Appearance**, and **Log Out**.
The left sidebar is the stack workspace. Below the Sencho brand, it starts with the node switcher, then **Create Stack**, a bulk-mode toggle, and **Scan stacks folder** for importing Compose projects added outside Sencho. Use **Search stacks...** with the **All**, **Up**, **Down**, and **Updates** chips to narrow the list. On a fresh install the stack list is empty until you create a stack or scan a populated `COMPOSE_DIR`.
The left sidebar is the stack workspace. Below the Sencho brand, it starts with the node switcher, then **Create Stack**, a bulk-mode toggle, and **Scan stacks folder** for re-indexing compose projects added outside Sencho. Use **Search stacks...** with the **All**, **Up**, **Down**, and **Updates** chips to narrow the list. On a fresh install with an empty stack list, Sencho scans your mounted compose directory automatically and shows what it found, including compose files that still need to be adopted into their own subfolder.
Click **Create Stack** in the sidebar and choose **Empty**, **From Git**, or **From Docker Run**. Open **App Store** for a template-driven deploy flow. Once a stack exists, select it in the sidebar or in **Stack health** to open the stack workspace with container health, logs, files, compose editing, and stack actions.
Click **Create Stack** in the sidebar and choose **Empty**, **From Git**, or **From Docker Run**. Use **Adopt existing files** when Sencho finds compose files that are loose at the compose directory root or one folder too deep (any `.yml` / `.yaml`, not only `compose.yaml`). Loose root files are also auto-promoted into stack folders on the next Sencho restart; adopt is the guided path especially for nested placements. Open **App Store** for a template-driven deploy flow. Once a stack exists, select it in the sidebar or in **Stack health** to open the stack workspace with container health, logs, files, compose editing, and stack actions.
## Where to next