Skip to content

Commit 5567f49

Browse files
authored
feat(webapp): expose project git settings (#2464)
* Fix settigns page delete project width issue * Apply a couple of touch-ups to the project settings page * Add UI flow to connect gh repos * Enabling adding another gh account in the ui * Enable connecting a repo to a project * Enable updating git settings * Enable disconnecting gh repos from a project * Remove prisma migration drifts * Hide git settings when github app is disabled * Fix migration order * Avoid using `location` to avoid SSR issues * Make branch tracking optional * Disable save buttons when there are no field changes * Disable delete project button unless the input matches the project slug * Show connected repo connectedAt date * Check that tracking branch exists when updating git settings * Show tracking branch hint in the deployments page * Fix positioning issue of the pagination pane in the deployments page * Use mono font for branch names * Add link to git settings * Show tracking branch hint for the preview env too * Add a confirmation prompt on repo disconnect * Add link to configure repo access in gh * Add rel prop to github links * Automatically open repo connection modal after app installation * Apply some fixes suggested by mr rabbit * Fix flash cookie issue * Extract project settings actions into a service * Extract project settings loader into a presenter service * Introduce neverthrow for error handling * Try out neverthrow for error handling in the project setting flows * Move env gh branch resolution to the presenter service
1 parent 71060d9 commit 5567f49

File tree

13 files changed

+1581
-172
lines changed

13 files changed

+1581
-172
lines changed

apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
Prisma,
2+
type Prisma,
33
type WorkerDeploymentStatus,
44
type WorkerInstanceGroupType,
55
} from "@trigger.dev/database";
@@ -9,6 +9,7 @@ import { type Project } from "~/models/project.server";
99
import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
1010
import { type User } from "~/models/user.server";
1111
import { processGitMetadata } from "./BranchesPresenter.server";
12+
import { BranchTrackingConfigSchema, getTrackedBranchForEnvironment } from "~/v3/github";
1213

1314
const pageSize = 20;
1415

