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 Expect these responsibilities:
52
53 - get the logged-in smbCloud user
54 - resolve the SSH identity path from `Config::ssh_key_path(user.id)`
55 - determine the runner host, usually through `runner.rsync_host()`
56 - build the remote destination from configured project `path`
57 - deploy the current source tree or configured source directory
58
59 The user-visible output may still mention the SSH key path. Preserve useful diagnostics, but avoid misleading claims about git deploy.
60
61 ## Dependency wiring
62
63 When the task is to replace shell `rsync` with the Rust embedding crate:
64
65 1. add the upstream rsync embedding crate at the workspace root `Cargo.toml`
66 2. add it as a workspace dependency in the consuming crate
67 3. wire only the minimal surface needed by `rsync_deploy.rs`
68
69 Prefer the embedding crate over copying CLI subprocess behavior when the goal is portability or tighter control.
70
71 Before coding, inspect:
72
73 - the embedding crate API surface
74 - whether it supports client mode directly
75 - how stdout/stderr and exit status are reported
76 - how remote-shell transport is configured, if at all
77
78 If the crate cannot actually express `host:path` SSH transport parity, say so explicitly instead of forcing a partial migration.
79
80 ## Implementation guidance
81
82 ### Path normalization
83
84 Be precise with trailing slashes.
85
86 Preferred rule:
87
88 - normalize local source to end with `/`
89 - normalize remote destination to end with `/`
90
91 That matches the intended `rsync -a source/ host:path/` behavior.
92
93 ### SSH identity
94
95 The CLI currently resolves the private key path from the smbCloud user id.
96
97 If the embedded rsync client still needs an ssh transport command, keep the identity handling equivalent to the current shell behavior.
98
99 Typical concerns:
100
101 - identity file path
102 - strict host key checking
103 - known hosts file or pinned host key strategy
104 - batch mode / no password prompts
105
106 ### Failure reporting
107
108 Preserve actionable errors.
109
110 Good failures include:
111
112 - local source path missing
113 - remote project path missing in config
114 - SSH key missing
115 - embedded client non-zero exit status
116 - stderr emitted by the transfer implementation
117
118 Do not flatten all deploy failures into a generic message.
119
120 ### Behavior parity
121
122 If replacing subprocess `rsync`, confirm parity for:
123
124 - source contents vs source directory copying
125 - remote path handling
126 - recursive transfer
127 - deletion behavior, if smbCloud depends on it
128 - stdout/stderr capture
129 - non-zero status mapping
130
131 ## Validation
132
133 Use small checks first.
134
135 ### Rust
136
137 - `cargo check -p smbcloud-cli`
138 - targeted tests if added around path normalization or command construction
139
140 ### Behavioral
141
142 Verify the resulting implementation matches the shell command shape conceptually:
143
144 - source `dir/`
145 - destination `host:path/`
146 - ssh identity derived from smbCloud config
147
148 If the code still shells out to `rsync`, validate the exact arguments.
149 If it migrates to the embedding crate, validate equivalent output and error handling.
150
151 ## Common mistakes
152
153 - mixing this generic rsync deploy path with the specialized `nextjs-ssr` flow
154 - losing trailing slash semantics and copying the wrong directory level
155 - adding the dependency in one `Cargo.toml` but not wiring the workspace dependency correctly
156 - assuming the embedding crate automatically supports ssh remote syntax without checking
157 - hiding transport stderr that the user needs to debug deploy failures
158 - hardcoding local paths instead of using smbCloud config and user-derived SSH identity