Skip to content
372 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Project settings say who created the project, by name1//! GitHub: signing in with a GitHub account, and bringing repositories
2//! across through g1t's GitHub App. Mirrors `packages/contracts/src/github.ts`.
3//!
4//! One GitHub App does both. Its user authorization (OAuth web flow, with
5//! PKCE) signs people in and lets g1t list what they installed it on; its
6//! installations give g1t access to the repositories they chose. The app
7//! is optional: with none configured every method here says so, and the
8//! site shows no GitHub buttons.
9//!
10//! The identity service (`POST /rpc/<method>`) keeps linked accounts and
11//! their user tokens:
12//!
13//! - `github_enabled` (no arguments) returns `bool`.
14//! - `github_start` takes `GithubStartArgs`, returns `Outcome<GithubStart>`.
15//! - `github_finish` takes `GithubFinishArgs`, returns `Outcome<GithubFinished>`.
16//! - `github_pending` takes `GithubPendingArgs`, returns `Outcome<GithubPending>`.
17//! - `github_sign_up` takes `GithubSignUpArgs`, returns `Outcome<SignedIn>`.
18//! - `github_claim` takes `GithubClaimArgs`, returns `Outcome<GithubAccount>`.
19//! - `github_account` takes `UserArgs`, returns `GithubAccountView`.
20//! - `github_unlink` takes `UserArgs`, returns `Outcome<bool>`.
21//! - `github_user_token` takes `GithubUserTokenArgs`, returns `Outcome<String>`.
22//! - `github_revoked` takes `GithubRevokedArgs`, returns `u32`.
23//! - `github_usernames` takes `GithubUsernamesArgs`, returns a map.
24//!
Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them.25//! The integrations service keeps installations and the repositories
26//! imported through them, and receives the app's webhook at `https://api.g1t.sh/hooks/github`:
Project settings say who created the project, by name27//!
28//! - `github_status` takes `GithubStatusArgs`, returns `Outcome<GithubAppStatus>`.
29//! - `github_add_installation` takes `GithubInstallationArgs`, returns
30//! `Outcome<GithubInstallation>`.
31//! - `github_remove_installation` takes `GithubInstallationArgs`, returns
32//! `Outcome<bool>`.
A GitHub App installed straight on GitHub can be added to a workspace: Import from GitHub lists the installations your GitHub account can see that this workspace hasn't added, each with Add for owners, and coming back from GitHub without g1t's own start asks which of your workspaces to add it to instead of stopping; the GitHub guide says how.33//! - `github_visible_installations` takes `GithubVisibleArgs`, returns
34//! `Outcome<Vec<GithubVisibleInstallation>>`.
Project settings say who created the project, by name35//! - `github_repositories` takes `GithubRepositoriesArgs`, returns
36//! `Outcome<GithubRepositories>`.
37//! - `github_import` takes `GithubImportArgs`, returns `Outcome<GithubRepoLink>`.
38//! - `github_link` takes `GithubLinkArgs`, returns `Option<GithubRepoLink>`.
39//! - `github_receive` takes `GithubReceiveArgs`, returns `integrations::Received`.
40
41use std::collections::HashMap;
42
43use serde::{Deserialize, Serialize};
44
45use crate::User;
46use crate::identity::SignedIn;
47
48/// Why someone is sent to GitHub.
49#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
50#[serde(rename_all = "snake_case")]
51pub enum GithubPurpose {
52 /// Sign in, or create an account.
53 SignIn,
54 /// Link GitHub to the signed-in account.
55 Link,
56}
57
58impl GithubPurpose {
59 pub fn as_str(self) -> &'static str {
60 match self {
61 GithubPurpose::SignIn => "sign_in",
62 GithubPurpose::Link => "link",
63 }
64 }
65}
66
67#[derive(Debug, Serialize, Deserialize)]
68#[serde(rename_all = "camelCase")]
69pub struct GithubStartArgs {
70 pub purpose: GithubPurpose,
71 /// The signed-in person, for `link`.
72 #[serde(default)]
73 pub user: Option<User>,
74 /// Exactly the callback registered on the app, such as
75 /// `https://g1t.sh/auth/github/callback`.
76 pub redirect_uri: String,
77 /// A same-site path to return to afterwards.
78 #[serde(default)]
79 pub next: String,
80 /// An invite code, from `/register?invite=…`, carried through GitHub
Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them.81 /// so a new account made with it gets the invitation it came with.
Project settings say who created the project, by name82 #[serde(default)]
83 pub invite_code: Option<String>,
84}
85
86/// Where to send the browser, and the state to bind to it in a cookie.
87#[derive(Debug, Serialize, Deserialize)]
88#[serde(rename_all = "camelCase")]
89pub struct GithubStart {
90 pub authorize_url: String,
91 pub state: String,
92}
93
94#[derive(Debug, Serialize, Deserialize)]
95pub struct GithubFinishArgs {
96 pub state: String,
97 pub code: String,
98}
99
100/// How a return from GitHub ended.
101#[derive(Debug, Serialize, Deserialize)]
102#[serde(tag = "kind", rename_all = "snake_case")]
103pub enum GithubFinished {
104 /// Signed in to the account the GitHub account is linked to, or to a
105 /// new one made with it.
106 SignedIn {
107 #[serde(rename = "signedIn")]
108 signed_in: SignedIn,
109 created: bool,
110 next: String,
111 },
112 /// Linked to the account that asked.
113 Linked { login: String, next: String },
114 /// An account with one of its verified emails exists: the person signs
115 /// in to it, and `github_claim` then links it.
116 NeedsLink { pending: String, login: String, next: String },
117 /// A new account, but the GitHub login cannot be its username.
118 NeedsUsername {
119 pending: String,
120 login: String,
121 suggestion: String,
122 next: String,
123 },
124}
125
126#[derive(Debug, Serialize, Deserialize)]
127pub struct GithubPendingArgs {
128 pub pending: String,
129}
130
131/// A GitHub sign-in waiting on a username or on signing in to link.
132#[derive(Debug, Serialize, Deserialize)]
133pub struct GithubPending {
134 pub login: String,
135 /// `link` or `username`.
136 pub kind: String,
137 pub suggestion: Option<String>,
138 pub next: String,
139}
140
141#[derive(Debug, Serialize, Deserialize)]
142pub struct GithubSignUpArgs {
143 pub pending: String,
144 pub username: String,
145}
146
147#[derive(Debug, Serialize, Deserialize)]
148pub struct GithubClaimArgs {
149 pub pending: String,
150 pub user: User,
151}
152
153/// A linked GitHub account. The numeric id is what identifies it; the
154/// login is for showing, and is refreshed at each sign-in.
155#[derive(Clone, Debug, Serialize, Deserialize)]
156#[serde(rename_all = "camelCase")]
157pub struct GithubAccount {
158 pub github_id: u64,
159 pub login: String,
160 /// RFC 3339.
161 pub linked_at: String,
162 /// Whether g1t holds a working user token, needed to list installations.
163 pub authorized: bool,
164}
165
166#[derive(Debug, Default, Serialize, Deserialize)]
167#[serde(rename_all = "camelCase")]
168pub struct GithubAccountView {
169 /// Whether this g1t has a GitHub App configured for sign-in.
170 pub enabled: bool,
171 pub account: Option<GithubAccount>,
172 /// Whether the account has a password, so GitHub is not its only way in.
173 pub has_password: bool,
174}
175
176#[derive(Debug, Serialize, Deserialize)]
177#[serde(rename_all = "camelCase")]
178pub struct GithubUserTokenArgs {
179 pub user_id: String,
180}
181
182#[derive(Debug, Serialize, Deserialize)]
183#[serde(rename_all = "camelCase")]
184pub struct GithubRevokedArgs {
185 pub github_id: u64,
186}
187
188/// `github_usernames`: the g1t usernames of linked GitHub accounts. Returns
189/// a map from GitHub id, as a string, to username.
190#[derive(Debug, Serialize, Deserialize)]
191#[serde(rename_all = "camelCase")]
192pub struct GithubUsernamesArgs {
193 pub github_ids: Vec<u64>,
194}
195
196// --- The app's installations and repositories (integrations) -------------
197
198#[derive(Clone, Debug, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200pub struct GithubInstallation {
201 pub id: u64,
202 pub workspace: String,
203 /// The GitHub user or organization it is installed on.
204 pub account: String,
205 /// `User` or `Organization`.
206 pub account_type: String,
207 /// `all` or `selected` repositories.
208 pub repository_selection: String,
209 pub suspended: bool,
210 /// Where to change which repositories it can see.
211 pub settings_url: String,
212 pub created_at: String,
213}
214
215#[derive(Debug, Serialize, Deserialize)]
216#[serde(rename_all = "camelCase")]
217pub struct GithubStatusArgs {
218 pub viewer: User,
219 pub workspace: String,
220}
221
222#[derive(Debug, Default, Serialize, Deserialize)]
223#[serde(rename_all = "camelCase")]
224pub struct GithubAppStatus {
225 /// Whether this g1t has a GitHub App configured for repositories.
226 pub configured: bool,
227 /// `https://github.com/apps/<slug>/installations/new`.
228 pub install_url: Option<String>,
229 /// Whether the viewer has linked GitHub with a working user token.
230 pub linked: bool,
231 pub installations: Vec<GithubInstallation>,
232}
233
234#[derive(Debug, Serialize, Deserialize)]
235#[serde(rename_all = "camelCase")]
236pub struct GithubInstallationArgs {
237 pub actor: User,
238 pub workspace: String,
239 pub installation_id: u64,
240}
241
A GitHub App installed straight on GitHub can be added to a workspace: Import from GitHub lists the installations your GitHub account can see that this workspace hasn't added, each with Add for owners, and coming back from GitHub without g1t's own start asks which of your workspaces to add it to instead of stopping; the GitHub guide says how.242/// `github_visible_installations`: the app's installations the person's
243/// own GitHub account can see, for claiming one that was installed on
244/// GitHub directly rather than from g1t.
Project settings say who created the project, by name245#[derive(Debug, Serialize, Deserialize)]
246#[serde(rename_all = "camelCase")]
A GitHub App installed straight on GitHub can be added to a workspace: Import from GitHub lists the installations your GitHub account can see that this workspace hasn't added, each with Add for owners, and coming back from GitHub without g1t's own start asks which of your workspaces to add it to instead of stopping; the GitHub guide says how.247pub struct GithubVisibleArgs {
248 pub actor: User,
249}
250
251/// An installation of the app, as the person's GitHub account sees it.
252#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
253#[serde(rename_all = "camelCase")]
254pub struct GithubVisibleInstallation {
255 pub id: u64,
256 /// The GitHub user or organization it is installed on.
257 pub account: String,
258 /// `User` or `Organization`.
259 pub account_type: String,
260 /// `all` or `selected` repositories.
261 pub repository_selection: String,
262 pub suspended: bool,
263 pub settings_url: String,
264 /// The person's workspaces it is recorded in already.
265 pub recorded_in: Vec<String>,
266}
267
268#[derive(Debug, Serialize, Deserialize)]
269#[serde(rename_all = "camelCase")]
Project settings say who created the project, by name270pub struct GithubRepositoriesArgs {
271 pub actor: User,
272 pub workspace: String,
273 pub installation_id: u64,
274 #[serde(default)]
275 pub page: Option<u32>,
276}
277
278#[derive(Clone, Debug, Serialize, Deserialize)]
279#[serde(rename_all = "camelCase")]
280pub struct GithubRepository {
281 pub id: u64,
282 /// `owner/name`.
283 pub full_name: String,
284 pub name: String,
285 pub private: bool,
286 pub description: Option<String>,
287 pub default_branch: String,
Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them.288 /// The g1t repository already imported from it, as `workspace/name`.
Project settings say who created the project, by name289 pub linked_to: Option<String>,
290}
291
292#[derive(Debug, Default, Serialize, Deserialize)]
293#[serde(rename_all = "camelCase")]
294pub struct GithubRepositories {
295 pub repositories: Vec<GithubRepository>,
296 pub total: u32,
297 pub page: u32,
298 pub per_page: u32,
299}
300
301#[derive(Debug, Serialize, Deserialize)]
302#[serde(rename_all = "camelCase")]
303pub struct GithubImportArgs {
304 pub actor: User,
305 pub workspace: String,
306 pub installation_id: u64,
307 pub github_repo_id: u64,
308 /// The g1t repository's name; the GitHub one's when left out.
309 #[serde(default)]
310 pub name: Option<String>,
311 /// Private on g1t; as on GitHub when left out.
312 #[serde(default)]
313 pub private: Option<bool>,
314 /// Also copy issues, with their labels, milestone and state.
315 #[serde(default)]
316 pub issues: bool,
317}
318
Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them.319/// Where a g1t repository was imported from on GitHub. The import is
320/// copied once: the g1t repository is then its own.
Project settings say who created the project, by name321#[derive(Clone, Debug, Serialize, Deserialize)]
322#[serde(rename_all = "camelCase")]
323pub struct GithubRepoLink {
324 pub repo_id: String,
325 /// `workspace/name` on g1t.
326 pub repo: String,
327 pub installation_id: u64,
328 pub github_repo_id: u64,
329 /// `owner/name` on GitHub.
330 pub full_name: String,
Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them.331 /// RFC 3339; when it was imported.
Project settings say who created the project, by name332 pub synced_at: Option<String>,
333 pub last_error: Option<String>,
334 /// Issues copied so far, when they were asked for.
335 pub issues_imported: u32,
336}
337
338#[derive(Debug, Serialize, Deserialize)]
339#[serde(rename_all = "camelCase")]
340pub struct GithubLinkArgs {
341 pub repo_id: String,
342}
343
344#[derive(Debug, Serialize, Deserialize)]
345pub struct GithubReceiveArgs {
346 /// Header names in lowercase.
347 pub headers: HashMap<String, String>,
348 pub body: String,
349}
A GitHub App installed straight on GitHub can be added to a workspace: Import from GitHub lists the installations your GitHub account can see that this workspace hasn't added, each with Add for owners, and coming back from GitHub without g1t's own start asks which of your workspaces to add it to instead of stopping; the GitHub guide says how.350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[test]
356 fn a_visible_installation_reads_as_the_site_expects() {
357 let item = GithubVisibleInstallation {
358 id: 7,
359 account: "flagon-io".into(),
360 account_type: "Organization".into(),
361 repository_selection: "all".into(),
362 suspended: false,
363 settings_url: "https://github.com/organizations/flagon-io/settings/installations/7".into(),
364 recorded_in: vec!["flagon-io".into()],
365 };
366 let json = serde_json::to_value(&item).unwrap();
367 assert_eq!(json["accountType"], "Organization");
368 assert_eq!(json["repositorySelection"], "all");
369 assert_eq!(json["recordedIn"][0], "flagon-io");
370 assert_eq!(serde_json::from_value::<GithubVisibleInstallation>(json).unwrap(), item);
371 }
372}

This file's history is long; its oldest lines are credited to the oldest commit read.