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