Skip to content
173 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.

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.1import { Form, Link, data, redirect } from "react-router";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2
3import type { Route } from "./+types/github-setup";
4import { AuthCard } from "../components/auth-card";
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.5import { GithubMark } from "../components/github";
6import { ErrorText, SubmitButton } from "../components/ui";
7import { INSTALL_COOKIE, cookie, installWorkspace, installationSummary, readCookie, setupChoices } from "../lib/github";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look8import { githubApp } from "../lib/github.server";
9import { page } from "../lib/meta";
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.10import { assertSameOrigin, requireUser, roleIn } from "../lib/session.server";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look11
12export function meta(args: Route.MetaArgs) {
13 return page(args, { title: "Connecting GitHub · g1t" });
14}
15
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.16type Problem = { kind: "error"; error: string; workspace: string | null; link: string | null };
17type Choice = {
18 kind: "choose";
19 installation: { id: number; account: string; summary: string };
20 workspaces: { slug: string; name: string; added: boolean }[];
21};
22
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look23/**
24 * The app's Setup URL: GitHub returns here after an installation with
25 * `installation_id`, `setup_action` and the state. The installation is
26 * recorded against the workspace it was started for, after integrations
27 * checks with the person's own GitHub token that they can see it.
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.28 *
29 * Without g1t's state (the app was installed on GitHub directly, from
30 * another browser, or the cookie was lost), the person chooses which of
31 * the workspaces they own to add it to, checked the same way.
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look32 */
33export async function loader({ request, context }: Route.LoaderArgs) {
34 const user = requireUser(context, request);
35 const url = new URL(request.url);
36 const clear = { "set-cookie": cookie(INSTALL_COOKIE, "", 0) };
37 const workspace = installWorkspace(readCookie(request.headers.get("cookie"), INSTALL_COOKIE), url.searchParams.get("state"));
38 const action = url.searchParams.get("setup_action");
39 const installation = Number(url.searchParams.get("installation_id"));
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.40 const problem = (error: string, at: string | null = workspace, link: string | null = null) =>
41 data<Problem | Choice>({ kind: "error", error, workspace: at, link }, { headers: clear });
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look42 // Repositories chosen again on GitHub: nothing to record, the picker
43 // lists what the installation can see now.
44 if (!workspace && action === "update") throw redirect("/new/github", { headers: clear });
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.45 // An organization member asked an owner to approve: nothing to record yet.
46 if (action === "request") {
47 const back = workspace ? `/new/github?workspace=${encodeURIComponent(workspace)}&` : "/new/github?";
48 throw redirect(`${back}requested=1`, { headers: clear });
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look49 }
50 if (!Number.isSafeInteger(installation) || installation <= 0) {
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.51 return problem("GitHub did not say which installation was made.");
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look52 }
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.53 if (workspace) {
54 const added = await githubApp.addInstallation(user, workspace, installation);
55 if (!added.ok) return problem(added.error.message);
56 throw redirect(`/new/github?workspace=${encodeURIComponent(workspace)}&installation=${installation}`, { headers: clear });
57 }
58 const owned = (user.workspaces ?? []).filter((membership) => membership.role === "owner");
59 if (owned.length === 0) {
60 return problem("Only a workspace owner can add a GitHub account. Ask an owner of your workspace to add it from Import from GitHub.", null);
61 }
62 const visible = await githubApp.visibleInstallations(user);
63 if (!visible.ok) {
64 // GitHub not linked, or its access ended: link it and come back here.
65 const unlinked = visible.error.code === "not_found" || visible.error.code === "unauthenticated";
66 const here = `${url.pathname}${url.search}`;
67 return problem(visible.error.message, null, unlinked ? `/auth/github?link=1&next=${encodeURIComponent(here)}` : null);
68 }
69 const found = visible.value.find((item) => item.id === installation);
70 if (!found) {
71 return problem(
72 "Your GitHub account cannot see that installation. Install the app from your own GitHub account or an organization you manage.",
73 null,
74 );
75 }
76 return data<Problem | Choice>(
77 {
78 kind: "choose",
79 installation: { id: found.id, account: found.account, summary: installationSummary(found) },
80 workspaces: setupChoices(owned, found),
81 },
82 { headers: clear },
83 );
84}
85
86export async function action({ request, context }: Route.ActionArgs) {
87 assertSameOrigin(request);
88 const user = requireUser(context, request);
89 const form = await request.formData();
90 const workspace = String(form.get("workspace") ?? "").toLowerCase();
91 const installation = Number(form.get("installation"));
92 if (roleIn(user, workspace) !== "owner") return { error: "Only an owner of the workspace can add GitHub accounts to it." };
93 if (!Number.isSafeInteger(installation) || installation <= 0) return { error: "Choose an installation to add." };
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look94 const added = await githubApp.addInstallation(user, workspace, installation);
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.95 if (!added.ok) return { error: added.error.message };
96 throw redirect(`/new/github?workspace=${encodeURIComponent(workspace)}&installation=${installation}`);
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look97}
98
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.99export default function GithubSetup({ loaderData, actionData }: Route.ComponentProps) {
100 if (loaderData.kind === "choose") {
101 const { installation, workspaces } = loaderData;
102 return (
103 <AuthCard
104 title="Add a GitHub account"
105 subtitle={`Add the installation on ${installation.account} to which workspace?`}
106 footer={
107 <Link to="/new/github" className="text-fg underline underline-offset-4">
108 Not now
109 </Link>
110 }
111 >
112 <div className="flex items-center gap-3 rounded-md border border-line px-4 py-3">
113 <GithubMark className="size-5 shrink-0" />
114 <div className="min-w-0">
115 <p className="truncate font-mono text-sm">{installation.account}</p>
116 <p className="text-xs text-faint">{installation.summary}</p>
117 </div>
118 </div>
119 <ul className="mt-4 divide-y divide-line rounded-md border border-line">
120 {workspaces.map((option) => (
121 <li key={option.slug} className="flex items-center gap-3 px-4 py-2.5">
122 <div className="min-w-0">
123 <p className="truncate text-sm">{option.name}</p>
124 {option.name !== option.slug && <p className="truncate font-mono text-xs text-faint">{option.slug}</p>}
125 </div>
126 <span className="ml-auto shrink-0">
127 {option.added ? (
128 <Link to={`/new/github?workspace=${option.slug}&installation=${installation.id}`} className="text-sm text-muted hover:text-fg">
129 Added · Open
130 </Link>
131 ) : (
132 <Form method="post">
133 <input type="hidden" name="installation" value={installation.id} />
134 <SubmitButton name="workspace" value={option.slug} pending="Adding…">
135 Add
136 </SubmitButton>
137 </Form>
138 )}
139 </span>
140 </li>
141 ))}
142 </ul>
143 <ErrorText>{actionData?.error}</ErrorText>
144 </AuthCard>
145 );
146 }
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look147 return (
148 <AuthCard
149 title="Connecting GitHub"
150 subtitle="That did not work"
151 footer={
152 <Link
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.153 to={loaderData.workspace ? `/new/github?workspace=${loaderData.workspace}` : "/new/github"}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look154 className="text-fg underline underline-offset-4"
155 >
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.156 Back to Import from GitHub
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look157 </Link>
158 }
159 >
160 <p className="text-sm text-danger" role="alert">
161 {loaderData.error}
162 </p>
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.163 {loaderData.link && (
164 <a
165 href={loaderData.link}
166 className="mt-4 inline-flex w-full items-center justify-center gap-2 rounded-md bg-fg px-3.5 py-2 text-sm font-medium text-bg hover:bg-fg-hover"
167 >
168 <GithubMark /> Link your GitHub account
169 </a>
170 )}
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look171 </AuthCard>
172 );
173}