Skip to content

Commit 55ea547

Browse files
authored
Merge branch 'main' into fix/tri-6731-cmd-left-arrow-does-not-work-when-focusing-on-a-task
2 parents caa1595 + b71bf89 commit 55ea547

File tree

34 files changed

+897
-263
lines changed

34 files changed

+897
-263
lines changed

.changeset/fluffy-weeks-sleep.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"trigger.dev": patch
3+
"@trigger.dev/core": patch
4+
---
5+
6+
The new `triggeredVia` field is now populated in deployments via the CLI.

.changeset/fuzzy-ghosts-admire.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"trigger.dev": patch
3+
---
4+
5+
fix(dev): stop max listeners exceeded warning messages when running more than 10 runs concurrently
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"trigger.dev": patch
3+
---
4+
5+
Upgrade @modelcontextprotocol/sdk to 1.24.3

.github/workflows/release.yml

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ on:
1515
- release
1616
- prerelease
1717
default: "prerelease"
18-
prerelease_ref:
19-
description: "The ref (branch, tag, or SHA) to checkout and release from (prerelease only)"
20-
required: false
18+
ref:
19+
description: "The ref (branch, tag, or SHA) to checkout and release from"
20+
required: true
2121
type: string
2222
prerelease_tag:
2323
description: "The npm dist-tag for the prerelease (e.g., 'v4-prerelease')"
@@ -30,6 +30,21 @@ concurrency:
3030
cancel-in-progress: false
3131

3232
jobs:
33+
show-release-summary:
34+
name: 📋 Release Summary
35+
runs-on: ubuntu-latest
36+
if: |
37+
github.repository == 'triggerdotdev/trigger.dev' &&
38+
github.event_name == 'pull_request' &&
39+
github.event.pull_request.merged == true &&
40+
github.event.pull_request.head.ref == 'changeset-release/main'
41+
steps:
42+
- name: Show release summary
43+
env:
44+
PR_BODY: ${{ github.event.pull_request.body }}
45+
run: |
46+
echo "$PR_BODY" | sed -n '/^# Releases/,$p' >> $GITHUB_STEP_SUMMARY
47+
3348
release:
3449
name: 🚀 Release npm packages
3550
runs-on: ubuntu-latest
@@ -53,7 +68,15 @@ jobs:
5368
uses: actions/checkout@v4
5469
with:
5570
fetch-depth: 0
56-
ref: main
71+
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref || github.sha }}
72+
73+
- name: Verify ref is on main
74+
if: github.event_name == 'workflow_dispatch'
75+
run: |
76+
if ! git merge-base --is-ancestor ${{ github.event.inputs.ref }} origin/main; then
77+
echo "Error: ref must be an ancestor of main (i.e., already merged)"
78+
exit 1
79+
fi
5780
5881
- name: Setup pnpm
5982
uses: pnpm/action-setup@v4
@@ -121,14 +144,7 @@ jobs:
121144
uses: actions/checkout@v4
122145
with:
123146
fetch-depth: 0
124-
ref: ${{ github.event.inputs.prerelease_ref }}
125-
126-
- name: Validate ref is on main
127-
run: |
128-
if ! git merge-base --is-ancestor ${{ github.event.inputs.prerelease_ref }} origin/main; then
129-
echo "Error: ref must be an ancestor of main (i.e., already merged)"
130-
exit 1
131-
fi
147+
ref: ${{ github.event.inputs.ref }}
132148

133149
- name: Setup pnpm
134150
uses: pnpm/action-setup@v4
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export function GoogleLogo({ className }: { className?: string }) {
2+
return (
3+
<svg className={className} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
4+
<path
5+
d="M19.9075 21.0983C22.7427 18.4521 24.0028 14.0417 23.2468 9.82031H11.9688V14.4827H18.3953C18.1433 15.9949 17.2612 17.255 16.0011 18.0741L19.9075 21.0983Z"
6+
fill="#4285F4"
7+
/>
8+
<path
9+
d="M1.25781 17.3802C2.08665 19.013 3.27532 20.4362 4.73421 21.5428C6.1931 22.6493 7.88415 23.4102 9.67988 23.7681C11.4756 24.1261 13.3292 24.0717 15.1008 23.6091C16.8725 23.1465 18.516 22.2877 19.9075 21.0976L16.0011 18.0733C12.6618 20.2785 7.11734 19.4594 5.22717 14.293L1.25781 17.3802Z"
10+
fill="#34A853"
11+
/>
12+
<path
13+
d="M5.22701 14.2922C4.72297 12.717 4.72297 11.2679 5.22701 9.69275L1.25765 6.60547C-0.191479 9.50373 -0.632519 13.5991 1.25765 17.3794L5.22701 14.2922Z"
14+
fill="#FBBC02"
15+
/>
16+
<path
17+
d="M5.22717 9.69209C6.6133 5.34469 12.5358 2.82446 16.5052 6.5418L19.9705 3.13949C15.0561 -1.58594 5.47919 -1.39692 1.25781 6.60481L5.22717 9.69209Z"
18+
fill="#EA4335"
19+
/>
20+
</svg>
21+
);
22+
}

