1 # smbCloud GitHub App
2
3 The smbCloud GitHub App connects a GitHub repository to an smbCloud project so
4 that pushes to the production branch deploy automatically — no `smb deploy`
5 needed. Deploy results are reported back to GitHub as commit statuses
6 (`context: smbcloud/deploy`).
7
8 ## How it works
9
10 1. You install the app on your GitHub account or organization and grant it
11 access to one or more repositories.
12 2. `smb github connect` links a repository (and a production branch) to your
13 smbCloud project's deploy repo.
14 3. On every push, GitHub delivers a `push` webhook to
15 `https://api.smbcloud.xyz/v1/github/webhook`. The server verifies the
16 webhook signature, matches the repository, installation, and branch against
17 a connection, then builds and deploys the project's apps.
18 4. The server records a deployment (visible via `smb project deployment`) and
19 sets a commit status on the pushed SHA: `pending` while building, then
20 `success` or `failure`.
21
22 The webhook receiver and build pipeline are server-side; this repository ships
23 the app manifest, the CLI commands, and the client-side API contract.
24
25 ## CLI usage
26
27 All commands accept `--project-id <id>`; without it they use the current
28 project (`smb project use --id <id>`). The project must have a deploy repo —
29 run `smb deploy` once to initialize it.
30
31 ```sh
32 smb github install # open the app installation page, wait for it to complete
33 smb github connect # pick an installation + repository interactively
34 smb github connect --repo <owner>/<repo> --branch main # non-interactive, CI-friendly
35 smb github status # show the current connection
36 smb github disconnect # remove the connection (asks for confirmation)
37 ```
38
39 `smb github install` opens the browser and polls the API until a new
40 installation appears. A GitHub App's setup URL is fixed app-wide and cannot
41 point at `localhost`, so a local callback server (as used for OAuth login) is
42 not possible here — polling is the intended flow. If the poll times out, just
43 finish the installation in the browser and run `smb github connect`.
44
45 ## Registering the app (operator, one-time)
46
47 The app is registered from [`github-app/manifest.json`](../github-app/manifest.json)
48 using GitHub's [manifest flow](https://docs.github.com/en/apps/sharing-github-apps/registering-a-github-app-from-a-manifest):
49
50 1. Serve a small HTML form that POSTs a `manifest` field containing the JSON to
51 `https://github.com/settings/apps/new?state=<random>` (or
52 `https://github.com/organizations/<org>/settings/apps/new?state=<random>`
53 for an org-owned app).
54 2. GitHub redirects to the manifest's `redirect_url` with a temporary `code`.
55 3. Exchange it within one hour: `POST https://api.github.com/app-manifests/<code>/conversions`.
56 The response contains the app id, slug, webhook secret, private key, and
57 OAuth credentials — store these in your server configuration.
58 4. If the granted slug differs from `smbcloud`, update `GH_APP_SLUG` in
59 `crates/smbcloud-networking/src/constants.rs` (the debug build uses a
60 separate `smbcloud-dev` app whose webhook targets a dev API).
61
62 ### Permissions and events
63
64 | Manifest entry | Why |
65 | --- | --- |
66 | `contents: read` | Fetch the repository at the pushed commit to build it. |
67 | `metadata: read` | Mandatory baseline for every GitHub App; powers repository listing. |
68 | `statuses: write` | Report deploy state as a commit status (one POST per state change). The Checks API adds no value for v1; upgrading to `checks: write` later is additive. |
69 | `default_events: ["push"]` | The only event auto-deploy needs. `installation` and `installation_repositories` events are always delivered to App webhooks without subscription — that is how the server tracks installs. |
70
71 ## API contract
72
73 Endpoints the CLI calls (all under the standard authenticated `v1/` API;
74 implemented server-side):
75
76 | Method | Path | Request | Response |
77 | --- | --- | --- | --- |
78 | GET | `v1/github/installations` | — | `[GithubInstallation]` — installations belonging to the authenticated user (correlated via their linked GitHub account) |
79 | GET | `v1/github/installations/{installation_id}/repositories` | — | `[GithubRepository]` |
80 | GET | `v1/deploy_repos/{deploy_repo_id}/github_connection` | — | `GithubConnectionStatus` — always `200`, `connected: false` when absent |
81 | POST | `v1/deploy_repos/{deploy_repo_id}/github_connection` | `GithubConnectionCreate` | `GithubConnection` — upsert; omitted `production_branch` defaults to the repository's default branch |
82 | DELETE | `v1/deploy_repos/{deploy_repo_id}/github_connection` | — | `null` (`200`, idempotent) |
83 | POST | `v1/github/webhook` | GitHub webhook payload | server-only; never called by the CLI |
84
85 Response bodies must use `200`/`201` (not `204`). The serde models live in
86 `crates/smbcloud-model/src/github.rs`; a connection may also arrive embedded in
87 a `DeployRepo` payload as `github_connection`.