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. | 1 | import { Form, Link, data, redirect } from "react-router"; |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 2 | |
| 3 | import type { Route } from "./+types/github-setup"; | |
| 4 | import { 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. | 5 | import { GithubMark } from "../components/github"; |
| 6 | import { ErrorText, SubmitButton } from "../components/ui"; | |
| 7 | import { 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 look | 8 | import { githubApp } from "../lib/github.server"; |
| 9 | import { 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. | 10 | import { assertSameOrigin, requireUser, roleIn } from "../lib/session.server"; |
| Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look | 11 | |
| 12 | export 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. | 16 | type Problem = { kind: "error"; error: string; workspace: string | null; link: string | null }; |
| 17 | type 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 look | 23 | /** |
| 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 look | 32 | */ |
| 33 | export 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 look | 42 | // 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 look | 49 | } |
| 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 look | 52 | } |
| 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 | ||
| 86 | export 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 look | 94 | 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 look | 97 | } |
| 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. | 99 | export 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 look | 147 | 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 look | 154 | 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 look | 157 | </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 look | 171 | </AuthCard> |
| 172 | ); | |
| 173 | } |