apps/webapp/app/components/UserProfilePhoto.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export function UserAvatar({
2222
className={cn("aspect-square rounded-full p-[7%]")}
2323
src={avatarUrl}
2424
alt={name ?? "User"}
25+
referrerPolicy="no-referrer"
2526
/>
2627
</div>
2728
) : (

apps/webapp/app/env.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ const EnvironmentSchema = z
9494
TRIGGER_TELEMETRY_DISABLED: z.string().optional(),
9595
AUTH_GITHUB_CLIENT_ID: z.string().optional(),
9696
AUTH_GITHUB_CLIENT_SECRET: z.string().optional(),
97+
AUTH_GOOGLE_CLIENT_ID: z.string().optional(),
98+
AUTH_GOOGLE_CLIENT_SECRET: z.string().optional(),
9799
EMAIL_TRANSPORT: z.enum(["resend", "smtp", "aws-ses"]).optional(),
98100
FROM_EMAIL: z.string().optional(),
99101
REPLY_TO_EMAIL: z.string().optional(),

apps/webapp/app/models/user.server.ts

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Prisma, User } from "@trigger.dev/database";
22
import type { GitHubProfile } from "remix-auth-github";
3+
import type { GoogleProfile } from "remix-auth-google";
34
import { prisma } from "~/db.server";
45
import { env } from "~/env.server";
56
import {
@@ -8,6 +9,8 @@ import {
89
} from "~/services/dashboardPreferences.server";
910
export type { User } from "@trigger.dev/database";
1011
import { assertEmailAllowed } from "~/utils/email";
12+
import { logger } from "~/services/logger.server";
13+
1114
type FindOrCreateMagicLink = {
1215
authenticationMethod: "MAGIC_LINK";
1316
email: string;
@@ -20,7 +23,14 @@ type FindOrCreateGithub = {
2023
authenticationExtraParams: Record<string, unknown>;
2124
};
2225

23-
type FindOrCreateUser = FindOrCreateMagicLink | FindOrCreateGithub;
26+
type FindOrCreateGoogle = {
27+
authenticationMethod: "GOOGLE";
28+
email: User["email"];
29+
authenticationProfile: GoogleProfile;
30+
authenticationExtraParams: Record<string, unknown>;
31+
};
32+
33+
type FindOrCreateUser = FindOrCreateMagicLink | FindOrCreateGithub | FindOrCreateGoogle;
2434

2535
type LoggedInUser = {
2636
user: User;
@@ -35,6 +45,9 @@ export async function findOrCreateUser(input: FindOrCreateUser): Promise<LoggedI
3545
case "MAGIC_LINK": {
3646
return findOrCreateMagicLinkUser(input);
3747
}
48+
case "GOOGLE": {
49+
return findOrCreateGoogleUser(input);
50+
}
3851
}
3952
}
4053

@@ -162,6 +175,134 @@ export async function findOrCreateGithubUser({
162175
};
163176
}
164177

178+
export async function findOrCreateGoogleUser({
179+
email,
180+
authenticationProfile,
181+
authenticationExtraParams,
182+
}: FindOrCreateGoogle): Promise<LoggedInUser> {
183+
assertEmailAllowed(email);
184+
185+
const name = authenticationProfile._json.name;
186+
let avatarUrl: string | undefined = undefined;
187+
if (authenticationProfile.photos[0]) {
188+
avatarUrl = authenticationProfile.photos[0].value;
189+
}
190+
const displayName = authenticationProfile.displayName;
191+
const authProfile = authenticationProfile
192+
? (authenticationProfile as unknown as Prisma.JsonObject)
193+
: undefined;
194+
const authExtraParams = authenticationExtraParams
195+
? (authenticationExtraParams as unknown as Prisma.JsonObject)
196+
: undefined;
197+
198+
const authIdentifier = `google:${authenticationProfile.id}`;
199+
200+
const existingUser = await prisma.user.findUnique({
201+
where: {
202+
authIdentifier,
203+
},
204+
});
205+
206+
const existingEmailUser = await prisma.user.findUnique({
207+
where: {
208+
email,
209+
},
210+
});
211+
212+
if (existingEmailUser && !existingUser) {
213+
// Link existing email account to Google auth, preserving original authenticationMethod
214+
const user = await prisma.user.update({
215+
where: {
216+
email,
217+
},
218+
data: {
219+
authenticationProfile: authProfile,
220+
authenticationExtraParams: authExtraParams,
221+
avatarUrl,
222+
authIdentifier,
223+
},
224+
});
225+
226+
return {
227+
user,
228+
isNewUser: false,
229+
};
230+
}
231+
232+
if (existingEmailUser && existingUser) {
233+
// Check if email user and auth user are the same
234+
if (existingEmailUser.id !== existingUser.id) {
235+
// Different users: email is taken by one user, Google auth belongs to another
236+
logger.error(
237+
`Google auth conflict: Google ID ${authenticationProfile.id} belongs to user ${existingUser.id} but email ${email} is taken by user ${existingEmailUser.id}`,
238+
{
239+
email,
240+
existingEmailUserId: existingEmailUser.id,
241+
existingAuthUserId: existingUser.id,
242+
authIdentifier,
243+
}
244+
);
245+
246+
return {
247+
user: existingUser,
248+
isNewUser: false,
249+
};
250+
}
251+
252+
// Same user: update all profile fields
253+
const user = await prisma.user.update({
254+
where: {
255+
id: existingUser.id,
256+
},
257+
data: {
258+
email,
259+
displayName,
260+
name,
261+
avatarUrl,
262+
authenticationProfile: authProfile,
263+
authenticationExtraParams: authExtraParams,
264+
},
265+
});
266+
267+
return {
268+
user,
269+
isNewUser: false,
270+
};
271+
}
272+
273+
// When the IDP user (Google) already exists, the "update" path will be taken and the email will be updated
274+
// It's not possible that the email is already taken by a different user because that would have been handled
275+
// by one of the if statements above.
276+
const user = await prisma.user.upsert({
277+
where: {
278+
authIdentifier,
279+
},
280+
update: {
281+
email,
282+
displayName,
283+
name,
284+
avatarUrl,
285+
authenticationProfile: authProfile,
286+
authenticationExtraParams: authExtraParams,
287+
},
288+
create: {
289+
authenticationProfile: authProfile,
290+
authenticationExtraParams: authExtraParams,
291+
name,
292+
avatarUrl,
293+
displayName,
294+
authIdentifier,
295+
email,
296+
authenticationMethod: "GOOGLE",
297+
},
298+
});
299+
300+
return {
301+
user,
302+
isNewUser: !existingUser,
303+
};
304+
}
305+
165306
export type UserWithDashboardPreferences = User & {
166307
dashboardPreferences: DashboardPreferences;
167308
};

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
BuildServerMetadata,
23
DeploymentErrorData,
34
ExternalBuildData,
45
prepareDeploymentError,
@@ -154,17 +155,23 @@ export class DeploymentPresenter {
154155
avatarUrl: true,
155156
},
156157
},
158+
buildServerMetadata: true,
157159
},
158160
});
159161

