feature/ci-ruby-gems
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 | --- |
| 2 | name: smbcloud-deploy-rsync |
| 3 | description: Use when implementing or debugging smbCloud's generic rsync-based deployment path in `smbcloud-cli`, especially `rsync_deploy.rs`, workspace dependency wiring, SSH identity handling, remote path normalization, and parity with shell rsync commands like `rsync -a source/ host:path/`. |
| 4 | --- |
| 5 | |
| 6 | # smbCloud Deploy Rsync |
| 7 | |
| 8 | Use this skill when work is about smbCloud's generic rsync deploy path in the Rust CLI, not the specialized Next.js SSR flow. |
| 9 | |
| 10 | Applies to: |
| 11 | |
| 12 | - `crates/cli/src/deploy/rsync_deploy.rs` |
| 13 | - workspace dependency wiring in `Cargo.toml` |
| 14 | - embedding the Rust rsync crate instead of shelling out to system `rsync` |
| 15 | - reproducing command semantics like `rsync -a source/ host:path/` |
| 16 | - SSH key and remote destination handling for smbCloud runners |
| 17 | |
| 18 | ## Scope boundary |
| 19 | |
| 20 | This skill is for the generic rsync deploy implementation in `smbcloud-cli`. |
| 21 | |
| 22 | Do not use it for: |
| 23 | |
| 24 | - `kind = "nextjs-ssr"` local-build deploys |
| 25 | - PM2 restart logic for standalone Next.js bundles |
| 26 | - Nginx config for SSR apps |
| 27 | |
| 28 | Those belong to the Next.js deploy skill. |
| 29 | |
| 30 | ## Target behavior |
| 31 | |
| 32 | The baseline command to replicate is: |
| 33 | |
| 34 | ```sh |
| 35 | rsync -a 5mb-dot-app/ rsync-deploy-id-11:apps/web/5mb.app/ |
| 36 | ``` |
| 37 | |
| 38 | Important semantics: |
| 39 | |
| 40 | - trailing slash on the source means copy the contents of the directory |
| 41 | - trailing slash on the destination means sync into that directory |
| 42 | - archive mode preserves the usual rsync metadata behavior |
| 43 | - the remote target is `host:path`, not a local filesystem path |
| 44 | |
| 45 | When implementing in Rust, preserve these semantics explicitly. Do not assume path handling will match shell rsync unless you normalize it yourself. |
| 46 | |
| 47 | ## Current smbCloud conventions |
| 48 | |
| 49 | Generic rsync deploy uses smbCloud config and runner metadata. |
| 50 | |
| 51 | Keep the deploy model straight: |
| 52 | |
| 53 | - `Project` = umbrella workspace in smbCloud (not deployable) |
| 54 | - `DeployRepo` = the git repository inside that workspace |
| 55 | - `FrontendApp` = the deployable app unit, identified by `frontend_app_id` in config |
| 56 | - `Deployment` = one release event, linked to a FrontendApp via `frontend_app_id` |
| 57 | |
| 58 | The canonical chain is: Deployment → FrontendApp → DeployRepo → Project |
| 59 | |
| 60 | The CLI config still uses `[project]` / `[[projects]]` entries for deploy targets during the migration, but they should be thought of as app targets, not umbrella workspaces. |
| 61 | |
| 62 | Expect these responsibilities: |
| 63 | |
| 64 | - get the logged-in smbCloud user |
| 65 | - resolve the SSH identity path from `Config::ssh_key_path(user.id)` |
| 66 | - determine the runner host, usually through `runner.rsync_host()` |
| 67 | - build the remote destination from configured project `path` |
| 68 | - deploy the current source tree or configured source directory |
| 69 | - pass `frontend_app_id` into deployment tracking payloads so the API attributes the deploy to the correct FrontendApp — this should always be present in new configs |
| 70 | |
| 71 | The user-visible output may still mention the SSH key path. Preserve useful diagnostics, but avoid misleading claims about git deploy. |
| 72 | |
| 73 | ## Dependency wiring |
| 74 | |
| 75 | When the task is to replace shell `rsync` with the Rust embedding crate: |
| 76 | |
| 77 | 1. add the upstream rsync embedding crate at the workspace root `Cargo.toml` |
| 78 | 2. add it as a workspace dependency in the consuming crate |
| 79 | 3. wire only the minimal surface needed by `rsync_deploy.rs` |
| 80 | |
| 81 | Prefer the embedding crate over copying CLI subprocess behavior when the goal is portability or tighter control. |
| 82 | |
| 83 | Before coding, inspect: |
| 84 | |
| 85 | - the embedding crate API surface |
| 86 | - whether it supports client mode directly |
| 87 | - how stdout/stderr and exit status are reported |
| 88 | - how remote-shell transport is configured, if at all |
| 89 | |
| 90 | If the crate cannot actually express `host:path` SSH transport parity, say so explicitly instead of forcing a partial migration. |
| 91 | |
| 92 | ## Implementation guidance |
| 93 | |
| 94 | ### Path normalization |
| 95 | |
| 96 | Be precise with trailing slashes. |
| 97 | |
| 98 | Preferred rule: |
| 99 | |
| 100 | - normalize local source to end with `/` |
| 101 | - normalize remote destination to end with `/` |
| 102 | |
| 103 | That matches the intended `rsync -a source/ host:path/` behavior. |
| 104 | |
| 105 | ### SSH identity |
| 106 | |
| 107 | The CLI currently resolves the private key path from the smbCloud user id. |
| 108 | |
| 109 | If the embedded rsync client still needs an ssh transport command, keep the identity handling equivalent to the current shell behavior. |
| 110 | |
| 111 | Typical concerns: |
| 112 | |
| 113 | - identity file path |
| 114 | - strict host key checking |
| 115 | - known hosts file or pinned host key strategy |
| 116 | - batch mode / no password prompts |
| 117 | |
| 118 | ### Failure reporting |
| 119 | |
| 120 | Preserve actionable errors. |
| 121 | |
| 122 | Good failures include: |
| 123 | |
| 124 | - local source path missing |
| 125 | - remote project path missing in config |
| 126 | - SSH key missing |
| 127 | - embedded client non-zero exit status |
| 128 | - stderr emitted by the transfer implementation |
| 129 | |
| 130 | Do not flatten all deploy failures into a generic message. |
| 131 | |
| 132 | ### Behavior parity |
| 133 | |
| 134 | If replacing subprocess `rsync`, confirm parity for: |
| 135 | |
| 136 | - source contents vs source directory copying |
| 137 | - remote path handling |
| 138 | - recursive transfer |
| 139 | - deletion behavior, if smbCloud depends on it |
| 140 | - stdout/stderr capture |
| 141 | - non-zero status mapping |
| 142 | - deploy tracking posts against the workspace `project_id` and includes `frontend_app_id` for app-level attribution — both should be present |
| 143 | |
| 144 | ## Validation |
| 145 | |
| 146 | Use small checks first. |
| 147 | |
| 148 | ### Rust |
| 149 | |
| 150 | - `cargo check -p smbcloud-cli` |
| 151 | - targeted tests if added around path normalization or command construction |
| 152 | |
| 153 | ### Behavioral |
| 154 | |
| 155 | Verify the resulting implementation matches the shell command shape conceptually: |
| 156 | |
| 157 | - source `dir/` |
| 158 | - destination `host:path/` |
| 159 | - ssh identity derived from smbCloud config |
| 160 | |
| 161 | If the code still shells out to `rsync`, validate the exact arguments. |
| 162 | If it migrates to the embedding crate, validate equivalent output and error handling. |
| 163 | |
| 164 | ## Common mistakes |
| 165 | |
| 166 | - mixing this generic rsync deploy path with the specialized `nextjs-ssr` flow |
| 167 | - losing trailing slash semantics and copying the wrong directory level |
| 168 | - adding the dependency in one `Cargo.toml` but not wiring the workspace dependency correctly |
| 169 | - assuming the embedding crate automatically supports ssh remote syntax without checking |
| 170 | - hiding transport stderr that the user needs to debug deploy failures |
| 171 | - hardcoding local paths instead of using smbCloud config and user-derived SSH identity |
| 172 | - treating the workspace `project.id` as the deployable app identity — always use `frontend_app_id` for app-level deploy tracking |
| 173 | - assuming `--copy-links` is harmless: it follows every symlink, so one dangling |
| 174 | link in the source (e.g. a stray pnpm `.pnpm/node_modules/<pkg>` pointing at an |
| 175 | untraced version) makes rsync exit **23** |
| 176 | (`IO error encountered -- skipping file deletion`). The `nextjs-ssr` path hits |
| 177 | this on `.next/standalone/` — prune dangling links before transfer (or use |
| 178 | `--copy-unsafe-links` semantics). See the nextjs skill. |