Skip to content

Commit 78a3d43

Browse files
committed
Merge remote-tracking branch 'origin/main' into feat/auto-reattempt
2 parents eed71f9 + 768036a commit 78a3d43

File tree

151 files changed

+7919
-1512
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

151 files changed

+7919
-1512
lines changed

.changeset/brave-forks-compare.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@trigger.dev/react-hooks": minor
3+
"@trigger.dev/sdk": minor
4+
"@trigger.dev/core": minor
5+
---
6+
7+
Access run status updates in realtime, from your server or from your frontend

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ concurrency:
1717
jobs:
1818
release:
1919
name: 🦋 Changesets Release
20-
runs-on: buildjet-8vcpu-ubuntu-2204
20+
runs-on: ubuntu-latest
2121
if: github.repository == 'triggerdotdev/trigger.dev'
2222
outputs:
2323
published: ${{ steps.changesets.outputs.published }}

.github/workflows/unit-tests.yml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
jobs:
77
unitTests:
88
name: "🧪 Unit Tests"
9-
runs-on: buildjet-8vcpu-ubuntu-2204
9+
runs-on: buildjet-16vcpu-ubuntu-2204
1010
steps:
1111
- name: ⬇️ Checkout repo
1212
uses: actions/checkout@v4
@@ -30,5 +30,15 @@ jobs:
3030
- name: 📀 Generate Prisma Client
3131
run: pnpm run generate
3232

33-
- name: 🧪 Run Unit Tests
34-
run: pnpm run test
33+
- name: 🧪 Run Webapp Unit Tests
34+
run: pnpm run test --filter webapp
35+
env:
36+
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/postgres
37+
DIRECT_URL: postgresql://postgres:postgres@localhost:5432/postgres
38+
SESSION_SECRET: "secret"
39+
MAGIC_LINK_SECRET: "secret"
40+
ENCRYPTION_KEY: "secret"
41+
42+
43+
- name: 🧪 Run Internal Unit Tests
44+
run: pnpm run test --filter "@internal/*"

.npmrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
link-workspace-packages=false
2-
public-hoist-pattern[]=*prisma*
2+
public-hoist-pattern[]=*prisma*
3+
prefer-workspace-packages=true

.vscode/extensions.json

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
{
2-
"recommendations": [
3-
"denoland.vscode-deno"
4-
],
5-
"unwantedRecommendations": [
6-
7-
]
2+
"recommendations": ["bierner.comment-tagged-templates"],
3+
"unwantedRecommendations": []
84
}