@@ -56,6 +57,18 @@ export class DeploymentListPresenter {
5657
},
5758
},
5859
},
60+
connectedGithubRepository: {
61+
select: {
62+
branchTracking: true,
63+
previewDeploymentsEnabled: true,
64+
repository: {
65+
select: {
66+
htmlUrl: true,
67+
fullName: true,
68+
},
69+
},
70+
},
71+
},
5972
},
6073
where: {
6174
slug: projectSlug,
@@ -140,9 +153,28 @@ ORDER BY
140153
string_to_array(wd."version", '.')::int[] DESC
141154
LIMIT ${pageSize} OFFSET ${pageSize * (page - 1)};`;
142155

156+
const { connectedGithubRepository } = project;
157+
158+
const branchTrackingOrError =
159+
connectedGithubRepository &&
160+
BranchTrackingConfigSchema.safeParse(connectedGithubRepository.branchTracking);
161+
const environmentGitHubBranch =
162+
branchTrackingOrError && branchTrackingOrError.success
163+
? getTrackedBranchForEnvironment(
164+
branchTrackingOrError.data,
165+
connectedGithubRepository.previewDeploymentsEnabled,
166+
{
167+
type: environment.type,
168+
branchName: environment.branchName ?? undefined,
169+
}
170+
)
171+
: undefined;
172+
143173
return {
144174
currentPage: page,
145175
totalPages: Math.ceil(totalCount / pageSize),
176+
connectedGithubRepository: project.connectedGithubRepository ?? undefined,
177+
environmentGitHubBranch,
146178
deployments: deployments.map((deployment, index) => {
147179
const label = labeledDeployments.find(
148180
(labeledDeployment) => labeledDeployment.deploymentId === deployment.id

apps/webapp/app/routes/_app.github.callback/route.tsx

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
import { type LoaderFunctionArgs } from "@remix-run/node";
1+
import { type LoaderFunctionArgs, redirect } from "@remix-run/node";
22
import { z } from "zod";
33
import { validateGitHubAppInstallSession } from "~/services/gitHubSession.server";
44
import { linkGitHubAppInstallation, updateGitHubAppInstallation } from "~/services/gitHub.server";
55
import { logger } from "~/services/logger.server";
6-
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
6+
import {
7+
redirectWithErrorMessage,
8+
setRequestSuccessMessage,
9+
commitSession,
10+
} from "~/models/message.server";
711
import { tryCatch } from "@trigger.dev/core";
812
import { $replica } from "~/db.server";
913
import { requireUser } from "~/services/session.server";
@@ -88,7 +92,14 @@ export async function loader({ request }: LoaderFunctionArgs) {
8892
return redirectWithErrorMessage(redirectTo, request, "Failed to install GitHub App");
8993
}
9094

91-
return redirectWithSuccessMessage(redirectTo, request, "GitHub App installed successfully");
95+
const session = await setRequestSuccessMessage(request, "GitHub App installed successfully");
96+
session.flash("gitHubAppInstalled", true);
97+
98+
return redirect(redirectTo, {
99+
headers: {
100+
"Set-Cookie": await commitSession(session),
101+
},
102+
});
92103
}
93104

94105
case "update": {
@@ -101,7 +112,14 @@ export async function loader({ request }: LoaderFunctionArgs) {
101112
return redirectWithErrorMessage(redirectTo, request, "Failed to update GitHub App");
102113
}
103114

104-
return redirectWithSuccessMessage(redirectTo, request, "GitHub App updated successfully");
115+
const session = await setRequestSuccessMessage(request, "GitHub App updated successfully");
116+
session.flash("gitHubAppInstalled", true);
117+
118+
return redirect(redirectTo, {
119+
headers: {
120+
"Set-Cookie": await commitSession(session),
121+
},
122+
});
105123
}
106124

107125
case "request": {
@@ -111,7 +129,13 @@ export async function loader({ request }: LoaderFunctionArgs) {
111129
callbackData,
112130
});
113131

114-
return redirectWithSuccessMessage(redirectTo, request, "GitHub App installation requested");
132+
const session = await setRequestSuccessMessage(request, "GitHub App installation requested");
133+
134+
return redirect(redirectTo, {
135+
headers: {
136+
"Set-Cookie": await commitSession(session),
137+
},
138+
});
115139
}
116140

117141
default:

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { ArrowUturnLeftIcon, BookOpenIcon } from "@heroicons/react/20/solid";
22
import { type MetaFunction, Outlet, useLocation, useNavigate, useParams } from "@remix-run/react";
33
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
4+
import { CogIcon, GitBranchIcon } from "lucide-react";
45
import { useEffect } from "react";
56
import { typedjson, useTypedLoaderData } from "remix-typedjson";
67
import { z } from "zod";
78
import { PromoteIcon } from "~/assets/icons/PromoteIcon";
89
import { DeploymentsNone, DeploymentsNoneDev } from "~/components/BlankStatePanels";
10+
import { OctoKitty } from "~/components/GitHubLoginButton";
911
import { GitMetadata } from "~/components/GitMetadata";
1012
import { RuntimeIcon } from "~/components/RuntimeIcon";
1113
import { UserAvatar } from "~/components/UserProfilePhoto";
@@ -50,7 +52,13 @@ import {
5052
} from "~/presenters/v3/DeploymentListPresenter.server";
5153
import { requireUserId } from "~/services/session.server";
5254
import { titleCase } from "~/utils";
53-
import { EnvironmentParamSchema, docsPath, v3DeploymentPath } from "~/utils/pathBuilder";
55+
import { cn } from "~/utils/cn";
56+
import {
57+
EnvironmentParamSchema,
58+
docsPath,
59+
v3DeploymentPath,
60+
v3ProjectSettingsPath,
61+
} from "~/utils/pathBuilder";
5462
import { createSearchParams } from "~/utils/searchParams";
5563
import { compareDeploymentVersions } from "~/v3/utils/deploymentVersions";
5664

@@ -122,8 +130,14 @@ export default function Page() {
122130
const organization = useOrganization();
123131
const project = useProject();
124132
const environment = useEnvironment();
125-
const { deployments, currentPage, totalPages, selectedDeployment } =
126-
useTypedLoaderData<typeof loader>();
133+
const {
134+
deployments,
135+
currentPage,
136+
totalPages,
137+
selectedDeployment,
138+
connectedGithubRepository,
139+
environmentGitHubBranch,
140+
} = useTypedLoaderData<typeof loader>();
127141
const hasDeployments = totalPages > 0;
128142

129143
const { deploymentParam } = useParams();
@@ -160,8 +174,8 @@ export default function Page() {
160174
<ResizablePanelGroup orientation="horizontal" className="h-full max-h-full">
161175
<ResizablePanel id="deployments-main" min="100px" className="max-h-full">
162176
{hasDeployments ? (
163-
<div className="grid max-h-full grid-rows-[1fr_auto]">
164-
<Table containerClassName="border-t-0">
177+
<div className="flex h-full max-h-full flex-col">
178+
<Table containerClassName="border-t-0 grow">
165179
<TableHeader>
166180
<TableRow>
167181
<TableHeaderCell>Deploy</TableHeaderCell>
@@ -286,11 +300,38 @@ export default function Page() {
286300
)}
287301
</TableBody>
288302
</Table>
289-
{totalPages > 1 && (
290-
<div className="-mt-px flex justify-end border-t border-grid-dimmed py-2 pr-2">
291-
<PaginationControls currentPage={currentPage} totalPages={totalPages} />
292-
</div>
293-
)}
303+
<div
304+
className={cn(
305+
"-mt-px flex flex-wrap justify-end gap-2 border-t border-grid-dimmed px-3 pb-[7px] pt-[6px]",
306+
connectedGithubRepository && environmentGitHubBranch && "justify-between"
307+
)}
308+
>
309+
{connectedGithubRepository && environmentGitHubBranch && (
310+
<div className="flex flex-nowrap items-center gap-2 whitespace-nowrap text-sm">
311+
<OctoKitty className="size-4" />
312+
Automatically triggered by pushes to{" "}
313+
<div className="flex max-w-32 items-center gap-1 truncate rounded bg-grid-dimmed px-1 font-mono">
314+
<GitBranchIcon className="size-3 shrink-0" />
315+
<span className="max-w-28 truncate">{environmentGitHubBranch}</span>
316+
</div>{" "}
317+
in
318+
<a
319+
href={connectedGithubRepository.repository.htmlUrl}
320+
target="_blank"
321+
rel="noreferrer noopener"
322+
className="max-w-52 truncate text-sm text-text-dimmed underline transition-colors hover:text-text-bright"
323+
>
324+
{connectedGithubRepository.repository.fullName}
325+
</a>
326+
<LinkButton
327+
variant="minimal/small"
328+
LeadingIcon={CogIcon}
329+
to={v3ProjectSettingsPath(organization, project, environment)}
330+
/>
331+
</div>
332+
)}
333+
<PaginationControls currentPage={currentPage} totalPages={totalPages} />
334+
</div>
294335
</div>
295336
) : environment.type === "DEVELOPMENT" ? (
296337
<MainCenteredContainer className="max-w-md">

0 commit comments

Comments
 (0)