Skip to content

Commit ccc8fe2

Browse files
committed
chore: merge main into feat/compute-workload-manager
2 parents 7cbd3aa + d7bc37f commit ccc8fe2

File tree

15 files changed

+440
-132
lines changed

15 files changed

+440
-132
lines changed

CONTRIBUTING.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,23 @@
22

33
Thank you for taking the time to contribute to Trigger.dev. Your involvement is not just welcomed, but we encourage it! 🚀
44

5-
Please take some time to read this guide to understand contributing best practices for Trigger.dev.
5+
Please take some time to read this guide to understand contributing best practices for Trigger.dev. Note that we use [vouch](https://github.com/mitchellh/vouch) to manage contributor trust, so you'll need to be vouched before opening a PR.
66

77
Thank you for helping us make Trigger.dev even better! 🤩
88

9+
## Getting vouched (required before opening a PR)
10+
11+
We use [vouch](https://github.com/mitchellh/vouch) to manage contributor trust. **PRs from unvouched users are automatically closed.**
12+
13+
Before you open your first pull request, you need to be vouched by a maintainer. Here's how:
14+
15+
1. Open a [Vouch Request](https://github.com/triggerdotdev/trigger.dev/issues/new?template=vouch-request.yml) issue.
16+
2. Tell us what you'd like to work on and share any relevant background.
17+
3. A maintainer will review your request and vouch for you by commenting on the issue.
18+
4. Once vouched, your PRs will be accepted normally.
19+
20+
If you're unsure whether you're already vouched, go ahead and open a PR — the check will tell you.
21+
922
## Developing
1023

1124
The development branch is `main`. This is the branch that all pull
@@ -223,19 +236,6 @@ See the [Job Catalog](./references/job-catalog/README.md) file for more.
223236
4. Navigate to your trigger.dev instance ([http://localhost:3030](http://localhost:3030/)), to see the jobs.
224237
You can use the test feature to trigger them.
225238

226-
## Getting vouched (required before opening a PR)
227-
228-
We use [vouch](https://github.com/mitchellh/vouch) to manage contributor trust. **PRs from unvouched users are automatically closed.**
229-
230-
Before you open your first pull request, you need to be vouched by a maintainer. Here's how:
231-
232-
1. Open a [Vouch Request](https://github.com/triggerdotdev/trigger.dev/issues/new?template=vouch-request.yml) issue.
233-
2. Tell us what you'd like to work on and share any relevant background.
234-
3. A maintainer will review your request and vouch for you by commenting on the issue.
235-
4. Once vouched, your PRs will be accepted normally.
236-
237-
If you're unsure whether you're already vouched, go ahead and open a PR — the check will tell you.
238-
239239
## Making a pull request
240240

241241
**If you get errors, be sure to fix them before committing.**

apps/webapp/app/components/navigation/HelpAndFeedbackPopover.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export function HelpAndFeedback({
5959
button={
6060
<PopoverTrigger
6161
className={cn(
62-
"group flex h-8 items-center gap-1.5 rounded pl-[0.4375rem] pr-2 transition-colors hover:bg-charcoal-750",
62+
"group flex h-8 items-center gap-1.5 rounded pl-[0.4375rem] pr-2 transition-colors hover:bg-charcoal-750 focus-custom",
6363
isCollapsed ? "w-full" : "w-full justify-between"
6464
)}
6565
>

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export type VercelSettingsResult = {
2525
enabled: boolean;
2626
hasOrgIntegration: boolean;
2727
authInvalid?: boolean;
28+
authError?: string;
2829
connectedProject?: {
2930
id: string;
3031
vercelProjectId: string;
@@ -52,6 +53,7 @@ export type VercelOnboardingData = {
5253
availableProjects: VercelAvailableProject[];
5354
hasProjectSelected: boolean;
5455
authInvalid?: boolean;
56+
authError?: string;
5557
existingVariables: Record<string, { environments: string[] }>; // Environment slugs (non-archived only)
5658
gitHubAppInstallations: GitHubAppInstallation[];
5759
isGitHubConnected: boolean;
@@ -98,6 +100,7 @@ export class VercelSettingsPresenter extends BasePresenter {
98100
enabled: true,
99101
hasOrgIntegration: false,
100102
authInvalid: true,
103+
authError: orgIntegrationResult.error instanceof Error ? orgIntegrationResult.error.message : "Failed to fetch organization integration",
101104
connectedProject: undefined,
102105
isGitHubConnected: false,
103106
hasStagingEnvironment: false,
@@ -116,6 +119,7 @@ export class VercelSettingsPresenter extends BasePresenter {
116119
enabled: true,
117120
hasOrgIntegration: true,
118121
authInvalid: true,
122+
authError: tokenResult.isErr() ? tokenResult.error.message : "Vercel token is invalid",
119123
connectedProject: undefined,
120124
isGitHubConnected: false,
121125
hasStagingEnvironment: false,
@@ -382,6 +386,7 @@ export class VercelSettingsPresenter extends BasePresenter {
382386
availableProjects: [],
383387
hasProjectSelected: false,
384388
authInvalid: true,
389+
authError: tokenResult.isErr() ? tokenResult.error.message : "Vercel token is invalid",
385390
existingVariables: {},
386391
gitHubAppInstallations,
387392
isGitHubConnected,
@@ -397,6 +402,7 @@ export class VercelSettingsPresenter extends BasePresenter {
397402
availableProjects: [],
398403
hasProjectSelected: false,
399404
authInvalid: clientResult.error.authInvalid,
405+
authError: clientResult.error.authInvalid ? clientResult.error.message : undefined,
400406
existingVariables: {},
401407
gitHubAppInstallations,
402408
isGitHubConnected,
@@ -426,6 +432,7 @@ export class VercelSettingsPresenter extends BasePresenter {
426432
availableProjects: [],
427433
hasProjectSelected: false,
428434
authInvalid: availableProjectsResult.error.authInvalid,
435+
authError: availableProjectsResult.error.authInvalid ? availableProjectsResult.error.message : undefined,
429436
existingVariables: {},
430437
gitHubAppInstallations,
431438
isGitHubConnected,
@@ -472,12 +479,19 @@ export class VercelSettingsPresenter extends BasePresenter {
472479
(sharedEnvVarsResult.isErr() && sharedEnvVarsResult.error.authInvalid);
473480

474481
if (authInvalid) {
482+
const authError =
483+
(customEnvironmentsResult.isErr() && customEnvironmentsResult.error.authInvalid && customEnvironmentsResult.error.message) ||
484+
(projectEnvVarsResult.isErr() && projectEnvVarsResult.error.authInvalid && projectEnvVarsResult.error.message) ||
485+
(sharedEnvVarsResult.isErr() && sharedEnvVarsResult.error.authInvalid && sharedEnvVarsResult.error.message) ||
486+
undefined;
487+
475488
return {
476489
customEnvironments: [],
477490
environmentVariables: [],
478491
availableProjects: availableProjectsResult.value,
479492
hasProjectSelected: true,
480493
authInvalid: true,
494+
authError: authError || undefined,
481495
existingVariables: {},
482496
gitHubAppInstallations,
483497
isGitHubConnected,

apps/webapp/app/routes/resources.incidents.tsx

Lines changed: 62 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,87 @@
11
import { ExclamationTriangleIcon } from "@heroicons/react/20/solid";
22
import { json } from "@remix-run/node";
3-
import { useFetcher } from "@remix-run/react";
3+
import { useFetcher, type ShouldRevalidateFunction } from "@remix-run/react";
44
import { motion } from "framer-motion";
5-
import { useCallback, useEffect } from "react";
5+
import { useEffect, useRef } from "react";
66
import { LinkButton } from "~/components/primitives/Buttons";
77
import { Paragraph } from "~/components/primitives/Paragraph";
88
import { Popover, PopoverContent, PopoverTrigger } from "~/components/primitives/Popover";
99
import { SimpleTooltip } from "~/components/primitives/Tooltip";
1010
import { useFeatures } from "~/hooks/useFeatures";
11-
import { BetterStackClient } from "~/services/betterstack/betterstack.server";
11+
import { BetterStackClient, type AggregateState } from "~/services/betterstack/betterstack.server";
12+
13+
// Prevent Remix from revalidating this route when other fetchers submit
14+
export const shouldRevalidate: ShouldRevalidateFunction = () => false;
15+
16+
export type IncidentLoaderData = {
17+
status: AggregateState;
18+
title: string | null;
19+
};
1220

1321
export async function loader() {
1422
const client = new BetterStackClient();
15-
const result = await client.getIncidents();
23+
const result = await client.getIncidentStatus();
1624

1725
if (!result.success) {
18-
return json({ operational: true });
26+
return json<IncidentLoaderData>({ status: "operational", title: null });
1927
}
2028

21-
return json({
22-
operational: result.data.attributes.aggregate_state === "operational",
29+
return json<IncidentLoaderData>({
30+
status: result.data.status,
31+
title: result.data.title,
2332
});
2433
}
2534

26-
export function IncidentStatusPanel({ isCollapsed = false }: { isCollapsed?: boolean }) {
35+
const DEFAULT_MESSAGE =
36+
"Our team is working on resolving the issue. Check our status page for more information.";
37+
38+
const POLL_INTERVAL_MS = 60_000;
39+
40+
/** Hook to fetch and poll incident status */
41+
export function useIncidentStatus() {
2742
const { isManagedCloud } = useFeatures();
2843
const fetcher = useFetcher<typeof loader>();
29-
30-
const fetchIncidents = useCallback(() => {
31-
if (fetcher.state === "idle") {
32-
fetcher.load("/resources/incidents");
33-
}
34-
}, []);
44+
const hasInitiallyFetched = useRef(false);
3545

3646
useEffect(() => {
3747
if (!isManagedCloud) return;
3848

39-
fetchIncidents();
49+
// Initial fetch on mount
50+
if (!hasInitiallyFetched.current && fetcher.state === "idle") {
51+
hasInitiallyFetched.current = true;
52+
fetcher.load("/resources/incidents");
53+
}
4054

41-
const interval = setInterval(fetchIncidents, 60 * 1000); // 1 minute
55+
// Poll every 60 seconds
56+
const interval = setInterval(() => {
57+
if (fetcher.state === "idle") {
58+
fetcher.load("/resources/incidents");
59+
}
60+
}, POLL_INTERVAL_MS);
4261

4362
return () => clearInterval(interval);
44-
}, [isManagedCloud, fetchIncidents]);
63+
}, [isManagedCloud]);
64+
65+
return {
66+
status: fetcher.data?.status ?? "operational",
67+
title: fetcher.data?.title ?? null,
68+
hasIncident: (fetcher.data?.status ?? "operational") !== "operational",
69+
isManagedCloud,
70+
};
71+
}
4572

46-
const operational = fetcher.data?.operational ?? true;
73+
export function IncidentStatusPanel({ isCollapsed = false }: { isCollapsed?: boolean }) {
74+
const { title, hasIncident, isManagedCloud } = useIncidentStatus();
4775

48-
if (!isManagedCloud || operational) {
76+
if (!isManagedCloud || !hasIncident) {
4977
return null;
5078
}
5179

80+
const message = title || DEFAULT_MESSAGE;
81+
5282
return (
5383
<Popover>
5484
<div className="p-1">
55-
{/* Expanded panel - animated height and opacity */}
5685
<motion.div
5786
initial={false}
5887
animate={{
@@ -62,35 +91,9 @@ export function IncidentStatusPanel({ isCollapsed = false }: { isCollapsed?: boo
6291
transition={{ duration: 0.15 }}
6392
className="overflow-hidden"
6493
>
65-
<div className="flex flex-col gap-2 rounded border border-warning/20 bg-warning/5 p-2 pt-1.5">
66-
{/* Header */}
67-
<div className="flex items-center gap-1 border-b border-warning/20 pb-1 text-warning">
68-
<ExclamationTriangleIcon className="size-4" />
69-
<Paragraph variant="small/bright" className="text-warning">
70-
Active incident
71-
</Paragraph>
72-
</div>
73-
74-
{/* Description */}
75-
<Paragraph variant="extra-small/bright" className="text-warning/80">
76-
Our team is working on resolving the issue. Check our status page for more
77-
information.
78-
</Paragraph>
79-
80-
{/* Button */}
81-
<LinkButton
82-
variant="secondary/small"
83-
to="https://status.trigger.dev"
84-
target="_blank"
85-
fullWidth
86-
className="border-warning/20 bg-warning/10 hover:!border-warning/30 hover:!bg-warning/20"
87-
>
88-
<span className="text-warning">View status page</span>
89-
</LinkButton>
90-
</div>
94+
<IncidentPanelContent message={message} />
9195
</motion.div>
9296

93-
{/* Collapsed button - animated height and opacity */}
9497
<motion.div
9598
initial={false}
9699
animate={{
@@ -102,8 +105,8 @@ export function IncidentStatusPanel({ isCollapsed = false }: { isCollapsed?: boo
102105
>
103106
<SimpleTooltip
104107
button={
105-
<PopoverTrigger className="flex !h-8 w-full items-center justify-center rounded border border-warning/20 bg-warning/10 transition-colors hover:border-warning/30 hover:bg-warning/20">
106-
<ExclamationTriangleIcon className="size-5 text-warning" />
108+
<PopoverTrigger className="flex !h-8 w-full items-center justify-center rounded border border-yellow-500/30 bg-yellow-500/15 transition-colors hover:border-yellow-500/50 hover:bg-yellow-500/25">
109+
<ExclamationTriangleIcon className="size-5 text-yellow-400" />
107110
</PopoverTrigger>
108111
}
109112
content="Active incident"
@@ -115,32 +118,32 @@ export function IncidentStatusPanel({ isCollapsed = false }: { isCollapsed?: boo
115118
</motion.div>
116119
</div>
117120
<PopoverContent side="right" sideOffset={8} align="start" className="!min-w-0 w-52 p-0">
118-
<IncidentPopoverContent />
121+
<IncidentPanelContent message={message} />
119122
</PopoverContent>
120123
</Popover>
121124
);
122125
}
123126

124-
function IncidentPopoverContent() {
127+
function IncidentPanelContent({ message }: { message: string }) {
125128
return (
126-
<div className="flex flex-col gap-2 rounded border border-warning/20 bg-warning/5 p-2 pt-1.5">
127-
<div className="flex items-center gap-1 border-b border-warning/20 pb-1 text-warning">
128-
<ExclamationTriangleIcon className="size-4" />
129-
<Paragraph variant="small/bright" className="text-warning">
129+
<div className="flex flex-col gap-2 rounded border border-yellow-500/30 bg-yellow-500/10 p-2 pt-1.5">
130+
<div className="flex items-center gap-1 border-b border-yellow-500/30 pb-1">
131+
<ExclamationTriangleIcon className="size-4 text-yellow-400" />
132+
<Paragraph variant="small/bright" className="text-yellow-300">
130133
Active incident
131134
</Paragraph>
132135
</div>
133-
<Paragraph variant="extra-small/bright" className="text-warning/80">
134-
Our team is working on resolving the issue. Check our status page for more information.
136+
<Paragraph variant="extra-small/bright" className="text-yellow-300">
137+
{message}
135138
</Paragraph>
136139
<LinkButton
137140
variant="secondary/small"
138141
to="https://status.trigger.dev"
139142
target="_blank"
140143
fullWidth
141-
className="border-warning/20 bg-warning/10 hover:!border-warning/30 hover:!bg-warning/20"
144+
className="border-yellow-500/30 bg-yellow-500/15 hover:!border-yellow-500/50 hover:!bg-yellow-500/25"
142145
>
143-
<span className="text-warning">View status page</span>
146+
<span className="text-yellow-300">View status page</span>
144147
</LinkButton>
145148
</div>
146149
);

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,12 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
188188
}
189189

190190
const authInvalid = onboardingData?.authInvalid || result.authInvalid || false;
191+
const authError = onboardingData?.authError || result.authError;
191192

192193
return typedjson({
193194
...result,
194195
authInvalid,
196+
authError,
195197
onboardingData,
196198
organizationSlug,
197199
projectSlug: projectParam,

0 commit comments

Comments
 (0)