release/v0.7.4
claude/smbcloud-github-app-k9641p
development
feature/apps
feature/build-release
feature/ci-docs
feature/ci-ruby-gems
feature/cicd
feature/completion
feature/dist-nuget
feature/fix-ci-sdk-pypi-rubygem
feature/fix-clippy-on-stable-channel
feature/fix-deployment-script
feature/github-email
feature/gresiq-v1
feature/project-new-structure
feature/rsync-deploy
feature/ruby-runner
feature/sdk-npm
feature/sdk-workflows
feature/smbcloud-cli-release-npm
feature/smbcloud-deploy
feature/wasm
main
release/v0.7.4
| 1 | # Plan: decouple build/deploy from the CLI into `smbcloud-deploy` |
| 2 | |
| 3 | Status: in progress. Stages 0 to 3 (vite_spa slice) shipped. Remaining work below |
| 4 | is the **next milestone**. |
| 5 | |
| 6 | ## Goal |
| 7 | |
| 8 | Move the deploy logic out of `crates/cli` into a reusable `crates/smbcloud-deploy` |
| 9 | engine so the CLI, the CI action, and (later) the server-side git receiver all |
| 10 | drive the same code. They differ only in how they report progress and how they |
| 11 | authenticate. |
| 12 | |
| 13 | Two inversions make it reusable: |
| 14 | - `Reporter` replaces direct `spinners` / `dialoguer` / `println!`, so the engine |
| 15 | never owns the terminal. The CLI supplies `SpinnerReporter`. |
| 16 | - Auth is passed in (a token / credentials); the engine never reads `~/.smb` or |
| 17 | prompts for login. |
| 18 | |
| 19 | Decisions already made: |
| 20 | - Networking crates (`smbcloud-networking*`) stay direct deps of the engine. |
| 21 | Only reporting and auth are inverted. |
| 22 | - `git.rs` (SSH-git remote setup) stays in the CLI for now. It is the SSH-git |
| 23 | path slated for replacement by git-smart-HTTP, so decoupling it is wasted work. |
| 24 | - Interactive setup (`setup*.rs`, `process_migrate.rs`) stays in the CLI. It is |
| 25 | UX, not engine. |
| 26 | |
| 27 | ## Done |
| 28 | |
| 29 | - **Stage 0** engine crate: `error::DeployError`, `report::{Reporter, NoopReporter}`, |
| 30 | `runner::detect_runner`. |
| 31 | - **Stage 1** CLI depends on the engine; `ui/reporter.rs::SpinnerReporter`; |
| 32 | `detect_runner` rewired; old `deploy/detect_runner.rs` removed. |
| 33 | - **Stage 2** transport: `transport::{Transport, RsyncTransport}` + moved |
| 34 | `known_hosts`. CLI helper `deploy::rsync_transport(config, runner, user_id)` |
| 35 | resolves the local `~/.ssh` identity and remote path. |
| 36 | - **Stage 3 (vite_spa slice)** build split: `build::{BuildStrategy, BuildArtifact, |
| 37 | ViteSpaBuild}`. `process_deploy_vite_spa.rs` now calls the engine for the build |
| 38 | and reuses one `SpinnerReporter` for build + transport. |
| 39 | |
| 40 | ## Next milestone |
| 41 | |
| 42 | ### 1. Shared remote-command step |
| 43 | |
| 44 | Several strategies do the same "SSH in and restart the service" step, inlined |
| 45 | per file. Extract it into the engine first (e.g. `transport::RemoteCommand` or a |
| 46 | `remote` module) so the strategies can share it. Model it behind the same pinned |
| 47 | host-key SSH used by `RsyncTransport`. |
| 48 | |
| 49 | ### 2. Remaining `BuildStrategy` extractions (one per pass, compile-green each) |
| 50 | |
| 51 | Do smallest first. Each: lift build mechanics into a `BuildStrategy`, swap |
| 52 | `spinners` + `stop_and_persist` for `Reporter`, take auth as a param, route the |
| 53 | remote-restart step through step 1. |
| 54 | |
| 55 | | File | Lines | Shape | |
| 56 | | --- | --- | --- | |
| 57 | | `process_deploy_rails.rs` | ~395 | rsync shared lib, SSH compile native gem, git force-push | |
| 58 | | `process_deploy_rust.rs` | ~641 | cross-build Linux binary, rsync, SSH restart | |
| 59 | | `process_deploy_nextjs_ssr.rs` | ~700 | install, build, upload `.next/standalone`, SSH restart | |
| 60 | | `process_deploy_swift.rs` | ~832 | Docker Linux build, rsync binary + Resources, SSH restart | |
| 61 | |
| 62 | Follow `ViteSpaBuild` as the template. Keep the deployment-record API calls |
| 63 | (`create_deployment` / `update`) in the CLI router, not the engine. |
| 64 | |
| 65 | ### 3. Stage 4: router + retire the CommandResult wart |
| 66 | |
| 67 | - Flip `process_deploy.rs`'s router to call an `engine::deploy(...)` orchestrator. |
| 68 | - Retire `CommandResult`'s `spinner: Spinner` field (the "return a live spinner, |
| 69 | caller stops it" pattern) now that the engine reports the whole flow through |
| 70 | `Reporter`. `CommandResult` has ~50 construction sites, so this is its own pass: |
| 71 | either make `spinner` optional or drop it and have commands return plain data. |
| 72 | |
| 73 | ## Reference |
| 74 | |
| 75 | - Spec: `smbcloud` repo `.agents/specs/smbcloud-deploy.md` (product direction), |
| 76 | `smbcloud-actions.md` (CI wrapper). Direction is git-smart-HTTP, local-first |
| 77 | build, drop SSH transport later. |
| 78 | - Engine crate: `crates/smbcloud-deploy/`. |