mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
feat: ordered multi-file Compose for Git sources (#1380)
* feat: ordered multi-file Compose for Git sources
Extend Git sources to deploy an ordered list of compose files merged with
docker compose -f base.yaml -f override.yaml ..., plus an optional project
directory.
- Pick and reorder compose files from the repository tree (drag to reorder on
desktop, up/down arrows on phones); manual path entry is also supported.
- The ordered set drives every stack-scoped compose command (deploy, update,
start/stop/restart/down, image scans, Compose Doctor) and the container
lookup, so a service or image declared only in an override is handled too.
- Runtime keys off the materialized set, not the saved configuration: saving a
source does not change deploy args until the pull is applied, and apply
materializes from the pending snapshot rather than live config.
- The project directory is passed as --project-directory, with -p <stack>
pinning the Compose project so container labels stay stable.
- The Mesh override is layered last; single-file sources are byte-identical to
before, and existing rows keep working via the single-path fallback.
Docs cover the picker, ordering, project directory, and the new troubleshooting
and limitations (referenced files are not materialized; the dependency graph,
drift, and networking views read the primary file).
* fix: harden multi-file Git source (hash, unlink, collisions, node id)
- hashContent folds ordered file CONTENTS (not paths) so a clean multi-file
stack is not flagged as locally edited: create/apply hash the fetched files
(repo paths) while pull hashes the on-disk files (materialized paths), which
previously disagreed and showed a false "local edits detected".
- Block unlinking a multi-file or project-directory Git source (409): the deploy
spec lives on the source row, so removing it would silently revert deploys to
root compose.yaml. Single-file sources still unlink.
- Reject materialized-path collisions in the selection validator: an additional
file equal to or nested under compose.yaml, an ancestor/descendant overlap
between selected files, and a project directory nested under a compose file
(previously a 500 at materialization).
- DockerController.getContainersByStack uses the controller's node compose dir
and passes its node id to the authored prefix, instead of the process default.
* fix: CI failures on multi-file Git source (test crash, aria query, path barrier)
- GitSourceFields no longer crashes when repoUrl/branch are falsy: the canBrowse
trim() is optional-chained, so a reusable field component tolerates partial
props. Fixes the apply-binding panel test, which feeds a minimal source object.
- GitSourcePanel tests query the footer Remove button by its exact name, so the
picker's per-file "Remove <path>" buttons no longer collide with the broad
/remove/i match (the test intent, footer Remove present/absent, is unchanged).
- validateCompose uses an inline resolve + startsWith barrier at the context-dir
mkdir sink (CodeQL does not credit the wrapped isPathWithinBase helper),
clearing the js/path-injection alert. The containment check is equivalent and
contextDir is also validated upstream.
* test: update Git source E2E spec for the multi-file compose picker
The compose-file picker replaced the single #git-source-path input and added
per-file Remove buttons, so the E2E spec drove selectors that no longer exist:
- Drop the redundant compose.yaml fills (the picker defaults to compose.yaml).
- Select the footer Remove button by exact name so the picker's per-file
"Remove <path>" buttons no longer make the locator ambiguous.
- Set a custom compose path through the picker (add via the manual input, press
Enter, then remove the default compose.yaml).
* test: match the footer Remove button with an exact Playwright name
Playwright's getByRole name option is a substring match by default, so
{ name: 'Remove' } also matched the picker's "Remove <path>" buttons. Require an
exact match so only the footer Remove button is selected.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: Git Sources
|
||||
description: Link a stack to a Git repository and keep compose.yaml in sync via manual pulls or webhook triggers.
|
||||
description: Link a stack to a Git repository and keep one or more compose files in sync via manual pulls or webhook triggers.
|
||||
---
|
||||
|
||||
Git Sources turn any stack into a GitOps target. Point Sencho at a repository, branch, and `compose.yaml` path; pull updates on demand or from CI; and review a diff before applying changes to disk. Optional sibling `.env` sync keeps configuration consistent too.
|
||||
Git Sources turn any stack into a GitOps target. Point Sencho at a repository and branch, choose one or more compose files to merge in order, pull updates on demand or from CI, and review a diff before applying changes to disk. Optional sibling `.env` sync keeps configuration consistent too.
|
||||
|
||||
<Note>
|
||||
Git Sources are available on every tier, including Community.
|
||||
@@ -12,7 +12,7 @@ Git Sources turn any stack into a GitOps target. Point Sencho at a repository, b
|
||||
## How it works
|
||||
|
||||
1. Open a stack and click the **Git Source** button in the editor toolbar.
|
||||
2. Fill in the repository URL, branch, and compose file path. Add a token if the repo is private.
|
||||
2. Fill in the repository URL and branch, then choose the compose files. Use **Browse** to pick them from the repository tree, or type a path and press Enter. Add a token if the repo is private.
|
||||
3. Click **Pull now** to fetch the latest commit. Sencho opens a side-by-side diff between the on-disk files and the incoming version.
|
||||
4. Click **Apply** to write the incoming content to disk. Tick **Deploy after apply** in the same dialog to redeploy in one step.
|
||||
|
||||
@@ -27,7 +27,7 @@ Writes land in the stack's existing directory using the same storage Sencho uses
|
||||
The panel groups four regions:
|
||||
|
||||
- **Pending update banner.** Appears at the top when a webhook in **Review only** mode has fetched a new commit. Click **Review** to re-fetch the incoming commit and open the diff dialog.
|
||||
- **Form fields.** Repository URL, branch, compose file path, optional sibling `.env` sync, authentication toggle, and the apply behavior radio group.
|
||||
- **Form fields.** Repository URL, branch, the ordered compose-file picker, an optional project directory, optional sibling `.env` sync, authentication toggle, and the apply behavior radio group.
|
||||
- **Last applied stat strip.** Shows the short SHA of the last commit Sencho applied to disk, plus the timestamp of the most recent successful save or pull.
|
||||
- **Footer actions.** **Remove** disconnects the source without touching the stack files; **Pull now** fetches the configured branch's HEAD; **Save** or **Update** persists form changes after a reachability check passes.
|
||||
|
||||
@@ -39,7 +39,7 @@ Skip the "empty stack then link later" detour and point at a repo from the start
|
||||
<img src="/images/git-sources/create-from-git-tab.png" alt="New stack dialog with the From Git tab selected, showing stack name, repository URL, branch, compose path, sibling .env toggle, authentication toggle, apply behavior radio group, a Deploy after create checkbox, and an HTTPS REPOS ONLY footer hint" />
|
||||
</Frame>
|
||||
|
||||
Sencho fetches the compose file, validates it with `docker compose config`, writes it to a fresh stack directory, and links the Git source in one step. The last-applied commit SHA is seeded from the fetch so the first manual pull produces a clean diff rather than a "local edits detected" warning.
|
||||
Sencho fetches the compose files, validates the merged result with `docker compose config`, writes them to a fresh stack directory, and links the Git source in one step. The last-applied commit SHA is seeded from the fetch so the first manual pull produces a clean diff rather than a "local edits detected" warning.
|
||||
|
||||
Tick **Deploy after create** to run `docker compose up -d` immediately after the files land. If the deploy fails, the stack and Git source are kept on disk so you can fix the underlying issue (missing image, port conflict, host resources) and retry the deploy from the editor.
|
||||
|
||||
@@ -58,12 +58,19 @@ Tick **Deploy after create** to run `docker compose up -d` immediately after the
|
||||
|-------|-------------|
|
||||
| **Repository URL** | `https://github.com/your-org/your-repo.git` (HTTPS only) |
|
||||
| **Branch** | Branch to track (e.g. `main`) |
|
||||
| **Compose file path** | Path within the repo (e.g. `deploy/compose.yaml`) |
|
||||
| **Also sync sibling `.env` file** | When enabled, also pulls the `.env` from the same directory as the compose file. The form shows the resolved path inline (e.g. `deploy/.env` for a compose at `deploy/compose.yaml`). |
|
||||
| **Compose files** | One or more paths within the repo, merged in the listed order (e.g. `deploy/base.yaml` then `deploy/prod.yaml`). The first file is the primary. Reorder by dragging (or the up/down arrows on a phone) and remove with the **×** button. |
|
||||
| **Project directory** | Optional path within the repo passed to `docker compose --project-directory`, so relative build contexts, bind mounts, and `env_file` references resolve from that base. Leave blank to use the stack root. |
|
||||
| **Also sync sibling `.env` file** | When enabled, also pulls the `.env` from the same directory as the primary compose file. The form shows the resolved path inline (e.g. `deploy/.env` for a primary at `deploy/compose.yaml`). |
|
||||
| **Authentication** | **Public (no auth)** for public repos, **Personal Access Token** for private repos |
|
||||
| **Apply behavior** | See the three modes below |
|
||||
|
||||
Saving runs a reachability check against the repository. If the URL is wrong, the token is invalid, the branch does not exist, or the file is missing, Sencho surfaces the error inline and nothing is persisted.
|
||||
Saving runs a reachability check against the repository. If the URL is wrong, the token is invalid, the branch does not exist, or a file is missing, Sencho surfaces the error inline and nothing is persisted.
|
||||
|
||||
### Multiple compose files
|
||||
|
||||
Many projects split a stack into a base file plus environment overrides. List the files in the order you want them merged and Sencho deploys with `docker compose -f base.yaml -f prod.yaml ...`, applying each later file's values on top of the earlier ones, exactly as the Compose CLI does. The same ordered set is used for every action on the stack: deploy, update, restart, stop, and teardown.
|
||||
|
||||
On disk, the primary file lands as the stack's `compose.yaml` and each additional file keeps its repository-relative path inside the stack directory, so you can still browse and edit them in the file explorer. A single-file source behaves exactly as before.
|
||||
|
||||
### Apply behavior modes
|
||||
|
||||
@@ -178,7 +185,7 @@ Pulls, applies, and create-from-git operations on the same stack are serialized
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="File not found">
|
||||
The compose path is relative to the repository root and must point at the file, not its parent directory. If the file was moved, update the path on the panel and save.
|
||||
Each compose path is relative to the repository root and must point at the file, not its parent directory. Every file in the list must exist on the tracked branch; if any is missing, the save or pull fails and names the path. If a file was moved, update its path on the panel and save.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Compose validation failed">
|
||||
@@ -217,6 +224,14 @@ Pulls, applies, and create-from-git operations on the same stack are serialized
|
||||
Repositories that use Git submodules do not have their submodule contents cloned during a Git Source fetch. Sencho surfaces a warning on create when `.gitmodules` is present. If the compose file references paths inside a submodule (build contexts, volume mounts, include directives), inline the referenced files into the main repository or flatten the submodule so the paths resolve at deploy time.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="An additional file named compose.yaml is rejected">
|
||||
The first file in the list is always written to the stack's root `compose.yaml`. To avoid clobbering it, an additional file whose repository path is also `compose.yaml` is rejected. Rename it in the repository, or move it to the top of the list to make it the primary.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="A relative build context or extends target is missing with multiple files">
|
||||
A Git Source materializes only the compose and `.env` files you select, not the rest of the repository. Build contexts, bind-mount sources, and cross-file `extends:` targets referenced by relative path are not pulled, so they can be missing at deploy time. Set a **Project directory** to fix the base for relative paths, and for build-based stacks keep the referenced files in the repository alongside the compose files or build the image out of band.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Only HTTPS is supported">
|
||||
Git Sources fetch over HTTPS only. SSH clone URLs (`git@host:org/repo.git`) and custom protocols are rejected with a client-side validation error. Paste the `https://...` URL and use a Personal Access Token for authentication on private repositories.
|
||||
</Accordion>
|
||||
@@ -229,3 +244,5 @@ Pulls, applies, and create-from-git operations on the same stack are serialized
|
||||
- **No submodules.** Submodule contents are not fetched; paths inside a submodule directory will be missing at deploy time. A warning is shown on create when `.gitmodules` is present.
|
||||
- **Branch-tracking only.** Sources follow the head of a branch. Specific commit SHAs and tags are not pinnable.
|
||||
- **Clone size cap.** A clone is bounded on how much it downloads (and each compose/env file is capped on read), so very large repositories are rejected. Operators can adjust the download ceiling with `GITSOURCE_MAX_CLONE_BYTES`.
|
||||
- **Referenced files are not materialized.** Only the selected compose and `.env` files are written to disk. Relative build contexts, bind-mount sources, and `extends:` targets are not pulled, so build-based stacks that depend on other repository files need those files committed alongside the compose files (or the image built out of band).
|
||||
- **Some read-only views read the primary file.** The dependency graph, drift snapshot, and networking inspector summarize the primary compose file, so a service declared only in an override may not appear in those views. Deploy, update, image-update checks, and mesh attachment use the full merged set.
|
||||
|
||||
@@ -57,7 +57,7 @@ Fleet-wide compose templates that Sencho keeps in sync across the nodes you choo
|
||||
|
||||
### Git sources
|
||||
|
||||
Link a stack to a Git repository and keep `compose.yaml` in sync via manual pulls or webhook triggers. Review a diff before applying changes, create stacks directly from a repo, and optionally sync sibling `.env` files for consistent configuration. [Learn more →](/features/git-sources)
|
||||
Link a stack to a Git repository and keep one or more compose files in sync via manual pulls or webhook triggers. Merge a base file with environment overrides in order, review a diff before applying changes, create stacks directly from a repo, and optionally sync sibling `.env` files for consistent configuration. [Learn more →](/features/git-sources)
|
||||
|
||||
### Stack labels
|
||||
|
||||
|
||||
@@ -43,10 +43,10 @@ If a preview shows a relative volume path (for example `./data:/app/data`), Senc
|
||||
|
||||
### From Git
|
||||
|
||||
The **From Git** tab clones a public or private repository and treats its compose file as the stack's source of truth.
|
||||
The **From Git** tab clones a public or private repository and treats its compose files as the stack's source of truth.
|
||||
|
||||
<Frame>
|
||||
<img src="/images/stack-management/create-stack-git.png" alt="New stack dialog on the From Git tab with repository URL, branch, compose file path, authentication, and apply behavior fields" />
|
||||
<img src="/images/stack-management/create-stack-git.png" alt="New stack dialog on the From Git tab with repository URL, branch, the ordered compose-file picker, an optional project directory, authentication, and apply behavior fields" />
|
||||
</Frame>
|
||||
|
||||
Fill in:
|
||||
@@ -54,8 +54,8 @@ Fill in:
|
||||
- **Stack Name**: same naming rules as the Empty tab.
|
||||
- **Repository URL**: HTTPS only. SSH URLs are not supported.
|
||||
- **Branch**: defaults to `main`.
|
||||
- **Compose file path**: path inside the repository, defaults to `compose.yaml`.
|
||||
- **Also sync sibling .env file**: when checked, a `.env` next to the compose file is pulled along with it.
|
||||
- **Compose files**: one or more paths inside the repository, merged in the listed order. Defaults to `compose.yaml`. Browse the repository tree to pick them, or type a path. An optional **Project directory** sets the base for relative paths.
|
||||
- **Also sync sibling .env file**: when checked, a `.env` next to the primary compose file is pulled along with it.
|
||||
- **Authentication**: pick **Public (no auth)** or **Personal Access Token** for private repos.
|
||||
- **Apply behavior**: controls what happens on future webhook pulls:
|
||||
- **Review only**: diffs surface in the sidebar; you apply manually.
|
||||
@@ -63,7 +63,7 @@ Fill in:
|
||||
- **Auto-deploy**: pulls write and redeploy automatically.
|
||||
- **Deploy after create**: deploys the stack immediately after the repo is cloned.
|
||||
|
||||
Click **Create from Git**. Sencho clones the repo, writes the compose file into the stack directory, and (when **Deploy after create** is checked) runs `docker compose up -d`.
|
||||
Click **Create from Git**. Sencho clones the repo, writes the compose files into the stack directory, and (when **Deploy after create** is checked) runs `docker compose up -d`.
|
||||
|
||||
### Convert from a docker run command
|
||||
|
||||
|
||||
Reference in New Issue
Block a user