feature/fix-clippy-on-stable-channel
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 | # Public, open-source repository |
| 2 | |
| 3 | `smbcloud-cli` is published on GitHub as **open source**. Everything you commit here — code, comments, docs, and `.agents/skills/*` — is **world-readable and permanent** (git history outlives any later deletion). Before writing anything, ask: "is this safe on a public repo forever?" |
| 4 | |
| 5 | Do **not** add internal smbCloud infrastructure detail or secrets: |
| 6 | |
| 7 | - server hostnames/IPs and operational endpoints beyond what the CLI already targets in source — e.g. account-scoped SSH key names (`id_<n>@smbcloud`), `api-1.smbcloud.xyz`, internal health ports |
| 8 | - account/user IDs and which user or tenant owns which project |
| 9 | - real customer/app domains, PM2 process names, the production port → app → domain registry |
| 10 | - workspace/project IDs and `frontend_app_id` / `deploy_repo_id` values |
| 11 | - secrets/config: API keys, tokens, `.env` values, connection strings, real auth/CORS origins |
| 12 | - commands that read local credentials to enumerate the API (e.g. `cat ~/.smb/token | curl …`) |
| 13 | - incident logs or examples that name real apps, tenants, customers, or dated rollouts |
| 14 | |
| 15 | Keep docs and skills **generic** — describe how the tool behaves, using placeholders (`example.com`, `<app>`, `<source>`, `<port>`, `<n>`). The base API host (`api.smbcloud.xyz`) is already in the source, so it is not a new leak; the items above are. Fleet-specific operational detail and the internal deploy reference live only in the private `smbcloud` repo. |
| 16 | |
| 17 | --- |
| 18 | |
| 19 | # Rust coding guidelines |
| 20 | |
| 21 | - Prioritize code correctness and clarity. Speed and efficiency are secondary priorities unless otherwise specified. |
| 22 | - Do not write organizational or comments that summarize the code. Comments should only be written in order to explain "why" the code is written in some way in the case there is a reason that is tricky / non-obvious. |
| 23 | - Prefer implementing functionality in existing files unless it is a new logical component. Avoid creating many small files. |
| 24 | - Avoid using functions that panic like `unwrap()`, instead use mechanisms like `?` to propagate errors. |
| 25 | - Be careful with operations like indexing which may panic if the indexes are out of bounds. |
| 26 | - Never silently discard errors with `let _ =` on fallible operations. Always handle errors appropriately: |
| 27 | - Propagate errors with `?` when the calling function should handle them |
| 28 | - Use `.log_err()` or similar when you need to ignore errors but want visibility |
| 29 | - Use explicit error handling with `match` or `if let Err(...)` when you need custom logic |
| 30 | - Example: avoid `let _ = client.request(...).await?;` - use `client.request(...).await?;` instead |
| 31 | - When implementing async operations that may fail, ensure errors propagate to the UI layer so users get meaningful feedback. |
| 32 | - Never create files with `mod.rs` paths - prefer `src/some_module.rs` instead of `src/some_module/mod.rs`. |
| 33 | - When creating new crates, prefer specifying the library root path in `Cargo.toml` using `[lib] path = "...rs"` instead of the default `lib.rs`, to maintain consistent and descriptive naming (e.g., `gpui.rs` or `main.rs`). |
| 34 | - Avoid creative additions unless explicitly requested |
| 35 | - Use full words for variable names (no abbreviations like "q" for "queue") |
| 36 | - Use variable shadowing to scope clones in async contexts for clarity, minimizing the lifetime of borrowed references. |
| 37 | Example: |
| 38 | ```rust |
| 39 | executor.spawn({ |
| 40 | let task_ran = task_ran.clone(); |
| 41 | async move { |
| 42 | *task_ran.borrow_mut() = true; |
| 43 | } |
| 44 | }); |
| 45 | ``` |
| 46 | |
| 47 | --- |
| 48 | |
| 49 | # Build, test, and run |
| 50 | |
| 51 | This is a Cargo workspace pinned to stable Rust (`rust-toolchain.toml`). The CLI binary is named **`smb`** (crate `smbcloud-cli`, in `crates/cli`). |
| 52 | |
| 53 | ```sh |
| 54 | cargo build # build everything (debug) |
| 55 | cargo run -p smbcloud-cli -- <args> # run the CLI, e.g. -- me, -- deploy, -- --help |
| 56 | cargo run -p smbcloud-cli -- -e dev me # talk to a local API (Environment::Dev -> http://localhost:8088) |
| 57 | ``` |
| 58 | |
| 59 | CI gate (mirror it locally before pushing — see `.github/workflows/ci.yml`): |
| 60 | |
| 61 | ```sh |
| 62 | cargo fmt --all -- --check |
| 63 | cargo clippy --workspace --exclude smbcloud-auth-sdk-wasm --tests -- -D warnings # warnings are denied |
| 64 | cargo test --workspace --exclude smbcloud-auth-sdk-wasm |
| 65 | cargo test -p smbcloud-model app_auth::tests::some_test # a single test |
| 66 | ``` |
| 67 | |
| 68 | `smbcloud-auth-sdk-wasm` targets `wasm32-unknown-unknown` and cannot build or test on the host — it is always excluded from workspace clippy/test and checked separately: `cargo check -p smbcloud-auth-sdk-wasm --target wasm32-unknown-unknown`. |
| 69 | |
| 70 | `CLI_CLIENT_SECRET` (see `.env.example`) is the OAuth client-credentials secret read at runtime; tests and `cargo check` don't need it, but login flows against a real API do. |
| 71 | |
| 72 | ## Releases |
| 73 | |
| 74 | Releases are prepared on the `development` branch with a clean tree via the Makefile, which drives `cargo workspaces` to bump every crate in lockstep, then syncs the package-manager manifests (npm/nuget/pypi) and regenerates the SDK lockfiles: |
| 75 | |
| 76 | ```sh |
| 77 | make patch | make minor | make major | make custom VERSION=0.x.y |
| 78 | ``` |
| 79 | |
| 80 | It commits `Release <version>` and tags `v<version>` locally; pushing is manual. Crate publishing is separate and manual (`cargo workspaces publish`, see `docs/development.md`). The per-target release workflows (`release-*.yml`) build the distributable artifacts. |
| 81 | |
| 82 | --- |
| 83 | |
| 84 | # Architecture |
| 85 | |
| 86 | `smb` is a thin async (`tokio`) `clap` front-end over a set of `smbcloud-*` library crates that talk to the smbCloud REST API. The product purpose is **deploying apps** (Node.js/Next.js, Ruby/Rails, Rust, Swift/Vapor, static/Vite) from the terminal. |
| 87 | |
| 88 | ## Request flow |
| 89 | |
| 90 | `crates/cli/src/main.rs` → `cli/mod.rs` (the `Cli`/`Commands` clap tree) → a `process_*` handler under `account/`, `project/`, `deploy/`, or `mail/`. Each handler returns a `CommandResult` (a spinner + final symbol/message); `main` prints it and sets the exit code. The top-level `--environment` (`dev`/`production`, default production) threads through every handler and selects the API host and the on-disk state dir (`.smb` vs `.smb-dev`, see `smbcloud-network/src/environment.rs`). |
| 91 | |
| 92 | Two cross-cutting globals are resolved once in `main` before any handler runs: |
| 93 | - **CI mode** (`crates/cli/src/ci.rs`): `--ci` / `SMB_CI=1` / conventional `CI` env. Stored in a process-global `AtomicBool` (`ci::is_ci()`) rather than threaded through signatures, so deep prompt code can fail fast instead of blocking on a missing TTY. New interactive prompts must check `is_ci()` and use `ci::interactive_message(...)`. |
| 94 | - **Logging**: bunyan JSON to `~/<smb_dir>/smbcloud-cli.log` (not stdout); user-facing output is `console`/spinner styling. |
| 95 | |
| 96 | ## Crate layout (workspace) |
| 97 | |
| 98 | - `smbcloud-model` — shared serde types (`Project`, `User`, `Runner`, deploy config, `ErrorCode`/`ErrorResponse`). `crate-type = ["cdylib","rlib"]` because the SDK crates re-export it. Most unit tests live here. |
| 99 | - `smbcloud-network` — `Environment` enum (host/protocol/state-dir resolution) and connectivity checks. |
| 100 | - `smbcloud-networking` — `SmbClient` (the OAuth client identity) and the low-level HTTP client/constants. |
| 101 | - `smbcloud-networking-project` — typed REST calls for projects, deploy repos, frontend apps, deploy config, and deployments (the `crud_*` files). |
| 102 | - `smbcloud-auth` / `smbcloud-auth-sdk` — auth flows (login, signup, OIDC, Apple, client-credentials, reset/verify). `auth` is the in-CLI implementation; `auth-sdk` is the embeddable client. |
| 103 | - `smbcloud-auth-sdk-wasm` / `-py` (+ `sdk/`) — language bindings: WASM/`wasm-bindgen` for npm, PyO3 `cdylib` for Python, and a separate `rb_sys` Cargo workspace under `sdk/gems` for the Ruby gem. These wrap `smbcloud-auth-sdk`. |
| 104 | - `smbcloud-mail`, `smbcloud-gresiq`(`-sdk`), `smbcloud-s6n` — clients for the Mail, GresIQ (serverless Postgres), and S6n (S3-compatible storage) products. |
| 105 | - `smbcloud-utils` — `.smb/config.toml` (`Config`) parsing and SSH key-path resolution. |
| 106 | |
| 107 | ## Deploy subsystem (`crates/cli/src/deploy/`) |
| 108 | |
| 109 | The most involved area. `process_deploy.rs` loads `.smb/config.toml` (running interactive `setup_project` if missing), overlays server-side config, validates project access, then **dispatches by `config.project.kind`** to a per-stack path: `vite-spa`, `nextjs-ssr`, `rails`, `rust`, `swift` each have their own `process_deploy_*.rs`. If `kind` is unset it falls back to `deployment_method`: `Rsync` (build locally, upload over rsync, restart over SSH) or `Git` (push to a remote git hook), with `detect_runner.rs` sniffing the framework from `package.json`/`Gemfile`/`Package.swift`/`Cargo.toml`. |
| 110 | |
| 111 | **Monorepo**: `runner = Monorepo` with a `[[projects]]` array in config; `smb deploy --project <name>` (or an interactive picker) swaps `config.project` to the named sub-project before the dispatch above. `migrate.rs`/`smb migrate` pushes local deploy fields up to the server. |
| 112 | |
| 113 | When editing a deploy path, keep the public-repo policy above in mind: SSH key names, hosts, ports, app/tenant identifiers, and PM2 process names must stay as placeholders — concrete fleet detail belongs only in the private `smbcloud` repo. |