160162
const gitMetadata = processGitMetadata(deployment.git);
161-
162163
const externalBuildData = deployment.externalBuildData
163164
? ExternalBuildData.safeParse(deployment.externalBuildData)
164165
: undefined;
166+
const buildServerMetadata = deployment.buildServerMetadata
167+
? BuildServerMetadata.safeParse(deployment.buildServerMetadata)
168+
: undefined;
165169

166170
let eventStream = undefined;
167-
if (env.S2_ENABLED === "1" && gitMetadata?.source === "trigger_github_app") {
171+
if (
172+
env.S2_ENABLED === "1" &&
173+
(buildServerMetadata || gitMetadata?.source === "trigger_github_app")
174+
) {
168175
const [error, accessToken] = await tryCatch(this.getS2AccessToken(project.externalRef));
169176

170177
if (error) {

apps/webapp/app/routes/auth.github.callback.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { redirect } from "@remix-run/node";
33
import { prisma } from "~/db.server";
44
import { getSession, redirectWithErrorMessage } from "~/models/message.server";
55
import { authenticator } from "~/services/auth.server";
6+
import { setLastAuthMethodHeader } from "~/services/lastAuthMethod.server";
67
import { commitSession } from "~/services/sessionStorage.server";
78
import { redirectCookie } from "./auth.github";
89
import { sanitizeRedirectPath } from "~/utils";
@@ -41,19 +42,19 @@ export let loader: LoaderFunction = async ({ request }) => {
4142
session.set("pending-mfa-user-id", userRecord.id);
4243
session.set("pending-mfa-redirect-to", redirectTo);
4344

44-
return redirect("/login/mfa", {
45-
headers: {
46-
"Set-Cookie": await commitSession(session),
47-
},
48-
});
45+
const headers = new Headers();
46+
headers.append("Set-Cookie", await commitSession(session));
47+
headers.append("Set-Cookie", await setLastAuthMethodHeader("github"));
48+
49+
return redirect("/login/mfa", { headers });
4950
}
5051

5152
// and store the user data
5253
session.set(authenticator.sessionKey, auth);
5354

54-
return redirect(redirectTo, {
55-
headers: {
56-
"Set-Cookie": await commitSession(session),
57-
},
58-
});
55+
const headers = new Headers();
56+
headers.append("Set-Cookie", await commitSession(session));
57+
headers.append("Set-Cookie", await setLastAuthMethodHeader("github"));
58+
59+
return redirect(redirectTo, { headers });
5960
};

0 commit comments

Comments
 (0)