apps/webapp/app/env.server.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const EnvironmentSchema = z.object({
3131
REMIX_APP_PORT: z.string().optional(),
3232
LOGIN_ORIGIN: z.string().default("http://localhost:3030"),
3333
APP_ORIGIN: z.string().default("http://localhost:3030"),
34-
ELECTRIC_ORIGIN: z.string(),
34+
ELECTRIC_ORIGIN: z.string().default("http://localhost:3060"),
3535
APP_ENV: z.string().default(process.env.NODE_ENV),
3636
SERVICE_NAME: z.string().default("trigger.dev webapp"),
3737
SECRET_STORE: SecretStoreOptionsSchema.default("DATABASE"),
@@ -103,6 +103,25 @@ const EnvironmentSchema = z.object({
103103
API_RATE_LIMIT_REFILL_RATE: z.coerce.number().int().default(250), // refix 250 tokens every 10 seconds
104104
API_RATE_LIMIT_REQUEST_LOGS_ENABLED: z.string().default("0"),
105105
API_RATE_LIMIT_REJECTION_LOGS_ENABLED: z.string().default("1"),
106+
API_RATE_LIMIT_LIMITER_LOGS_ENABLED: z.string().default("0"),
107+
108+
API_RATE_LIMIT_JWT_WINDOW: z.string().default("1m"),
109+
API_RATE_LIMIT_JWT_TOKENS: z.coerce.number().int().default(60),
110+
111+
//Realtime rate limiting
112+
/**
113+
* @example "60s"
114+
* @example "1m"
115+
* @example "1h"
116+
* @example "1d"
117+
* @example "1000ms"
118+
* @example "1000s"
119+
*/
120+
REALTIME_RATE_LIMIT_WINDOW: z.string().default("1m"),
121+
REALTIME_RATE_LIMIT_TOKENS: z.coerce.number().int().default(100),
122+
REALTIME_RATE_LIMIT_REQUEST_LOGS_ENABLED: z.string().default("0"),
123+
REALTIME_RATE_LIMIT_REJECTION_LOGS_ENABLED: z.string().default("1"),
124+
REALTIME_RATE_LIMIT_LIMITER_LOGS_ENABLED: z.string().default("0"),
106125

107126
//Ingesting event rate limit
108127
INGEST_EVENT_RATE_LIMIT_WINDOW: z.string().default("60s"),

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { prisma } from "~/db.server";
22
import { generateFriendlyId } from "~/v3/friendlyIdentifiers";
33

4-
export const MAX_TAGS_PER_RUN = 5;
4+
export const MAX_TAGS_PER_RUN = 10;
55

66
export async function createTag({ tag, projectId }: { tag: string; projectId: string }) {
77
if (tag.trim().length === 0) return;

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

Lines changed: 44 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ type CommonRelatedRun = Prisma.Result<
6262
export class ApiRetrieveRunPresenter extends BasePresenter {
6363
public async call(
6464
friendlyId: string,
65-
env: AuthenticatedEnvironment,
66-
showSecretDetails: boolean
65+
env: AuthenticatedEnvironment
6766
): Promise<RetrieveRunResponse | undefined> {
6867
return this.traceWithEnv("call", env, async (span) => {
6968
const taskRun = await this._replica.taskRun.findFirst({
@@ -72,11 +71,7 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
7271
runtimeEnvironmentId: env.id,
7372
},
7473
include: {
75-
attempts: {
76-
orderBy: {
77-
createdAt: "desc",
78-
},
79-
},
74+
attempts: true,
8075
lockedToVersion: true,
8176
schedule: true,
8277
tags: true,
@@ -111,50 +106,48 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
111106
let $output: any;
112107
let $outputPresignedUrl: string | undefined;
113108

114-
if (showSecretDetails) {
115-
const payloadPacket = await conditionallyImportPacket({
116-
data: taskRun.payload,
117-
dataType: taskRun.payloadType,
118-
});
109+
const payloadPacket = await conditionallyImportPacket({
110+
data: taskRun.payload,
111+
dataType: taskRun.payloadType,
112+
});
119113

120-
if (
121-
payloadPacket.dataType === "application/store" &&
122-
typeof payloadPacket.data === "string"
123-
) {
124-
$payloadPresignedUrl = await generatePresignedUrl(
125-
env.project.externalRef,
126-
env.slug,
127-
payloadPacket.data,
128-
"GET"
129-
);
130-
} else {
131-
$payload = await parsePacket(payloadPacket);
132-
}
114+
if (
115+
payloadPacket.dataType === "application/store" &&
116+
typeof payloadPacket.data === "string"
117+
) {
118+
$payloadPresignedUrl = await generatePresignedUrl(
119+
env.project.externalRef,
120+
env.slug,
121+
payloadPacket.data,
122+
"GET"
123+
);
124+
} else {
125+
$payload = await parsePacket(payloadPacket);
126+
}
133127

134-
if (taskRun.status === "COMPLETED_SUCCESSFULLY") {
135-
const completedAttempt = taskRun.attempts.find(
136-
(a) => a.status === "COMPLETED" && typeof a.output !== null
137-
);
128+
if (taskRun.status === "COMPLETED_SUCCESSFULLY") {
129+
const completedAttempt = taskRun.attempts.find(
130+
(a) => a.status === "COMPLETED" && typeof a.output !== null
131+
);
138132

139-
if (completedAttempt && completedAttempt.output) {
140-
const outputPacket = await conditionallyImportPacket({
141-
data: completedAttempt.output,
142-
dataType: completedAttempt.outputType,
143-
});
133+
if (completedAttempt && completedAttempt.output) {
134+
const outputPacket = await conditionallyImportPacket({
135+
data: completedAttempt.output,
136+
dataType: completedAttempt.outputType,
137+
});
144138

145-
if (
146-
outputPacket.dataType === "application/store" &&
147-
typeof outputPacket.data === "string"
148-
) {
149-
$outputPresignedUrl = await generatePresignedUrl(
150-
env.project.externalRef,
151-
env.slug,
152-
outputPacket.data,
153-
"GET"
154-
);
155-
} else {
156-
$output = await parsePacket(outputPacket);
157-
}
139+
if (
140+
outputPacket.dataType === "application/store" &&
141+
typeof outputPacket.data === "string"
142+
) {
143+
$outputPresignedUrl = await generatePresignedUrl(
144+
env.project.externalRef,
145+
env.slug,
146+
outputPacket.data,
147+
"GET"
148+
);
149+
} else {
150+
$output = await parsePacket(outputPacket);
158151
}
159152
}
160153
}
@@ -165,6 +158,7 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
165158
payloadPresignedUrl: $payloadPresignedUrl,
166159
output: $output,
167160
outputPresignedUrl: $outputPresignedUrl,
161+
error: ApiRetrieveRunPresenter.apiErrorFromError(taskRun.error),
168162
schedule: taskRun.schedule
169163
? {
170164
id: taskRun.schedule.friendlyId,
@@ -179,17 +173,9 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
179173
},
180174
}
181175
: undefined,
182-
attempts: !showSecretDetails
183-
? []
184-
: taskRun.attempts.map((a) => ({
185-
id: a.friendlyId,
186-
status: ApiRetrieveRunPresenter.apiStatusFromAttemptStatus(a.status),
187-
createdAt: a.createdAt ?? undefined,
188-
updatedAt: a.updatedAt ?? undefined,
189-
startedAt: a.startedAt ?? undefined,
190-
completedAt: a.completedAt ?? undefined,
191-
error: ApiRetrieveRunPresenter.apiErrorFromError(a.error),
192-
})),
176+
// We're removing attempts from the API
177+
attemptCount: taskRun.attempts.length,
178+
attempts: [],
193179
relatedRuns: {
194180
root: taskRun.rootTaskRun
195181
? await createCommonRunStructure(taskRun.rootTaskRun)

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

Lines changed: 31 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const CoercedDate = z.preprocess((arg) => {
2929
return arg;
3030
}, z.date().optional());
3131

32-
const SearchParamsSchema = z.object({
32+
export const ApiRunListSearchParams = z.object({
3333
"page[size]": z.coerce.number().int().positive().min(1).max(100).optional(),
3434
"page[after]": z.string().optional(),
3535
"page[before]": z.string().optional(),
@@ -121,58 +121,44 @@ const SearchParamsSchema = z.object({
121121
"filter[createdAt][period]": z.string().optional(),
122122
});
123123

124-
type SearchParamsSchema = z.infer<typeof SearchParamsSchema>;
124+
type ApiRunListSearchParams = z.infer<typeof ApiRunListSearchParams>;
125125

126126
export class ApiRunListPresenter extends BasePresenter {
127127
public async call(
128128
project: Project,
129-
searchParams: URLSearchParams,
129+
searchParams: ApiRunListSearchParams,
130130
environment?: RuntimeEnvironment
131131
): Promise<ListRunResponse> {
132132
return this.trace("call", async (span) => {
133-
const rawSearchParams = Object.fromEntries(searchParams.entries());
134-
const $searchParams = SearchParamsSchema.safeParse(rawSearchParams);
135-
136-
if (!$searchParams.success) {
137-
logger.error("Invalid search params", {
138-
searchParams: rawSearchParams,
139-
errors: $searchParams.error.errors,
140-
});
141-
142-
throw fromZodError($searchParams.error);
143-
}
144-
145-
logger.debug("Valid search params", { searchParams: $searchParams.data });
146-
147133
const options: RunListOptions = {
148134
projectId: project.id,
149135
};
150136

151137
// pagination
152-
if ($searchParams.data["page[size]"]) {
153-
options.pageSize = $searchParams.data["page[size]"];
138+
if (searchParams["page[size]"]) {
139+
options.pageSize = searchParams["page[size]"];
154140
}
155141

156-
if ($searchParams.data["page[after]"]) {
157-
options.cursor = $searchParams.data["page[after]"];
142+
if (searchParams["page[after]"]) {
143+
options.cursor = searchParams["page[after]"];
158144
options.direction = "forward";
159145
}
160146

161-
if ($searchParams.data["page[before]"]) {
162-
options.cursor = $searchParams.data["page[before]"];
147+
if (searchParams["page[before]"]) {
148+
options.cursor = searchParams["page[before]"];
163149
options.direction = "backward";
164150
}
165151

166152
// filters
167153
if (environment) {
168154
options.environments = [environment.id];
169155
} else {
170-
if ($searchParams.data["filter[env]"]) {
156+
if (searchParams["filter[env]"]) {
171157
const environments = await this._prisma.runtimeEnvironment.findMany({
172158
where: {
173159
projectId: project.id,
174160
slug: {
175-
in: $searchParams.data["filter[env]"],
161+
in: searchParams["filter[env]"],
176162
},
177163
},
178164
});
@@ -181,46 +167,46 @@ export class ApiRunListPresenter extends BasePresenter {
181167
}
182168
}
183169

184-
if ($searchParams.data["filter[status]"]) {
185-
options.statuses = $searchParams.data["filter[status]"].flatMap((status) =>
170+
if (searchParams["filter[status]"]) {
171+
options.statuses = searchParams["filter[status]"].flatMap((status) =>
186172
ApiRunListPresenter.apiStatusToRunStatuses(status)
187173
);
188174
}
189175

190-
if ($searchParams.data["filter[taskIdentifier]"]) {
191-
options.tasks = $searchParams.data["filter[taskIdentifier]"];
176+
if (searchParams["filter[taskIdentifier]"]) {
177+
options.tasks = searchParams["filter[taskIdentifier]"];
192178
}
193179

194-
if ($searchParams.data["filter[version]"]) {
195-
options.versions = $searchParams.data["filter[version]"];
180+
if (searchParams["filter[version]"]) {
181+
options.versions = searchParams["filter[version]"];
196182
}
197183

198-
if ($searchParams.data["filter[tag]"]) {
199-
options.tags = $searchParams.data["filter[tag]"];
184+
if (searchParams["filter[tag]"]) {
185+
options.tags = searchParams["filter[tag]"];
200186
}
201187

202-
if ($searchParams.data["filter[bulkAction]"]) {
203-
options.bulkId = $searchParams.data["filter[bulkAction]"];
188+
if (searchParams["filter[bulkAction]"]) {
189+
options.bulkId = searchParams["filter[bulkAction]"];
204190
}
205191

206-
if ($searchParams.data["filter[schedule]"]) {
207-
options.scheduleId = $searchParams.data["filter[schedule]"];
192+
if (searchParams["filter[schedule]"]) {
193+
options.scheduleId = searchParams["filter[schedule]"];
208194
}
209195

210-
if ($searchParams.data["filter[createdAt][from]"]) {
211-
options.from = $searchParams.data["filter[createdAt][from]"].getTime();
196+
if (searchParams["filter[createdAt][from]"]) {
197+
options.from = searchParams["filter[createdAt][from]"].getTime();
212198
}
213199

214-
if ($searchParams.data["filter[createdAt][to]"]) {
215-
options.to = $searchParams.data["filter[createdAt][to]"].getTime();
200+
if (searchParams["filter[createdAt][to]"]) {
201+
options.to = searchParams["filter[createdAt][to]"].getTime();
216202
}
217203

218-
if ($searchParams.data["filter[createdAt][period]"]) {
219-
options.period = $searchParams.data["filter[createdAt][period]"];
204+
if (searchParams["filter[createdAt][period]"]) {
205+
options.period = searchParams["filter[createdAt][period]"];
220206
}
221207

222-
if (typeof $searchParams.data["filter[isTest]"] === "boolean") {
223-
options.isTest = $searchParams.data["filter[isTest]"];
208+
if (typeof searchParams["filter[isTest]"] === "boolean") {
209+
options.isTest = searchParams["filter[isTest]"];
224210
}
225211

226212
const presenter = new RunListPresenter();

0 commit comments

Comments
 (0)