Compare commits

...

282 Commits

Author SHA1 Message Date
github-actions[bot] e7bd1ee676 chore: Update version for release (beta) (#1042)
* chore: Update version for release (beta)

* update lockfile

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: nicktrn <55853254+nicktrn@users.noreply.github.com>
2024-04-18 17:53:33 +01:00
nicktrn 584c7da5df v3: prod worker graceful shutdown (#1034)
* graceful exit with timeout

* handle and display graceful timeout errors

* fix for very long waits

* changeset

* increase termination grace period to an hour
2024-04-18 15:46:14 +01:00
nicktrn c9e1a3e9c5 v3: fix dependency trigger and wait (#1030)
* fix dev dependecy resumes

* add catalog entry with multiple trigger waits

* update docker provider lifecycle command

* switch to x for clearing run filters

* Revert "fix dev dependecy resumes"

This reverts commit b4061f2ae71f21889adcf061928e2e77c436f0ed.

* fix dependency resumes, properly this time

* add catalog entry for dependency waits in loops

* advice in docs re parallel dependency waits

* fix link from v3 to v2 docs

* move lifecycle command logging to debug only

* Removed batchOptions from the trigger options and the docs

---------

Co-authored-by: Matt Aitken <matt@mattaitken.com>
2024-04-18 14:48:43 +01:00
nicktrn 2f5b4a8471 v3: fix raw queries for custom schemas (#1033)
* add custom validation to db url env vars

* extract schema from db url and use in all raw queries

* use qualified names in scheduling raw queries

* cook a few raw queries

* Added missing raw query schema specifier to DeploymentListPresenter

---------

Co-authored-by: Matt Aitken <matt@mattaitken.com>
2024-04-18 12:01:38 +01:00
Eric Allam 69f6891687 Fixed rebalancing when message is acked
I’ve also added a “rebalancer worker” that will periodically rebalance parent queues, in case have been any issues with rebalancing (basically a “sweeper of entropy” process)
2024-04-17 21:00:39 +01:00
Matt Aitken acd7681e58 Scheduled task docs (#1039)
* Scheduled docs v1

* Improved copy

* Rejigged the docs some more. Added a dynamic section
2024-04-17 17:41:16 +01:00
Eric Allam 180a5ef01d Fix pnpm lock file 2024-04-17 16:34:14 +01:00
github-actions[bot] 62544d3234 chore: Update version for release (beta) (#1038)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-17 16:33:23 +01:00
Eric Allam 9b045071d8 Better errors when trying to delete a schedule that doesn’t exist 2024-04-17 16:05:51 +01:00
Eric Allam 618d2207c6 Fixed the deployed scheduled task check by looking at the slug, not the task.id 2024-04-17 15:38:39 +01:00
Eric Allam 36d8bee14a Don’t delete task runs if the attached task schedule or instance is deleted 2024-04-17 15:26:35 +01:00
Eric Allam 44e1b87547 v3: various schedule fixes (#1040)
* Improve the SDK function types and expose a new APIError instead of the APIResult type

* Skip triggering scheduled tasks if the task isn’t found in the current deployment

Also fixes an issue when editing the environments of a schedule
2024-04-17 15:10:23 +01:00
Matt Aitken 71b0ef8f77 Don’t allow editing the Deduplication key when updating a schedule 2024-04-17 14:42:18 +01:00
Matt Aitken f66543c9c7 When updating a schedule in the UI, it now says “Schedule updated” 2024-04-17 14:33:11 +01:00
Matt Aitken 03b104a3d5 Added JSDocs to the schedule options in the SDK 2024-04-17 14:25:40 +01:00
Eric Allam fde939a30e Fixed empty strings making their way into schedule externalId 2024-04-17 11:59:29 +01:00
Matt Aitken 4986bfda2e Scheduled tasks (#1036)
* Database schema and migrations for schedules

* Added schedules to the side menu

* The pagination can optionally hide the page numbers for a compactive mode

* Filters for the schedule page

* Added triggerSource (“STANDARD”, “SCHEDULED”) to BackgroundWorkerTask

* Added the ability to disabled a LinkButton

* Started work on the schedule page

* Environment buttons

* The new schedule form styles

* Added a cxouple of extra fields

* Allow a checkbox to have a rich label

* Added cronstrue package to the webapp

* WIP creating tasks using the form

* Improved the form styling

* Creating schedules is working in the UI

* Minor improvements

* Basic schedule table is displaying

* Creating tasks with triggerSource = scheduled. Refactored how task metadata is stored and accessed to be cleaner

* Resource route

* Fixes in the form

* Added a gap between the environments

* WIP on OpenAI generating CRON expressions

* AI generated CRON expressions is working

* Fix for the CRON field being uneditable after an AI generation

* Improvements

* Table padding

* useThrottle now behaves correctly

* Added filtering to the schedules list

* Improved the layout and fixed CRON search

* Fixed pagination for the schedules list. Just use a regular Prisma query

* Page size of 20

* Added links to the schedule rows

* Get rid of the Last run column for now

* Implement triggered scheduled tasks

Also implemented superjson payloads and dev runtime environment “presence” with RuntimeEnvironmentSession

* Move CronPattern and CreateSchedule into a common client-accessible file

* Latest UI changes

* Fix for creating a task schedule with a blank dedup key

* Refactor the human to cron stuff into a separate file and use json_object OpenAI response format

* Fix for trying to use a hook on the server-side…

* A couple of fixes to the new schedule form

* WIP on viewing a scheduled run

* Make the filters all optional

* Use the RunListPresenter from the schedule presenter

* Display a table of runs… the wrong runs but still

* Runs from the schedule

* Deleting schedules from the UI

* Tidied imports and fixed name of options object

* Disabling a schedule

* Editing schedules

* Tidied imports

* Added icons to the task list, needs some design love

* Added a tooltip for CRON pattersn

* Show the last run in the schedules table

* Some tweaks

* Added a placeholder to the CRON AI field

* Improved the trigger source icon

* Dim out disabled schedules

* Scheduled tasks have the correct icon in a run

* Added the task source icon to the test task list

* Added the date field component to storybook

* Style improvements to the date field

* Implement Task Schedule API

* added a medium sized variant to the date field

* Fixed replay run for superjson payload types

Also now linking from the replaying run to the original run (using span links)

Also added a project metrics prometheus endpoint to detect the state of the queues

* WIP on allowing different forms for testing

* If you pass a string to prettyPrintPacket which is json or superjson, safeParse it first

* Test page, deal json and superjson. Started splitting UI for schedules

* WIP on schedule form

* Removed the Label from the DateField

* WIP on test schedule form

* Fix for the runs page showing the wrong message when there are no runs from time filtering

* Removed labels from the DateField

* Fixes for the form

* Test runs are working for schedules

* Fix for nextScheduledTimestamps in triggerScheduledTask

* Implement idempotency key support and fix issue with cancelled runs causing concurrency usage

* Added API documentation for the schedule API

* Remove log

* Removed console log from runs page

* Transform the recent runs test data on the server

* Fix for hydration mismatch

* Current date as the default for the test form

* Recent payloads working

* Delete schedule modal

* Deal with empty strings from the form

* Set the initial value for the scheduled test form

* Add option to print console logs in the dev CLI locally (issue #1014)

* Export queue from the SDK

* Fix for schedules list when you have no schedule tasks

* Blank states improved

* Make task schedules more generic, to support additional schedule generators in the future

* Removed log from maqrs

* Removed “v3/schedules” export from the SDK

---------

Co-authored-by: Eric Allam <eallam@icloud.com>
Co-authored-by: James Ritchie <james@jamesritchie.co.uk>
2024-04-17 11:14:23 +01:00
Matt Aitken a3abe4ca08 Documentation edits made through Mintlify web editor 2024-04-17 09:08:08 +01:00
Matt Aitken fad32a79dd Documentation edits made through Mintlify web editor 2024-04-17 09:07:16 +01:00
Matt Aitken 9d843caf83 Documentation edits made through Mintlify web editor 2024-04-16 14:33:07 +01:00
nicktrn 5eaf7bca34 don't require git for build 2024-04-12 19:02:38 +01:00
nicktrn 4a0368dec3 lockfile 2024-04-12 18:18:49 +01:00
github-actions[bot] f10f120e55 chore: Update version for release (beta) (#1028)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-12 18:18:03 +01:00
nicktrn d3a18fbdf6 v3: cli and misc fixes for windows (#1027)
* fix: otlp-importer script

* update pluginPath

* Refactor script to ensure compatibility with both Windows and Unix platforms

* Revert command changes

* Fix: Init command was failing on Windows because of bad template paths

* Removed resolveInternalFilePath

* Path fixes for the dev command

* Fix for the import paths being wrong on Windows because the contain backslashes

* The metaOutputKey should have forward slashes in it

* Print the banner immediately, otherwise with bad internet you get a blank console for a long time

* Try normalizing the trigger.config import path

* Allow tsx and jsx files

* Improved task file names and paths

* Removed closing bracket for the upgrade message

* Log out the metafile outputs for debugging

* Log out the entryPointContents before esbuild

* Log the metafile out

* ballmerize the cli

* replace npm-watch with plain old nodemon

* fix text inputs on windows

* changeset

---------

Co-authored-by: Kritik Jiyaviya <kritikjiyaviya07@gmail.com>
Co-authored-by: Matt Aitken <matt@mattaitken.com>
2024-04-12 18:12:01 +01:00
nicktrn b82a07ad1c add missing ca certs to prod worker 2024-04-12 18:07:54 +01:00
Eric Allam fa81bf356a Fix pnpm lock file 2024-04-12 12:34:32 +01:00
github-actions[bot] fec4dc3bef chore: Update version for release (beta) (#1025)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-12 12:34:02 +01:00
Eric Allam a80089c88f Fix the package version of trigger.dev with the rest of the @trigger.dev/* packages 2024-04-12 11:19:04 +01:00
nicktrn 63a643b7c9 v3: fix digest extraction (#1024)
* fix image digest extraction

* fix project ref cli arg override

* Create late-steaks-behave.md

---------

Co-authored-by: Eric Allam <eric@trigger.dev>
2024-04-12 11:10:52 +01:00
nicktrn efd970a901 v3: correctly handle triggering tasks prior to deploy (#1019)
* add waiting for deploy status

* sort tasks filter

* marqs: optional timestamp on enqueue

* add new status to consts

* detect and execute tasks waiting for deploy

* display runs list even without any deploys

* ensure we only execute after the run creation tx has completed

* ensure correct execution order after waiting for deploy
2024-04-12 11:03:22 +01:00
Eric Allam 89a5e9f7da Move gc to it’s own admin endpoint 2024-04-11 16:24:40 +01:00
James Ritchie 404931e224 Made the message when you’re in a developer preview project punchier 2024-04-11 14:34:45 +01:00
Eric Allam 57bf98307a Run gc before taking heapsnapshot 2024-04-11 13:53:14 +01:00
Eric Allam 7551adfeb6 Add ability to disable tracing to test if it’s the cause of the memory leak 2024-04-11 13:14:02 +01:00
Matt Aitken 69117e8ba1 Re-arrange the mint.json file so the v3 docs become the default 2024-04-11 11:22:18 +01:00
Eric Allam b580d53b59 Better identifiy heap snapshots 2024-04-11 11:13:36 +01:00
Eric Allam d81e21d2ec Remove admin config endpoint 2024-04-11 10:44:26 +01:00
Matt Aitken 826a64fe6f If your dev run hasn’t started, display an info panel saying you need to run the CLI 2024-04-11 10:36:44 +01:00
Eric Allam d1849b0ea9 Adding admin config endpoint (temp) 2024-04-11 10:30:45 +01:00
Eric Allam 3aa634d2b8 Add a metric for number of event repo subscribers and project subscribers 2024-04-11 10:30:37 +01:00
Matt Aitken 7de3fb01c7 Added more docs for replaying a run 2024-04-10 18:35:43 +01:00
github-actions[bot] 4ae9a3feac chore: Update version for release (beta) (#1023)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-10 18:04:20 +01:00
Matt Aitken 5b18298fef Multiple machine CLI support (#1022)
* If a cli PAT exists, associate the authorization code with it

* Improved the error messages
2024-04-10 17:59:21 +01:00
nicktrn 7a9bd18ba2 v3: stop swallowing deployment errors and display them better (#1020)
* exit worker with code 111 for handled errors

* task monitor will ignore previously handled errors

* fix logging of index errors

* deployment error component

* changeset
2024-04-10 17:58:51 +01:00
Matt Aitken 03af44e545 Improved the title of one of the debugger scripts 2024-04-10 11:23:17 +01:00
Matt Aitken 5776257663 Changed the Discord link to use the trigger.dev one. Mainly to bump the GitHub action 2024-04-10 11:08:56 +01:00
Matt Aitken d3997c9fc6 Merge branch 'main' into changeset-release/main 2024-04-10 10:20:56 +01:00
github-actions[bot] 0676ea9668 chore: Update version for release (beta) (#1013)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-10 10:14:00 +01:00
github-actions[bot] 7a4721e122 chore: Update version for release (beta) 2024-04-09 16:44:12 +00:00
Eric Allam a5aed0d139 Fix memory leak by calling punsubscribe instead of unsubscribe
Also adding a /metrics endpoint with a few custom metrics and the built-in prom-client ones
2024-04-09 17:42:28 +01:00
Eric Allam f1480595d5 Stop swallowing webapp logs when the span isn’t being recorded 2024-04-09 17:11:09 +01:00
Eric Allam 328947dbfd Use the dashboard url instead of the API url for the View logs link 2024-04-09 15:15:16 +01:00
Eric Allam 279717b092 v3: pnpm deploy & various fixes for dependency resolution (#1012)
* Don’t swallow some error messages when deploying

* Try and resolve dependency versions using pnpm/npm/yarn

* Adding some debug logs around dependency resolution

* Use npm list instead of npm show to resolve deps

* Improve the dependency resolution
2024-04-09 15:03:31 +01:00
Matt Aitken 702d198445 Latest lockfile 2024-04-09 09:25:28 +01:00
github-actions[bot] 803f3c15ab chore: Update version for release (beta) (#1010)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-09 09:24:46 +01:00
nicktrn 1c24348f7d v3: add openssl to prod image and fix deploy command auth via env var (#1011)
* add openssl to prod worker

* make deploy command accept auth token via env var
2024-04-08 19:28:35 +01:00
Matt Aitken f854cb90eb Replay\Cancel a run (v3) (#1006)
* WIP on replaying a task from the run page

* Don’t pass the existing runs idempotency key, it will cause the replay to always return the original run

* Replay from the run list

* Don’t use fetchers in the replay/cancel dialogs

* API endpoint for replaying a run

* REST API docs (mostly coming soon) but added replay run

* replayRun function added to the SDK

* Cancel run added to the SDK

* v3-catalog file to test canceling and replaying

* Changed the SDK to be runs.replay and runs.cancel

* Removed comment

* Latest lockfile
2024-04-08 19:08:44 +01:00
github-actions[bot] 7268f17b00 chore: Update version for release (beta) (#1009)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-08 14:11:17 +01:00
nicktrn 624ddce32f v3: fix prod worker node_modules permissions (#1008)
* set node user prior to npm install

* changeset

* add ffmpeg and ffprobe catalog entries
2024-04-08 14:05:05 +01:00
Matt Aitken 9be1557bb7 CLI v3 binary changed from trigger.dev to trigger.dev, to fix a Windows issues (#1007)
* Changed the v3 CLI binary name from trigger.dev to triggerdev to fix a Windows issue

* Changed the exec to triggerdev
2024-04-08 14:03:12 +01:00
nicktrn 6ce6f8e3ad fix lockfile 2024-04-05 16:51:10 +01:00
github-actions[bot] d462b7a51b chore: Update version for release (beta) (#1001)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-05 16:45:38 +01:00
nicktrn f2894c177a switch to busybox and only call post start hook for restore (#1005) 2024-04-05 15:47:58 +01:00
Matt Aitken e35f29764a We want retrying enabled in dev by default, it’s confusing otherwise (#1004) 2024-04-05 14:45:19 +01:00
nicktrn 1207efbbad v3: prod image upgrade and fixes (#1003)
🚢 Publish Infra Images / build (coordinator) (push) Has been cancelled
🚢 Publish Infra Images / build (kubernetes-provider) (push) Has been cancelled
* fix shutdown after final attempt

* fix workdir permissions

* correctly handle self-hosted deploy errors

* switch to node:20-bookworm-slim

* add binaries catalog file

* changeset

* skip recommended packages

* post start hook retries

* comment and improve lifecycle retry
2024-04-05 13:09:07 +01:00
nicktrn 7ea8532cce v3: crash observability (#1002)
* indexing resource limits

* refactor and storage limits

* handle and display worker crashes

* enable prod cancellation

* fix reconnect delay

* improve crash messages

* log and display crash events

* changeset
2024-04-05 13:00:20 +01:00
Eric Allam 6642228f26 Fixing the dev & shared queue consumer telemetry so we can see what’s going on 2024-04-04 17:54:33 +01:00
Matt Aitken d39145d810 Defer migration guide: string not a date in the payload 2024-04-04 17:03:22 +01:00
Matt Aitken 8886bb76e0 Defer migration guide v1 2024-04-04 17:02:15 +01:00
Matt Aitken 4b72726078 defer.run migration guide 2024-04-04 17:02:15 +01:00
Eric Allam 6dcfeadaca v3: pnpm support (#1000)
* WIP fixing pnpm nested install strat

* Add changeset

* Pass (and log) cwd during background worker initialization

* Log out the env

* manually add the .pnpm/node_modules to NODE_PATH if in a pnpm repo

* Remove supports color dep
2024-04-04 16:13:04 +01:00
Matt Aitken ae839ebe11 Renamed Limits to “Limits & Performance” 2024-04-04 11:17:02 +01:00
Matt Aitken 5d0d71c2ae Added a section on upgrading your project 2024-04-04 11:17:02 +01:00
James Ritchie 1239a3ceb9 V3 project labels (#998)
* Added a side menu label to indicate your project version

* Added a link to request access to v3

* Toggle password visiblility of the env var inputs
2024-04-04 09:53:59 +01:00
Matt Aitken dd31b1e668 Fix for absolute URL from card 2024-04-04 09:23:40 +01:00
James Ritchie a707446989 Fixed Triggers pages not scrolling (#999) 2024-04-04 09:18:18 +01:00
Matt Aitken eb050f6730 Fix for typo on triggering page. “yourTask.batchTriggerAndWait” -> “yourTask.batchTrigger” 2024-04-04 09:12:29 +01:00
Matt Aitken 56d9bf7c67 Started the v2 -> v3 docs 2024-04-03 19:40:19 +01:00
Matt Aitken 73e469daf5 Added a known issues page to the docs 2024-04-03 14:30:43 +01:00
Matt Aitken 0382cf8719 Fix for bad tests link 2024-04-03 14:16:46 +01:00
Matt Aitken 0fe835492d Added more to the feature matrix 2024-04-02 19:47:55 +01:00
James Ritchie 29b69160e1 Fix for run page not scrolling the full runs content 2024-04-02 19:02:35 +01:00
Eric Allam ae9efe3d8b Added an error property to the problem JSON response 2024-04-02 17:46:40 +01:00
Eric Allam 3feb5ffb5f whitelist more api paths in the rate limiter 2024-04-02 17:29:53 +01:00
Eric Allam 7fb482de64 Allow OPTIONS requests in the rate limiter 2024-04-02 17:08:43 +01:00
Matt Aitken 6f11584aaa Remove our PostHog analytics from the docs 2024-04-02 15:35:36 +01:00
Matt Aitken 43e240cd50 Absolute link to v3 docs from v2 docs home, to fix nav issue 2024-04-02 15:30:44 +01:00
Matt Aitken afe7f410c7 v3 docs improvements (#993)
* WIP on adding versioning to the docs to make it less confusing

* Link to v3 from v2 and vice versa. Also added a warning to the v2 docs

* Make v3 the default

* Trigger config docs about bundling

* Next stpe to triggering guide from tasks overview

* Server actions guidance for triggering

* Moved the trigger.config docs to the end of fundamentals
2024-04-02 14:32:28 +01:00
nicktrn 28837f39b3 Prevent crash on upgrade errors 2024-04-02 13:58:40 +01:00
Eric Allam 5c64cefaf0 Fix pnpm lock file 2024-04-02 13:25:05 +01:00
github-actions[bot] dc5ed0a0cd chore: Update version for release (beta) (#995)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-02 13:24:32 +01:00
Eric Allam e7e7397ed9 Force CLI change 2024-04-02 13:20:26 +01:00
Eric Allam 49184c7189 Add trigger.dev changeset 2024-04-02 13:03:37 +01:00
Eric Allam eb60126284 Fixed batched otel in dev and made prod configurable to be batched as well 2024-04-02 12:58:56 +01:00
Eric Allam d876c358d2 Update the default env var concurrency limit and set them when orgs and envs are created 2024-04-02 11:55:00 +01:00
Eric Allam 6e9c8ab555 Fix pnpm lock file 2024-04-02 11:13:31 +01:00
github-actions[bot] 9a3eb289cd chore: Update version for release (beta) (#992)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-02 11:12:56 +01:00
nicktrn 51315fc3c8 v3: shared queue pool (#994)
* configurable retry delay checkpoint threshold

* shared queue consumer pool

* add more attributes to marqs spans

* inject trace context into queued messages
2024-04-02 10:22:52 +01:00
Eric Allam 4047f00562 Allow each service to define their own name 2024-04-01 22:01:36 +01:00
Matt Aitken 545f85b44e Fix for shared queryKey between useRunDetails and useRunStatuses 2024-04-01 13:20:39 +01:00
Eric Allam abe202e7b7 Fix pnpm lock file 2024-04-01 10:54:29 +01:00
github-actions[bot] ae27fd83af chore: Update version for release (beta) (#987)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-01 10:53:45 +01:00
Eric Allam c702d6a9ca v3: better task metadata errors (#991)
* v3: better handle task metadata parse errors, and display nicely formatted errors (dev, deploy, UI)

* Add changeset
2024-03-31 21:47:05 +01:00
Eric Allam 9af2570da6 Retry 429, 500, and connection error API requests to the trigger.dev server 2024-03-31 16:06:12 +01:00
Eric Allam a946797d95 v3: Upgrades to MarQS (#989)
* Upgrades to MarQS

- It’s “queue choosing” algorithm is now MUCH better (pretty much didn’t work before) and should be performant even when we have a bunch of prod queues
- There are now concurrency limits at the environment and organization level, as well as the task/queue level. So if any of them are at capacity the message won’t be dequeued. This means we can have an org wide concurrency limit, as well as different limits for dev/prod/staging
- I’ve added an admin API that can be used to update the org/env concurrency limits

* More MarQS upgrades

- Extract the queue priortity choosing strategy into an interface
- Implement a much better weighted average strategy
- “Slide the window” of queue candidates if the parent queue sends all at-capacity queues (so we won’t get stuck attempting to choose the same 12 full queues)
- Added some unit tests for the priority stuff
- Added some ideas for expanding the priority choosing strategy with more dynamic features

* Fixes an issue with the shared queue consumer relying on the queue name to get the env id
2024-03-31 15:58:00 +01:00
Eric Allam 8c4df326cc v3: Better dev/deploy errors and handle image build errors (#988)
* Better error output when an ESM only package is required

* Add better errors for npm package errors, and bring deploy errors to the dev CLI

* Handle depot build errors and write out a temporary file to view the build logs

* Add changeset
2024-03-29 12:46:11 +00:00
Eric Allam 11b997d2bf Make env var value input fields passwords 2024-03-29 09:05:53 +00:00
Eric Allam 8694e573f5 Fix CLI logout and add list-profiles command 2024-03-28 22:18:15 +00:00
Eric Allam b271742dca v3: implement configurable log levels via config file and env var (#985)
* v3: implement configurable log levels via config file and TRIGGER_LOG_LEVEL

Also, test runs automatically set the TRIGGER_LOG_LEVEL to debug

* Fix type error and changeset

* Added a Node.js runtime check for the CLI dev command
2024-03-28 22:00:38 +00:00
Eric Allam 6f9f25481e Update pnpm lock file 2024-03-28 19:55:14 +00:00
github-actions[bot] 67aaffb6fc chore: Update version for release (beta) (#984)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-03-28 19:54:21 +00:00
Eric Allam e3cf456c69 v3: fix string and non-standard outputs (#983)
* Fixed string outputs missing and incorrectly formatted

* Handle non-serializable outputs like a function

* Add changeset
2024-03-28 19:51:29 +00:00
Matt Aitken bf7827e7b8 Fix for the test latest runs showing other environments 2024-03-28 18:14:47 +00:00
Eric Allam bc020a3ffe Get the internal telemetry ready for test/prod 2024-03-28 18:12:01 +00:00
Eric Allam b361afbfe4 Update pnpm lock file 2024-03-28 16:29:17 +00:00
github-actions[bot] a3d809740d chore: Update version for release (beta) (#979)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-03-28 16:28:41 +00:00
Eric Allam f93eae300e v3: superjson dynamic import and deploy fixes (#982)
* v3: Dynamically import superjson and fix some bundling issues

* Added changeset

* Better error handling in the registry proxy and catch uncaught exceptions and unhandled promise rejections instead of crashing the server

* Await the prettyPrintPackage
2024-03-28 16:22:00 +00:00
Matt Aitken a2365e406d Fixed v3 docs link in the sidebar 2024-03-28 14:46:47 +00:00
Eric Allam 42d319c2d1 Remove all the v3-catalog code for triggering in code 2024-03-28 14:33:01 +00:00
nicktrn b66d5525ef v3: machine config (#978)
* add and use machine config

* assign tasks to worker nodes only

* add secure flag to zod connection

* changeset

* add pre stop hook

* don't use secure connection by default

* pass more identifiers to provider and apply labels
2024-03-28 14:21:49 +00:00
Eric Allam 719c0a0b94 v3: better clock management for spans/logs in CRIU envs (#980)
* durable clock WIP

* Fixed incorrect span timings around checkpoints by implementing a precise wall clock that resets after restores

* Add changeset
2024-03-28 14:20:44 +00:00
Matt Aitken f1c768a255 Docs for API keys, fixed links to docs from app (#981)
* Add missing API keys docs and link from the web app to them. Renamed Server API keys to Secret keys

* Correct link to env var docs

* Duplicate image fixed on environment variables docs
2024-03-28 14:00:42 +00:00
Matt Aitken d9c9e80bc4 Changed "Worker" to "Version" in the CLI dev command key 2024-03-28 12:13:58 +00:00
Matt Aitken d39932ebf7 Changed the throttle so the function fires at the end of the period, not immediately when first called 2024-03-28 12:05:48 +00:00
Matt Aitken 9bcb8cb42a Ignore the DEBUG env var when deploying 2024-03-28 11:28:34 +00:00
nicktrn 2374f8e8ac fix docs for shopify scopes 2024-03-28 09:57:04 +00:00
github-actions[bot] a22b5869e4 chore: Update version for release (beta) (#977)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-03-27 19:43:19 +00:00
Eric Allam f1571cbfab Fixed an issue where the trigger.dev package was not being built before publishing to npm 2024-03-27 19:39:18 +00:00
Eric Allam 222dfed4d2 Fixed pnpm lock file 2024-03-27 18:56:10 +00:00
github-actions[bot] 89c0e68fb5 chore: Update version for release (beta) (#975)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-03-27 18:54:28 +00:00
Eric Allam 5b745dc1a4 Improved the dev CLI output 2024-03-27 18:36:52 +00:00
Eric Allam 2dea8dec35 Implement simple 7 day log retention and blank states (#976) 2024-03-27 17:22:16 +00:00
Matt Aitken 252f5b3967 Fix for janky scrolling on the run page 2024-03-27 16:55:03 +00:00
Eric Allam 014d233339 Couple changes to better test the output/payload offloading 2024-03-27 16:27:45 +00:00
Eric Allam e398bdf859 Only use 16vcpu on e2e 2024-03-27 16:27:28 +00:00
Matt Aitken 426316aa3c There was an error about a div being in a paragraph 2024-03-27 16:23:11 +00:00
Matt Aitken 4f9b8d6721 Task that logs a lot 2024-03-27 16:22:29 +00:00
Matt Aitken a345ad2d57 Fix for the large JSON generator 2024-03-27 15:49:46 +00:00
Matt Aitken e2b67400ea TRIGGER_API_URL defaults to https://api.trigger.dev 2024-03-27 15:18:40 +00:00
Eric Allam c4dc2849af Remove changesets from the pre.json (accidentially added) 2024-03-27 15:17:42 +00:00
Eric Allam a8263dec86 Include core-apps in trigger.dev typecheck tsconfig file 2024-03-27 15:01:02 +00:00
Eric Allam 53054d0146 Enter into changesets “beta” tag for packages 2024-03-27 14:55:14 +00:00
Eric Allam 74236ae7c1 Fix changeset workflow (thought I’d already fixed this?) 2024-03-27 14:47:42 +00:00
Eric Allam 055d468b38 Setup socket.io redis adapter and fix deployment builds for superjson error 2024-03-27 14:42:10 +00:00
Matt Aitken fc473105e3 Better error messages if you haven’t set the API URL or SECRET for the v3 client 2024-03-27 14:08:13 +00:00
Matt Aitken 3a35d60091 Change the npm tag from "latest" to "beta" in docs/cli/onboarding (#974)
* The v3 init command defaults to the “@v3” tag

* Change it to beta

* Updated the docs and in-app docs

* Updated GitHub action
2024-03-27 13:39:53 +00:00
Eric Allam f11c0f991b Log out the headers and url when no key is found 2024-03-27 13:30:47 +00:00
Eric Allam 1cf181fd02 Control api rate limiter logging through env vars 2024-03-27 12:42:05 +00:00
Eric Allam 412d0149d5 Fixed release workflow 2024-03-27 12:37:23 +00:00
Eric Allam d614c8d2ca More CI fixes for node 20/pnpm 8 2024-03-27 12:33:24 +00:00
Eric Allam 4329d1130a Attempt to fix typecheck 2024-03-27 12:30:54 +00:00
Eric Allam 17f9fc8273 Upgrade to pnpm 8.15.5 to fix ERR_INVALID_THIS issue
https://github.com/pnpm/pnpm/issues/6424
2024-03-27 12:27:19 +00:00
James Ritchie db611bebba Text truncation fixes (#973)
* Span titles and code blocks truncate when window is small

* Tree view toggle arrow doesn’t have red hover state if row is an error
2024-03-27 12:16:20 +00:00
Eric Allam 215b60c742 v3: superjson output support (#971)
* superjson output support

* Better support for superjson in the task events, limiting output attributes

* Offload large outputs to object store (r2)

* Finishing up the offloading of large outputs/payloads to an object store
2024-03-27 11:57:22 +00:00
Matt Aitken 9c89ab40aa Throttle the live streaming of the run to 1 second updates (#972) 2024-03-26 14:20:10 +00:00
Matt Aitken b342e06583 Fix for scrolling with long run traces 2024-03-26 11:30:05 +00:00
Eric Allam 4418de515b Whitelist api endpoints that don’t use an authorization endpoint from the rate limiter 2024-03-25 21:18:05 +00:00
Eric Allam 0d2a71c5cf handle when traces/logs don’t have traceIds/spanIds 2024-03-25 21:10:31 +00:00
Eric Allam 9e02b45808 Fixed export 2024-03-25 17:24:23 +00:00
Eric Allam 395abe1b92 v3: Various fixes to support papermark use-case (#970)
* Adding a changeset for v3

* Add a version field to @trigger.dev/core-apps package.json

* Build trigger.dev when doing a prerelease

* bundle @trigger.dev/core-apps with trigger.dev cli

* Fix the init command config template

* Don’t use * for the @trigger.dev/core dep version specifier

* strip workspace: from the package version before installing it

* Added dependenciesToBundle config option to bundle ESM only packages

* Added logging around resolving dependency paths

* Try again

* Resolve dependencies based on the project dir first

* flip the bundled default

* Adding some logs around dev task completion notifications

* Adding some additional logs

* Add more logs

* Write out the log using process.stdout

* Store pending completion notifications and resume them when awaited (fixes race condition)

* Cleanup some of the logs

* Copy over the postinstall step from the projects package.json

* Add support for including additional files when deploying (e.g. prisma schema)

* Don’t run scripts when resolving deps

* copy all the files just in case anything is needed in postinstall

* Remove duplicate option

* Use the tag when outputting the dev command

* Remove the postinstall script

* add the trigger dir to the config if the default is not chosen

* Remove the “hud” display in the dev command

* trigger file names with dashes now work

* Better file watching in dev

* Much better duplicate ID experience now

* Much better “Project not found” error

* Export the handleError function types from sdk

* Add support for configuring instrumentation

* Upgrade and unify @opentelemetry/* packages (and remove storybook from the webapp)

* Fix typescript error in react package

* Upgrade react types in webapp

* Allow span icons to be determined based on the span name (e.g. prisma:)

* Ignore built-in env vars when checking for env vars, and allow continuing the deployment even if missing env vars were detected

* Improve the retry.fetch default behavior and option structure

* Fixed typescript errors with packages/email react types

* Update the retry.fetch docs
2024-03-25 17:02:49 +00:00
Matt Aitken b35eebb666 Rate limit API requests and changed SQS reading speed (#969)
* Trying to use the @upstash/ratelimit package with ioredis…

* WIP using the redis package instead

* Revert the action back

* Removed redis

* Started refactoring

* SQS setting for the poll interval. Set the default queue reading to be slower

* API rate limiter as Express middleware

* Organise imports

* Fixed spelling mistake “limitter”

* No authorization header response is problem+json
2024-03-25 16:39:20 +00:00
nicktrn 9ecf07731a v3: small features and fixes for prod infra (#968)
* add checkpoint restore events

* fix retries.enabledInDev

* fix tsconfig paths

* fix provider build and dev

* update kubernetes provider and fix builds again

* upgrade prod base to node 20

* update infra publish workflow

* rethrow k8s errors after logging

* shorten index container names

* fix error type assertion

* rename type assertion

* remove resource limits for now

* add missing run id on create

* add push to deploy command for self-hosting

* checkpointing fixes

* update coordinator image

* ensure valid registry login

* delete checkpoint archive after successful push

* log options on error

* structured logs for socket connections

* fix structured log merge

* exit process after checkpointing

* update restore pull secret name

* append shortcode to restore names

* log handler payload

* disable post start lifecycle hook

* pass in coordinator host via volume

* replace dapi with taskinfo

* add missing restore label

* don't restart restored containers

* remove init container from create

* atomic post-completion checkpoints

* switch to run id for container names

* improve wait accuracy

* measure basic checkpoint perf

* always log disconnect reason

* use system clock to end wait spans

* checkpoint readiness and cancel signals

* restore from checkpoint events and fix statuses

* remove attempt id env var

* restore dependencies from events

* reconnect wip

* lifecycle hooks are back

* fix hooks and improve reconnect

* make docker send postStart hook

* only checkpoint for retry if large delay

* fix a few more resume issues

* lifecycle hook fixes

* skip connection handler when waiting for post start hook

---------

Co-authored-by: Eric Allam <eric@trigger.dev>
2024-03-25 16:28:56 +00:00
Matt Aitken b890a64046 The in-progress stripes are much more subtle 2024-03-25 16:20:51 +00:00
Matt Aitken 4266b308e7 Link from trigger() to a run. Link back to the triggering run. (#967)
* Schema for SpanMessagingEvents

* RunFilters status map was missing the key

* Link from a trigger to the run

* Two-way links working
2024-03-25 11:28:35 +00:00
Matt Aitken 67c62f7d25 v3 is restricted to approved orgs (#965)
* v3 projects can only be created if the org has permissions to do it

* Admin page

* Removed unused classes

* Admin org page, with links between user and org pages

* Set v3 enabled or not
2024-03-25 11:28:21 +00:00
Matt Aitken c6d6a9191c Hide show.actions from the properties panel (#964) 2024-03-22 19:53:28 +00:00
Eugene Yaroslavtsev cd8f6b9af0 fix: [nestjs integration] fastify HTTP adapter detection now works correctly for response headers (#938)
* fix: [nestjs integration] fastify HTTP adapter detection now works correctly for response headers

- fixed a bug where Fastify responses were incorrectly processed as Express due to overlapping method names.
- introduced type guards to differentiate between Express and Fastify response objects.
- bumping @trigger.dev/nestjs patch version to 2.3.19

* fix TS type check build error
2024-03-22 14:41:21 +00:00
Kritik Jiyaviya a49b659701 feat: Ability to revoke an invite to an org (#902)
* feat: Ability to revoke an invite to an org

* Resolve a conflict with main

---------

Co-authored-by: Matt Aitken <matt@mattaitken.com>
2024-03-22 14:35:49 +00:00
Edward Fu 61c9a7c7d6 Fix opanai code example in docs (#961) 2024-03-22 14:17:37 +00:00
Matt Aitken de302858f8 v3 docs (#929)
* Started building out the docs navigation

* Drafted out a lot of the docs pages. With just titles and descriptions with a coming soon message for now.

* A ton more scaffolded pages with titles, descriptions, coming soon/incomplete docs messages

* Created the remaining page stubs

* Introduction page improvements

* Lots of docs changes, mostly the task overview page

* Moved some bits around

* Task overview fleshed out

* Triggering docs page

* Regular task page

* CLi dev docs

* Docs for debugging locally

* Environment variables docs

* Added env var images

* Deployment guides

* Updated the title of “Integrations” to “Deployment integrations” to make it less confusing

* Lots more docs pages

* Changed the language to JavaScript

* Fixed typos about queues and added a simple code sample

* Moved the local debugger docs into the CLI dev page

* Added the CLI deploy options

* Delete the local debugger page

* Updated the logging page with structured logging

* Added the new CLI deploy options

* Change to h4 for one of the options

* Errors page

* WIP on retrying

* Retrying guide

* Wait docs pages

* Queuing docs simplified, partial written

* Queuing and concurrency docs

* Run tests docs

* New combined error and retrying page

* Errors and retrying page updated

* More docs

* Added a possible configurations coming soon table

* Created a limits page

* Added warnings about how you need to get early access

* Minor fixes for the docs
2024-03-22 14:09:27 +00:00
Matt Aitken cf9d466b9e Basic JSDocs for the v3 SDK (#963)
* task(), retry and queue

* Added machine JSDocs

* Add JSdocs for the run function params
2024-03-22 14:09:06 +00:00
Eric Allam 7b3ffe040b v3: config file is now ts by default and uses named exports (#962)
* A bunch of changes to the config file:

- No more default export, the config is exported via the `config` named export
- `handleError` is moved to a separate named export
- Added the ability to include additional packages (e.g. wrangler) that aren’t included by default (think CLI usage)
- More reliably install packages in dev
- The config is now built first instead of directly imported, meaning we can now support typescript config files (`trigger.config.ts`)

* Update to trigger.config.ts in `trigger.dev init`
2024-03-21 10:40:44 +00:00
Eric Allam 737ff9928c When cancelling a run in the UI make sure the task run status is cancelled not interrupted 2024-03-20 13:22:50 +00:00
Eric Allam fc4b95d69e A bunch of deployment fixes (#960)
- Time out deployments to prevent never-ending building/deploying
- Use the CURRENT worker deployment when determining which version to execute
- Fix some of the deployment error handling logic
2024-03-20 13:15:48 +00:00
James Ritchie 6c546936f3 Improved v3 feedback button (#959)
* Removed feedback button from PageHeader

* Show a different feedback button in the side menu for v3 projects

* Fixed issue with button wrapping on smaller width browser
2024-03-20 12:16:20 +00:00
James Ritchie 08e4fc002f V3 UI improvements (#958)
* Adding a v3 feedback button top-right

* Changed paused to frozen for attempts

* Reordered and renamed run statuses

* Dropdown status menu shows tool tips on hover

* Added a new DP feedback button in the header

* Created a more useful feedback sheet

* Added loading bar animation to storybook

* Switch the integration page to point at the “feature” help instead of “integration” which has been removed

---------

Co-authored-by: Matt Aitken <matt@mattaitken.com>
2024-03-19 20:36:04 +00:00
Eric Allam 0e347b001b Don’t override TRIGGER_SECRET_KEY and TRIGGER_API_URL with local env vars 2024-03-19 16:04:31 +00:00
Matt Aitken 2e56af9742 Tasks page live-reloads with new tasks (#957)
* DevPubSub: use the schema that’s already been defined

* Publish a message to projectPubSub when a new DEV worker is created

* Live reloading of the tasks table (for dev tasks)

* Live notifications for deployed workers as well as local workers
2024-03-19 15:42:57 +00:00
Eric Allam dc5eb68a0b Fix typecheck errors because of resolveJsonModule 2024-03-19 15:33:09 +00:00
Eric Allam 891278ab1f Enable loading package.json inside of core 2024-03-19 15:24:01 +00:00
Eric Allam 41e1ac9e0b v3: nanosecond precision task events and immediate mode for dev events (#956)
* Store startTime in nanoseconds since epoch, ensure logs using nanosecond precision for their timestamp (so they are not out of order)

* export task events immediately in dev
2024-03-19 15:06:47 +00:00
Matt Aitken c5aaabdb4e The run timeline updates on the client if there's no new data (#955)
* Use “@v3” instead of “@latest” for the npx commands for v3

* Split the Timeline into a component so it can live refresh without re-rendering everything

* Timeline now live refreshes on the client every 500ms when run is executing

* The timeline bars now animate their position/width when it changes

* Export some more types from TreeView
2024-03-19 12:13:48 +00:00
Eric Allam affa3f25ee Trying beefier buildjet machines for CI 2024-03-19 11:38:48 +00:00
Eric Allam 86c24afb44 Support json traces and logs in the otel endpoints (#954) 2024-03-19 10:59:50 +00:00
Eric Allam 6afda40c35 Add depot setup action 2024-03-19 09:24:33 +00:00
nicktrn b585d1f2c5 v3: checkpoint restore events (#951)
* add checkpoint restore events

* fix retries.enabledInDev

---------

Co-authored-by: Eric Allam <eric@trigger.dev>
2024-03-19 09:02:56 +00:00
Eric Allam f26ba9a5cd Use depot.dev for docker builds (remove publishing to latest for now) 2024-03-18 20:52:08 +00:00
Eric Allam ba0ffacb15 Set the OTEL_EXPORTER_OTLP_ENDPOINT env var on dev workers 2024-03-18 20:16:25 +00:00
Eric Allam e55ca0279b Adding profile support to the v3 CLI (#953) 2024-03-18 17:35:29 +00:00
Matt Aitken 9304c447be Test, output and canceling improvements (#952)
* The vertical lines in the run tree view are now brighter

* Blank state when you click a task with no runs

* Canceling from the runs table

* Fix for styles coming through incorrectly to properties

* Fix for the OTEL output column having a $output prefix if it’s an object
2024-03-18 17:27:52 +00:00
Eric Allam ebec14093d Add healthcheck endpoint outside of remix if remix routes are disabled 2024-03-18 17:19:56 +00:00
Eric Allam 67cac97b26 Add DEPLOY_REGISTRY_HOST to allow configuring the deploy registry via the server 2024-03-18 15:56:28 +00:00
Eric Allam 0f32dee0eb Prevent depot command from displaying update message 2024-03-18 15:32:26 +00:00
Eric Allam 479d355b7b Setting DASHBOARD_AND_API_DISABLED=true will now disable the remix routes 2024-03-18 14:59:21 +00:00
Eric Allam 49e9dd8cb2 Disable fetch experimental warning and detect process resources (#950) 2024-03-18 13:46:24 +00:00
Matt Aitken cae3298564 Cancel run confirmation modal (#949)
* Tasks get the SHOW_ACTIONS property

* Override properties with the passed in ones in eventRepository

* Link from deployments to environment variables page, no need to link to docs

* “Show parent items” keeps the selected item selected

* LinkButton can accept onMouseDown, onMouseEnter and onMouseLeave

* “Show parent items” keeps any items focused and has a keyboard shortcut

* Action bar with focus button

* Canceling is working

* Nicer layout and button loading and disabled state

* US english spelling “Cancelling” -> “Canceling”

* Added a confirm modal to canceling

* Copy tweaks

* Fix for bad import

---------

Co-authored-by: Eric Allam <eric@trigger.dev>
2024-03-18 12:22:06 +00:00
nicktrn 1f190fe680 v3: fix dependency resume (#946)
* fix basic resume mechanics

* fix dependency resume

* fix deploy page sort

* remove task list console log

* checkpoint restore between attempts

* add checkpoint requirement checks to docker provider

* switch to raw execa for long statements

* catch and report pre-connection index errors

* graceful worker shutdown after completion

* fix final completion

* use same task process for each attempt

* update retry catalog entry

* add immediate return task

---------

Co-authored-by: Eric Allam <eric@trigger.dev>
2024-03-18 12:21:40 +00:00
Matt Aitken 85ec1af198 Don’t add proj_ to the front of the project ref 2024-03-18 11:05:09 +00:00
Eric Allam 52c364d691 v3: cli init command + CLI telemetry (#948)
* Adding telemetry to the deploy command

* Add ability to opt-out of telemetry in the CLI

* Remove log

* Unifying some telemetry stuff and adding it to the login command

* init command

* Remove extra trigger dirs

* Couple tweaks to the init CLI

* Don’t use npm init -y to create the package.json file
2024-03-18 10:12:11 +00:00
Matt Aitken a5d83971aa Cancel a run from the span view (#947)
* Tasks get the SHOW_ACTIONS property

* Override properties with the passed in ones in eventRepository

* Link from deployments to environment variables page, no need to link to docs

* “Show parent items” keeps the selected item selected

* LinkButton can accept onMouseDown, onMouseEnter and onMouseLeave

* “Show parent items” keeps any items focused and has a keyboard shortcut

* Action bar with focus button

* Canceling is working

* Nicer layout and button loading and disabled state

* US english spelling “Cancelling” -> “Canceling”
2024-03-17 11:42:30 +00:00
Matt Aitken 5b9c67aeef v3 deployments page (#945)
* Set the “current” deployment in WorkerDeploymentPromotion when a new deployment is completed

* Deployments page added to sidenav

* Basic deployments page working

* Pagination for deployments

* Right-align the pagination controls

* Improved the blank state

* Selected deployment panel

* Improved the side panel
2024-03-15 16:04:01 +00:00
Matt Aitken 1c98eb2b61 Use TaskRunStatus (instead of TaskRunAttemptStatus) (#944)
* Using the new run status instead of run attempt status

* The test page now uses the TaskRunStatus as well

* Organized imports

* Use TaskRun icons instead of attempt ones

* 10x zoom on the run timeline
2024-03-15 10:32:07 +00:00
Eric Allam f424c81086 No staging by default, and project external refs now are prefixed with proj_ 2024-03-14 15:11:31 +00:00
Eric Allam 0a33bf7206 v3: handle errors and customizing retrying (#943)
* Add the ability to handle errors at the task and project level

* Cancel in progress runs when disconnecting the dev CLI (in between attempts)

* unify the task executor across dev & prod
2024-03-14 14:38:13 +00:00
Matt Aitken 819b663ad7 Test page environment selection (#942)
* useLinkStatus hook

* Now you can customize the appearance of InlineCode

* Created <TaskFunctionName/>

* The test page now has an environment selector at the top
2024-03-14 13:54:47 +00:00
Eric Allam 478ce006cb v3: task run status and canceling runs (#941)
* WIP task run status, revamped resuming task dependencies

* Don’t select a span when toggling collapsing

* Cancel runs and attempts, in prod and dev
2024-03-13 15:51:43 +00:00
Eric Allam fe9435f63f Allow alternate config file names and skipping using a config file by passing —project-ref (#940) 2024-03-11 21:31:15 +00:00
Eric Allam d7cc2c9d6c v3: task payload refactor and configurable default retries (#939)
* Make the payload parameter the first argument to the task functions

* Allow configuring default retry behavior, and disabling in dev
2024-03-11 20:30:29 +00:00
Eric Allam 851b1769cb v3: use structured logging in a few places 2024-03-11 15:53:24 +00:00
Eric Allam e8381d5341 v3: Correctly handle tasks that have no output when resuming 2024-03-11 15:43:45 +00:00
nicktrn 20aa1cbe9b v3: checkpoint and restore with sim for local dev (#933)
* zod ipc connection with acks

* pass sender through to ipc handlers

* bits and bobs

* add host networking back in

* disable verbose logs

* restore after tasks and batches.. almost

* restore and resume all the things

* fix for systems without checkpoint support

* Get deployment indexing errors to work with the new ZodIpc stuff

---------

Co-authored-by: Eric Allam <eallam@icloud.com>
2024-03-11 15:20:33 +00:00
Eric Allam 883feab257 v3: various deploy improvements (#936)
* Cache the dependency resolution step of deploys

* Better deploy output and fixed an issue with deleting env vars
2024-03-11 14:31:55 +00:00
Eric Allam 83304f86f8 Capture uncaugh exceptions during indexing and fail deploys, with error output (#934) 2024-03-10 20:57:46 +00:00
Eric Allam 5d78bba5e3 Edit env var panel submit button Edit -> Save 2024-03-08 14:55:41 +00:00
Eric Allam e87a0cba1d v3: cli deploy improvements (#932)
* Add support for deploying to staging and prod

* Abort deployment if environment variables are missing

* Actually do typechecking

* Remove the build command and add a —self-hosted option to the deploy command
2024-03-08 14:52:46 +00:00
James Ritchie 8852d5b388 Callouts can handle inline children elements 2024-03-08 14:47:37 +00:00
James Ritchie 79ea8edc7c Table rows have a specified group class to fix conflicts with nested groups 2024-03-08 14:47:01 +00:00
James Ritchie 553370a9e5 secure clipboard fields show fixed number of “•••” 2024-03-08 14:45:53 +00:00
Eric Allam 47c18d9146 Fixed trigger.dev dev command 2024-03-08 11:16:56 +00:00
Eric Allam c385db63be v3: cli deploy command (#931)
* WIP proxy/deploy

* WIP, registry proxy in express working

* A couple of notes, preparing for indexing

* Move the changes to prod-worker into the new file

* Deploy command working with indexing and runs (docker provider only for now)

* Removed ts-expect-error directive

* Fixed build command
2024-03-08 10:49:42 +00:00
James Ritchie 86b2674e68 Updated login page (#928)
* WIP new login page

* Remix meta v2 implemented on the root page

* Added a spinner colour that works on our primary green

* Added new company logos

* Improvements to the login page

* Updated the meta function so the login page is responsive

* Fixed issue with the new logos not colouring correctly

* Login page now nicely responsive

* font size fix

* Spacing improvement

* Improved style of the relative time  dropdown

* Fixed viewport meta on login pages

* oranised imports

* improved the Sheet and feedback form

* Text area style update

* Feedback form tweak

* New project copy change: Project type -> Project version

* Runs filters use the correct variant

* Improved the select style

* Inputs now styled like Selects

* Removed login form from storybook

* Popover style tweak

* Popover looks like other filters

* Removed gap from filters and organized imports

* Cleanup

* Organized imports

* Made the inline code block smaller

* Fixed active badge label not being readable

* Fixed hero icon inside the button in the welcome page

* Fixed the filter button variants on the v2 runs page

* Fixed the select styles on the v3 runs page

* Improved the switch

* Fixed the filter styles on the events page

* Run page summary panels look good

---------

Co-authored-by: Matt Aitken <matt@mattaitken.com>
2024-03-07 10:35:52 +00:00
James Ritchie 161b2b3323 V3 style improvements (#926)
* Added more clipboard variants to storybook and improved styling

* Improvements to the clipboard components

* Code blocks match the design

* Improved the code block styles

* Dialog modal improvements

* Improved the free plan usage bar

* Better alignment of the shortcut icon in the button

* Side menu displays ‘v3’ next to the project header

* Page header improvements

* Project dropdown displays a badge if it’s a v3 project

* billing page now shows message

* Added the v3 badge to the project list page

* version 3 projects skip the select plan onboarding

* Added a new blank state to the runs page

* Conditionally set the blank state on the runs page

* Button padding tweak

* Updated the task and attempt icons

* Added a new blank state to the runs page

* Added more buttons variants to the button storybook

* Added new blank onboarding state for the tasks page

* Added a blank state to the test page

* Fix for bad svg attribute in TaskIcon

* Onboarding tweaks

* Add the projectRef to the init command

* Task icon is blue

* Added a v3 Project settings page

* Fix for typecheck error

* Added the new CodeBlocks with rowTitles to the span panel

---------

Co-authored-by: Matt Aitken <matt@mattaitken.com>
2024-03-06 18:06:20 +00:00
nicktrn 9b6f8f9238 v3: provider and coordinator updates (#927)
* add zod socket

* start using zod socket

* use zod socket for shared queue

* use shared provider shell

* update k8s provider task ops

* set otlp endpoint

* update docker actions and fix builds

* remove unused types

* update injected secret key env var name

* complete socket.io types to schemas migration

* update env example to new v3 key var

* fix coordinator build

* set task run label

* ensure attempt id is always set

* increase prod worker timeout

* set otlp endpoint for on runs

* pass otlp env var to prod worker

* auto-remove completed index jobs
2024-03-06 16:01:59 +00:00
Matt Aitken f50cee66d9 Upgraded to the latest Resend package 2024-03-06 15:18:56 +00:00
Matt Aitken 44d443a048 v3 live run reloading and minor style improvements (#925)
* Initial work creating the Redis publishing and subscribing

* Live view is working

* Show the live reloading status on the page

* Fixed some style issues on the run page with James

* Fix for invalid JSX attributes in the ExitIcon svg

* Some sexy shit

* The end line is now correctly coloured

* millisecondsToNanoseconds exported from core/v3 and imported correctly

* Fix for e2e tests, we changed the h1 to an h2

* Tidied imports

* Removed unused hook
2024-03-06 09:48:00 +00:00
Matt Aitken 2a1273b683 Major v3 web app update (#924)
* Added the new colours to the Tailwind config

* Fixed Tsailwind compile errors

* WIP Geist working in the app

* WIP Storybook

* Storybook is working again… ouch

* Use Geist mono for CodeMirror

* Some find/replace class changes

* Resizable colours improved

* WIP on primary button

* Minor style improvements

* Improve the side menu header styling

* Improved v2 breadcrumbs

* Account side menu tweaks

* The page now works with the NavBar and UpgradePrompt

* Removed unused import

* Refactored the buttons to be dry-er

* Removed the breadcrumbs

* WIP on nav bar

* WIP updating the button styles

* Fix for the old test page

* Fix for type errors

* Removed console.log

* Colour change in the resizable handle

* New loading bar divider

* Removed the design plugin

* Creating our own storybook pages

* Added some “storybook” pages

* Added DetailCell storybook

* WIP new animation for the loading page

* Added the new loading divider

* Fix for the time frame filter having a green border

* Style improvements to the new test page

* Small style improvements

* Improvements to the list pagination components

* Nav bar back button improved

* Improvements to the run page

* Lots of v2 style improvements

* Help looks better

* Improvements to the regenerate API key modal

* More general improvements

* Added section titles to our storybook

* Added form stories

* Fix for missing key prop

* Loading bar is working

* Fixed button submitting state

* Fix for the test button icon

* Code bg improved

* Improved oauth buttons

* Integration page header fix

* Various improvements, mainly changing button variants

* Segmented control style updates

* Added an outline of 3px for the segment control

* Fixes for the billing pages

* More pricing improvements

* Added dialog to storybook

* Typography now in storybook

* Free usage bar added to storybook

* Added inline clode to storybook

* Separated menu types into a sub section in storybook

* NamedIcon now in storybook

* Added page header to storybook

* Added pricing callout to storybook

* Added radio groups to storybook

* Added segmented controllers to storybook

* Added shortcuts to storybook

* Added switches to storybook

* Added toast to storybook

* Added tooltips to storybook

* Added tree view to storybook

* Added usage charts to storybook

* Tarted up the environment variables page

* Fix for the FreePlanUsage panel bg colours

* Added new badge variants

* Added the <TaskPath /> to the tasks table

* Span header improvements

* Started work on the timeline

* Started work on the timeline

* Fix for the run page header

* Timeline tick marks are working

* Timeline working

* Got the durations showing on hover

* Button improvements

* Experimenting with how the duration should be shown

* Lots of refinements to the timeline

* Much nicer API for the Timeline

* The switch is now purple to match the design, with inverted thumb when on

* Removed old stuff

* Export the props

* Added the span with duration back in

* Simplified more and fixed the span durations

* Put in some decent dummy data

* Rendering defaults

* Wip with the mouse cursor

* Improvements to the story

* Added Slider primitive

* Integrated the icons into the slider

* Lots of layout improvements

* Cleaned up imports

* “Root” indicator

* Animated background on partials

* Pull the styles through to the spans and log dots

* Fixed some bugs in the TreeView when nodes get created or destroyed

* Fix for incorrect parentTask reference

* Added run streaming endpoint for live refreshing

* Added the tabs to Storybook

* Fix for status font size mismatch

* Fix for the misaligned run filter status

* Set a minimum duration on the timeline

* The TreeView now re-calculates if the number of nodes has changed

* Partials now render to the end

* Improved the durations gutter

* The duration is calculated using the current time if there are partial events

* Added the environment label to storybook

* Improved the environment label style

* Removed bottom margin from badge

* Button style improvements

* Callouts style fits the new design better

* Got the scrollbars to appear on the tree/timeline

* Added scroll support

* Synced scrolling working

* Close button and exit icon for the detail panel

* secure field shows start of env

* Clipboard improvements

* Latest lockfile

* Fix for the durations wrapping

---------

Co-authored-by: James Ritchie <james@jamesritchie.co.uk>
2024-03-04 17:33:53 +00:00
Eric Allam 64401ee8ec TRIGGER_API_KEY -> TRIGGER_SECRET_KEY in v3 2024-03-04 15:47:58 +00:00
Eric Allam b7845685eb v3: no bundle builds for the dev CLI (#923)
* WIP no bundling

* Convert dev CLI to use an unbundled build process to support otel instrumentation

* Fix pnpm lock file

* A couple of fixes to get typechecking to work
2024-03-04 15:36:09 +00:00
nicktrn 52c9d485f8 v3: cli build command, prod runs, checkpoints (#919)
* fix trailing slash in api url

* add maybe platform down error

* shorten config path

* extend protobuf compiler install instructions

* build command and image model

* new image triggers task indexing

* index support for prod builds

* shorten example provider secret

* multi-stage task prod build

* lock prod tasks to node 18 image

* extract shared build and dev command libs

* pull out more shared deps

* add coordinator and providers

* fix core-apps build

* add dev builds for new apps

* fix cwd

* enable corepack

* some build fixes

* enable buildkit for old docker versions

* coordinator image fixes

* update provider containerfile

* build dev images in parallel

* upgrade pgadmin

* fix prod facade build

* prod runs

* fix merge

* don't knock out platform on invalid attempt id

* fix prod facade

* rename to build.ts

* fix prod builds

* set to executing after fetching payload

* make prod worker listen on random port if in use

* remove experimental warnings in dev

* prod resume

* prevent execution after completion

* exit prod worker after completion

* always restart otel collector

* docker checkpoints and prod runtime messaging

* don't retry indexing without chance of success

* make platform checkpoint aware

* deploy with existing hash sets latest worker

* log restore requests

* only try to checkpoint long waits

* tidying up

* lockfile

* fix build

* prod worker merge fixes

* fix prod complete and cancel

* fix lua nil checks

* socket namespace abstraction

* cleanup

* make all build args optional

* add build script

* don't require env vars for dev

* fix schema

* prod merge

* small fix

* bind correct logger

* fix v3 ref catalog entry

* resume prod batch

* pass socket to error and disconnect handlers

* fix non-batch resume

* fix batch resume

* send connection env vars when not in dev

* create worker via socket

* move api client back into v3 cli

* fix lockfile

* fix resume with failures

* marqs replace message

* typecheck prior to build

* don't define api url in prod builds

* support prod retries after resume

* skip typecheck option
2024-03-04 13:10:07 +00:00
Eric Allam ca47e6bdcd v3: cli cancel improvements and add back in deprecating background workers (#918)
* Show runs as cancelled when killing the dev CLI

* Fixed error when waiting for a task

* Deprecated workers will no longer be able to run unlocked task runs

* Fixed selecting the latest background worker version in test list

* Don’t propogate trace context when triggering an async task

Also add batch id to the context and the task events
2024-02-29 15:34:00 +00:00
Eric Allam 933cf0f119 v3: run.isTest now propagates through child tasks and the trace (#917)
* run.isTest now propagates through child tasks and the trace

* Try to fix the pr checks docker hub rate limit error
2024-02-28 17:01:31 +00:00
Eric Allam b6517af522 v3: Cancel dev runs when the dev CLI exits (#916)
* Cancel dev runs when the dev CLI exits

* Clear out in progress attempts after cancelling them
2024-02-28 16:00:39 +00:00
Matt Aitken 479445f741 V3 Test feature (#915)
* Added isTest to TaskRun

* TriggerTaskService now accepts isTest and adds it to TaskRun if set

* Set the cliVersion and sdkVersion when indexing

* Added a test page

* Radio buttons are now circles and accept a ReactNode

* The background of the code editor is now darker

* WIP on the test task page

* Improvements to the test page

* Testing is working

* Fix typo

* Show the real isTest value in the runs table

* Added a key to the outlet so it correctly resets all the state on the sub-route
2024-02-28 15:52:24 +00:00
Eric Allam 1fabcf68fa Override local env vars with env vars in the server (#914) 2024-02-27 15:44:55 +00:00
Matt Aitken d7c79ee651 V3 API keys page and Environment Variables (#907)
* New API keys page working

* Added EnvironmentVariable and EnvironmentVariableValue tables

* Environment variables presenter and repository started

* Icon now accepts a ReactNode as well

* The create form is working

* Improvements to validation

* Getting all the environment variables is working

* Editing an env var working

* Fix for deleting a value from an env var

* Separate route for adding new env vars. Works with “Save and add another”

* Editing is working properly, and revealing all values

* Deleting env vars

* Get environment variables with the environment id

* Added th env vars count to the Environment variables page

* Removed the commented out old Env & API keys side menu item

* Fix imports in ClipboardField

* Added a storybook page for all the variants of input fields

* Upgraded Tailwind for the :has selector

* Fix for the edit button not filling the width

* Reworked the input fields so they work with a variable width icon on the left

* Improvements to the new env var modal

* Improve the style of the edit environment variable panel

* Fix for when Input is type=“text”

* Icon now works with FunctionComponents and ComponentTypes properly

* Removed a console log

* Removed unused imports

* Use a const schema for SecretValue

* Added friendlyId to EnvironmentVariable
2024-02-27 14:35:43 +00:00
Eric Allam 8d6e710dc2 V3: retry utilities (#913)
* WIP

* retry.fetch w/intercepting to test

* Fixed the incorrect renaming of limit to maxAttempts in v2

* Fixed source map support in dev

* Cleanup background worker files once they close
2024-02-27 13:54:51 +00:00
Eric Allam 90a302f9f7 v3: batch trigger (#908)
* Support for batch triggers

* Various trace fixes and improvements

* Moved trace into the logger, fixed span cardinality issues with waits and triggers

* Add support for attaching a debugger in dev with —debugger flag
2024-02-26 11:21:06 +00:00
Matt Aitken 07efef405d v3 span details (#906)
* WIP on saving the resized state in a cookie

* Progress on remembering the panel width

* The resizable panel now remembers even when closed

* Improvements to the span panel

* Hide integrations in the v3 side menu

* Added “UTC” to the times

* WIP changing how styles work

* Reference unflattenAttributes from the package

* Unflatten now correctly unflattens arrays

* Unflatten typecheck fix

* Improvements to the spans and UI for them

* Added the “events” to a span, including special styling for errors

* Unflatten doesn’t work on an array of Attributes
2024-02-23 09:54:01 +00:00
Eric Allam dd63fe6e9f v3: Implemented in-process task retries in dev w/cache support (#905)
* Implemented in-process task retries in dev w/cache support

* Improve the stack trace correction
2024-02-22 10:12:23 +00:00
Matt Aitken d8c645a3f0 Fix for projectVersion input name change 2024-02-22 09:18:52 +00:00
Eric Allam 24600d2c01 Fixed unFlattenAttributes import 2024-02-21 20:15:06 +00:00
Matt Aitken 584936fa29 Early v3 project UI (#904)
* Added ProjectVersion to projects, defaults to V2

* Moved v3Enabled to the existing feature flags

* Update to using the v3Enabled feature flag

* Allow people to choose v3 when creating a new project

* Added version to the ProjectPresenter

* v2 project and v3 project redirecting

* .env.example for the v3-catalog

* Added sdkVersion and cliVersion columns to the BackgroundWorker table

* Added additional classes for environments

* First draft of the Tasks table

* Link to the task page

* V3 side menu project items

* Moved the ListPagination component into the components folder

* Moved the TaskListPresenter to a v3 folder

* Bare bones task page with tabs

* Task page, we’re going to delete this though

* Bare bones runs table working

* Rejigged the columns

* The run table status

* Environment and status filtering working

* Added time filters

* Cursor and direction is working… I think it’s tricky to know for sure

* Added TaskRun numbers

* Selecting a task now links to the runs page with filters turned on

* Added support for the enqueued status

* Reworked the task table

* Link to the task and environment runs

* The run page is rendering a tree of the events

* Change the task table column to “Created at"

* WIP on making the run tree view look good

* Improvements to the run tree

* Removed the janky scroll bar flash

* Change the title of the task logs to just the task ID

* Live timer when a span is running

* Added “Show parent items” link

* Fixed the bug jumping to parent items

* Style improvements to the run

* Added the resizable handle to the run page

* Moved formatDuration to core/v3

* Wait for now has nice log messages

* The run detail panel is working with just a title for now

* Navigating to a span and persisting between page reloads is working

* The run detail timeline

* Wrap log dates in a paragraph

* Latest span detail view

* Scrolling in the right-hand panel

* Tweak default resizable layout for the run page

* Attempted improvements to stop a recursive issue on TreeView

* TreeView useReducer WIP

* Changed the change callback

* Add changes to the state

* Use the new onSelectedIdChanged in the app

* Don’t update the state from the outside

* Filtering fix, although navigation doesn’t work nicely

* Filtering working when changing the content and clearing it

* Don’t allow the same selectedId callback to be called twice…

* useDebounce hook

* Switch from using defer to just typedjson for now

* Latest attempt at span navigation

* Removed log

* Turn off filtering for now, it’s causing the Links to break somehow

* Fix for page height issue

* Added (Developer Preview) to the v3 project select box

* Revert "Filtering working when changing the content and clearing it"

This reverts commit 20d9fbe36ded619e9d3eb0b23d4ed0568a88a615.

# Conflicts:
#	apps/webapp/app/components/primitives/TreeView/TreeView.tsx

* Switch to using the old filtering

* Added useThrottle hook

* Instantly close the panel when deselecting a node, use debounce when navigating to a span

* Better spacing in the right hand panel

* Comment out the v3 side menu pages that don’t exist yet

* Fix for typecheck fail

* Clear the statuses too
2024-02-21 18:42:12 +00:00
Eric Allam a54bdb23bf Fixed the Docker build (error in generate-protos.mjs) 2024-02-20 13:31:16 +00:00
Eric Allam 07f7c65054 v3 queue system (#903)
* Introducing Modular Asynchronous Reliable Queueing System (MarQS). Works in dev

* Convert MarQS to using lua and dealing with concurrency

* Simplified the timeout queue and current concurrency is now a set instead of a flat value (to support idempotency)

* Implement task heartbeating and reconnect the background workers CLI when the websocket connection reconnects

* Start adding internal telemetry support for the server

* Get env vars to work in dev and implement prisma tracing in webapp

* Cleanup telemetry and implement it in the consumer

* Implement dequeuing a message from a parent shared queue

* Implement a custom logger exporter instead of using console log exporter

* Use node instead of shell for generating protocol buffer code

* Propogate trace context into debug logs, and allow turning off logger exporter through env vars

* Switch to using baselime for internal otel data

* Make orgMember optional to fix type issues

* Provide the CLI dev env vars through the CLI, don’t build dotenv into facade

* Removed the logger import

* Address Matt’s comments

* Addressing more of Matt’s comments

* Handle sending an execution after a websocket connection closes

* Remove auth from the env attributes to prevent obfuscation
2024-02-20 12:16:41 +00:00
Matt Aitken d04c8e89f6 Added a “repo-token” to the Install Protoc GitHub action steps
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 0s
🚀 Publish Trigger.dev Docker / units (push) Failing after 0s
🚀 Publish Trigger.dev Docker / e2e (push) Failing after 19s
🚀 Publish Trigger.dev Docker / publish (push) Has been skipped
2024-02-19 14:15:15 +00:00
Matt Aitken b69902dd84 Deduplicate integrations in the JobListPresenter (#901)
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 6s
🚀 Publish Trigger.dev Docker / e2e (push) Failing after 1s
🚀 Publish Trigger.dev Docker / units (push) Failing after 19s
🚀 Publish Trigger.dev Docker / publish (push) Has been skipped
2024-02-19 13:13:28 +00:00
Eric Allam 2aabb994cf Install the protobuf compiler in the docker builder stage 2024-02-12 16:58:57 +00:00
Matt Aitken 310d51bac0 Tree view component (#900)
* WIP creating a TreeView component

* Very simple tree displaying

* WIP on TreeView types

* The state is computing properly

* Improved the state and types

* Virtualized rendering

* Added state inside the useTreeState hook

* Selection working

* Deselecting nodes

* Toggle selection

* Expanding/collapsing nodes

* Get notifications when changes are made

* Only try show the visible items

* Experiment to try getting cell animations working

* Renamed hook to useTree

* Keyboard navigation is working

* Proper focus support

* Nice selection of the parent by pressing left

* End selects the last visible node

* Showing how the TreeView can accept a selected id and expanded ids from the outside

* The useTree hook now uses selectedId and collapsedIds to make it easier to use

* Check the inputs are valid before applying them, ignore if they’re not

* Don’t allow a selected node to be hidden by collapsed parents

* Nicer API for the onChange callback

* Supports animated collapsing of the tree view

* Nice smooth scrolling

* Removed logs

* Removed unused key

* Remove intricate estimation

* StandardTreeView has lots of good defaults baked in

* Allow passing of parent class name in

* Work on scrolling to the node… needs a bit of a reshuffle

* Just make the TreeView better rather than adding StandardTreeView

* Renamed rowVirtualizer to virtualizer

* Move the virrtualizer into the hook

* Scrolling to selection is working

* Added filtering
2024-02-12 16:43:47 +00:00
Eric Allam 6be1099a12 Don’t git submodule in docker 2024-02-12 14:53:03 +00:00
Eric Allam baf3a84cda v3 dev cli command + more (#894) 2024-02-12 14:24:32 +00:00
Matt Aitken 70f9bd0d70 Latest lockfile 2024-02-12 13:11:14 +00:00
github-actions[bot] a1860dbaae chore: Update version for release (#899)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-12 13:00:14 +00:00
Matt Aitken d69e4e712d OpenAI integration: retry 400 status code because OpenAI returns this sometimes for no reason (#898) 2024-02-12 12:48:48 +00:00
Matt Aitken 7fae67c47d Fix/autoyield canceled run (#890)
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 3s
🚀 Publish Trigger.dev Docker / units (push) Failing after 16s
🚀 Publish Trigger.dev Docker / e2e (push) Failing after 20s
🚀 Publish Trigger.dev Docker / publish (push) Has been skipped
* Todo for the fix

* If the run has been canceled during execution, don’t do any further processing of the run
2024-02-09 11:06:19 +00:00
Matt Aitken 9ae0ca64af Fix edge cases with deleting last org/project (#892)
* If you have an org with no projects, it displays in the project dropdown with a “New project” button

* When creating a new org disable the button whilst it’s doing the request

* If an org already had any deleted projects it couldn’t be deleted…

* When selecting the best project, factor in deleted ones

* Don’t show the cancel button when creating a new org if there are no non-deleted projects

* If a project has already been deleted just return
2024-02-09 11:05:28 +00:00
Matt Aitken da69fa0613 Fixes #886. Removed “public” schema from the JobListPresenter query
🚀 Publish Trigger.dev Docker / units (push) Failing after 4s
🚀 Publish Trigger.dev Docker / e2e (push) Failing after 4s
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 20s
🚀 Publish Trigger.dev Docker / publish (push) Has been skipped
2024-02-07 16:48:18 +00:00
Matt Aitken 336029b842 Organization renaming, deleting and one-click email unsubscribe (#885)
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 0s
🚀 Publish Trigger.dev Docker / e2e (push) Failing after 4s
🚀 Publish Trigger.dev Docker / units (push) Failing after 10s
🚀 Publish Trigger.dev Docker / publish (push) Has been skipped
* Added the org settings page to the sidebar

* Added loading states when renaming/deleting projects

* Don’t show deleted orgs in the app

* The actual db migration file

* The Org settings page with the actions working

* Don’t remove org members, just leave them

* Don’t show invites from orgs that are deleted

* Allow disabling IntegrationConnections

* Don’t refresh IntegrationConnections that are disabled

* Set all the integrations as disabled

* Updated the unsubscribe checkbox text

* Unsubscribe route

* Use the magic link secret, not the encryption key. Also make the error message more vague

* Only members of the org or project can rename them

* Don’t throw an error if the connection can’t be refreshed, return undefined instead
2024-02-07 10:52:50 +00:00
Matt Aitken 29edcd3df9 Rename and delete projects (#880)
🚀 Publish Trigger.dev Docker / units (push) Failing after 0s
🚀 Publish Trigger.dev Docker / e2e (push) Failing after 0s
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 4s
🚀 Publish Trigger.dev Docker / publish (push) Has been skipped
* Side menu: selector title changed to project. Changed section headers and made it Slack + Discord

* Added project settings page. Renaming is working

* Added project.deletedAt column

* Deleting projects WIP

* Allow clearing the project from the session

* Don’t load projects that are deleted

* Improved the project selection logic

* Clear the session project id when deleting a project

* Deleting the last project in an org now works. The new project page doesn’t have the sidebar anymore.

* Removed false code comment

* Added the JobRun index back in, using Prisma. Otherwise it tries to remove it on each migration

* Only return environments where the project isn’t deleted. This will block API calls

* Do the project deletedAt check in code, not SQL

* Made it clearer what the code does with a comment and applied the same logic to the public api key
2024-02-05 13:14:33 +00:00
Matt Aitken a9ff32418e Fix for when there are no jobs…
🚀 Publish Trigger.dev Docker / e2e (push) Failing after 0s
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 4s
🚀 Publish Trigger.dev Docker / units (push) Failing after 4s
🚀 Publish Trigger.dev Docker / publish (push) Has been skipped
2024-01-30 17:35:04 +00:00
Matt Aitken 7358fcb891 Latest lockfile 2024-01-30 17:07:24 +00:00
Matt Aitken 28052daad9 Faster jobs page (#879)
* Optional next/previous with the list pagination

* Defer the loading of the runs table

* Only return the latest run in a separate query, then do a join in code

* Added a composite index to speed up getting the latest run from a job id
2024-01-30 17:02:04 +00:00
github-actions[bot] 364c8c5f7f chore: Update version for release (#876)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-01-30 13:36:36 +00:00
Matt Aitken 4b3b418abb Set endpoint URLs to null, instead of deleting them (#878)
🚀 Publish Trigger.dev Docker / units (push) Failing after 4s
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 5s
🚀 Publish Trigger.dev Docker / e2e (push) Failing after 4s
🚀 Publish Trigger.dev Docker / publish (push) Has been skipped
* Added Endpoint deletedAt column

* Only show endpoints where they’re not deleted

* Don’t delete Endpoints, set the deletedAt and change their slug name

* Only perform indexing if the endpoint isn’t deleted

* Have a nullable URL for endpoints

* Deal with null URLs throughout the app

* Re-running and retrying behaves properly when there’s no endpoint URL

* Remove console.log

* Better error message when doing a run
2024-01-30 10:45:12 +00:00
Matt Aitken 2e354d342c Deleting endpoint working (#875) 2024-01-29 11:57:25 +00:00
Matt Aitken dd879c8e4a Fix api run statuses (#874)
* Added a subtask for testing

* Make it easier to run the CLI from the nextjs reference project

* Copies of the run and statuses endpoints, but without simplifying the run statuses

* Use the new v2 endpoints that give the full run statuses

* Removed unused import

* v2 events endpoint with the full run status info

* Changeset
2024-01-29 11:42:19 +00:00
Matt Aitken af485b9180 Fix custom oauth client (#872)
* A checkbox can now be readonly and not be checkable, with correct styling

* Use readOnly for the hasCustomClient checkbox, not disabled

* Better styling of the disabled state

* The update oauth form too
2024-01-26 17:25:15 +00:00
Matt Aitken a739ebaa88 Tweaked the layout of the usage runs charts so the daily runs are in the box with other run charts 2024-01-26 15:05:52 +00:00
Kritik Jiyaviya 3ebc2578e0 feat: Add daily runs graph (#865)
* feat: Add daily runs graph

* Merge branch 'main' into feat/daily-runs-graph

* update DayRunsChart

---------

Co-authored-by: Matt Aitken <matt@mattaitken.com>
2024-01-26 15:00:30 +00:00
Kritik Jiyaviya 0b657b33f9 Allow retrying a job run with an invalid payload (#869) 2024-01-26 12:39:23 +00:00
Erin Allison 7ac942dc0e Address unintended type coercion in PageInfoPropertyComponent (#861)
Fixes triggerdotdev/trigger.dev#858

Signed-off-by: Erin Allison <erin@eallison.us>
2024-01-26 09:44:55 +00:00
Matt Aitken 9b12016428 Improved SQL reads for some dashboard pages (#868)
* Pass subscription status into the usage bar

* Page navigation spinner is now blue (was a bit subtle before)

* Better logging of db queries, this will be commented out before the PR is merged

* Select only the required fields

* We don’t need the member count for each org

* WIP redirecting with projectId in session

* Switching projects is now working, without duplicating the project query

* Removed logs in revalidate function

* Removes some unused imports

* ProjectPresenter: removed lots of unused db selects

* Root use defaultShouldRevalidate, not just true

* Simplified the job list query and separated the deleting job modal query

* Use requireUserId instead of requireUser wherever possible

* EventListPresenter query simplified

* Simplified the RunListPresenter query

* Disable query logging

* Use the latest updated version for the job list table

* We need to use the org presenter on the select plan page
2024-01-26 09:41:11 +00:00
Matt Aitken d6b44de4ba Defer the loading of the usage data (#866)
🚀 Publish Trigger.dev Docker / units (push) Failing after 10s
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 20s
🚀 Publish Trigger.dev Docker / e2e (push) Failing after 19s
🚀 Publish Trigger.dev Docker / publish (push) Has been skipped
* Updated to the latest remix-typedjson package

* Throw an error if the org isn’t found

* Use defer. Doesn’t work because Date isn’t deserialized correctly

* Use regular defer, typeddefer isn’t working properly…

* Don’t update remix-typedjson, wasn’t needed

* Organize imports
2024-01-22 10:26:33 +00:00
Matt Aitken 17df4839d7 Latest lockfile
🚀 Publish Trigger.dev Docker / typecheck (push) Failing after 5s
🚀 Publish Trigger.dev Docker / e2e (push) Failing after 5s
🚀 Publish Trigger.dev Docker / units (push) Failing after 21s
🚀 Publish Trigger.dev Docker / publish (push) Has been skipped
2024-01-21 17:30:21 +00:00
github-actions[bot] e3f78178f7 chore: Update version for release (#864)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-01-21 17:29:23 +00:00
Kritik Jiyaviya da90ee13c3 fix: Resend integration example comment (#860) 2024-01-21 17:16:26 +00:00
Matt Aitken 583da458ec Changed the minimum interval time period from 60s to 20s (with changeset) 2024-01-21 17:06:09 +00:00
Matt Aitken dcf95c4eb2 Revert "Changeset: Changed the minimum interval time from 60s to 20s"
This reverts commit c272e38de2.
2024-01-21 17:04:11 +00:00
Matt Aitken 32cf5790cb Revert "Changed the minimum interval time from 60s to 20s"
This reverts commit ca78ddc2c2.
2024-01-21 17:04:11 +00:00
Matt Aitken c272e38de2 Changeset: Changed the minimum interval time from 60s to 20s 2024-01-21 16:29:54 +00:00
Matt Aitken ca78ddc2c2 Changed the minimum interval time from 60s to 20s 2024-01-21 16:28:28 +00:00
Matt Aitken 07ed8c346a Fix for enabled env var in docs 2024-01-20 14:01:56 +00:00
Matt Aitken d4391f2e2d Fixed link in io.logger to the run log 2024-01-20 12:54:11 +00:00
1060 changed files with 77997 additions and 19004 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
better handle task metadata parse errors, and display nicely formatted errors
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Vastly improved dev command output
+8
View File
@@ -0,0 +1,8 @@
---
"@trigger.dev/sdk": patch
"@trigger.dev/core-apps": patch
"trigger.dev": patch
"@trigger.dev/core": patch
---
add machine config and secure zod connection
+6 -2
View File
@@ -4,7 +4,8 @@
"commit": false,
"fixed": [
[
"@trigger.dev/*"
"@trigger.dev/*",
"trigger.dev"
]
],
"linked": [],
@@ -16,7 +17,10 @@
"emails",
"proxy",
"yalt",
"@trigger.dev/database"
"@trigger.dev/database",
"coordinator",
"docker-provider",
"kubernetes-provider"
],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
+7
View File
@@ -0,0 +1,7 @@
---
"@trigger.dev/sdk": patch
"trigger.dev": patch
"@trigger.dev/core": patch
---
Configurable log levels in the config file and via env var
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Dont swallow some error messages when deploying
+6
View File
@@ -0,0 +1,6 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Add option to print console logs in the dev CLI locally (issue #1014)
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/core": patch
---
Fixed batch otel flushing
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Fix permissions inside node_modules
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/react": patch
---
Fix for shared queryKey between useRunDetails and useRunStatuses
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/core": patch
---
Retry 429, 500, and connection error API requests to the trigger.dev server
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/sdk": patch
---
Export queue from the SDK
+7
View File
@@ -0,0 +1,7 @@
---
"@trigger.dev/core-apps": patch
"trigger.dev": patch
"@trigger.dev/core": patch
---
Display errors for runs and deployments
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
v3: fix digest extraction
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/nestjs": patch
---
fix: [nestjs integration] fastify HTTP adapter detection now works correctly for response headers
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Changed "Worker" to "Version" in the dev command key
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Correctly handle self-hosted deploy command errors
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Default to retrying enabled in dev when running init
+6
View File
@@ -0,0 +1,6 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
Handle string and non-stringifiable outputs like functions
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Improve error messages during dev/deploy and handle deploy image build issues
+6
View File
@@ -0,0 +1,6 @@
---
"@trigger.dev/sdk": patch
"@trigger.dev/core": patch
---
Added replayRun function to the SDK
+6
View File
@@ -0,0 +1,6 @@
---
"@trigger.dev/sdk": patch
"@trigger.dev/core": patch
---
Improve the SDK function types and expose a new APIError instead of the APIResult type
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Added a Node.js runtime check for the CLI
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Fix post start hooks
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Use the dashboard url instead of the API url for the View logs link
+92
View File
@@ -0,0 +1,92 @@
{
"mode": "pre",
"tag": "beta",
"initialVersions": {
"coordinator": "0.0.1",
"docker-provider": "0.0.1",
"kubernetes-provider": "0.0.1",
"proxy": "0.0.11",
"webapp": "1.0.0",
"yalt": "0.0.1",
"@trigger.dev/airtable": "2.3.18",
"@trigger.dev/github": "2.3.18",
"@trigger.dev/linear": "2.3.18",
"@trigger.dev/openai": "2.3.18",
"@trigger.dev/plain": "2.3.18",
"@trigger.dev/replicate": "2.3.18",
"@trigger.dev/resend": "2.3.18",
"@trigger.dev/sendgrid": "2.3.18",
"@trigger.dev/shopify": "2.3.18",
"@trigger.dev/slack": "2.3.18",
"@trigger.dev/stripe": "2.3.18",
"@trigger.dev/supabase": "2.3.18",
"@trigger.dev/typeform": "2.3.18",
"@trigger.dev/astro": "2.3.18",
"@trigger.dev/cli": "2.3.18",
"trigger.dev": "2.3.18",
"@trigger.dev/core": "2.3.18",
"@trigger.dev/core-apps": "0.0.0",
"@trigger.dev/core-backend": "2.3.18",
"@trigger.dev/database": "0.0.1",
"emails": "1.0.0",
"@trigger.dev/eslint-plugin": "2.3.18",
"@trigger.dev/express": "2.3.18",
"@trigger.dev/hono": "2.3.18",
"@trigger.dev/integration-kit": "2.3.18",
"@trigger.dev/nestjs": "2.3.18",
"@trigger.dev/nextjs": "2.3.18",
"@trigger.dev/otlp-importer": "2.3.11",
"@trigger.dev/react": "2.3.18",
"@trigger.dev/remix": "2.3.18",
"@trigger.dev/sveltekit": "2.3.18",
"@trigger.dev/testing": "2.3.18",
"@trigger.dev/sdk": "2.3.18",
"@trigger.dev/yalt": "2.3.18"
},
"changesets": [
"breezy-gorillas-mate",
"chilled-hornets-move",
"clean-pianos-listen",
"cool-glasses-bake",
"cuddly-feet-approve",
"dry-walls-check",
"eight-pumas-float",
"few-students-share",
"green-bags-wink",
"khaki-apricots-design",
"khaki-poems-lay",
"late-icons-lie",
"late-steaks-behave",
"lemon-jobs-repair",
"light-bulldogs-press",
"light-dragons-complain",
"loud-actors-remember",
"many-ligers-pump",
"mighty-camels-joke",
"new-rivers-tell",
"ninety-pets-travel",
"odd-poets-own",
"polite-ducks-switch",
"poor-flowers-cross",
"rare-roses-float",
"real-planets-stare",
"rotten-dryers-exercise",
"sharp-zebras-serve",
"silly-suits-switch",
"smart-needles-move",
"smart-olives-eat",
"spicy-lamps-smoke",
"strange-ghosts-matter",
"strong-lemons-add",
"stupid-bulldogs-applaud",
"sweet-lizards-press",
"swift-dragons-peel",
"tall-bees-wave",
"tame-guests-know",
"tender-oranges-rhyme",
"tidy-balloons-suffer",
"tiny-doors-type",
"tiny-elephants-scream",
"tricky-bulldogs-heal"
]
}
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Add openssl to prod worker image and allow passing auth token via env var for deploy
+7
View File
@@ -0,0 +1,7 @@
---
"@trigger.dev/sdk": patch
"trigger.dev": patch
"@trigger.dev/core": patch
---
Fixed incorrect span timings around checkpoints by implementing a precise wall clock that resets after restores
+7
View File
@@ -0,0 +1,7 @@
---
"@trigger.dev/sdk": patch
"trigger.dev": patch
"@trigger.dev/core": patch
---
Adding task with a triggerSource of schedule
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Update trigger.dev CLI for new batch otel support
+6
View File
@@ -0,0 +1,6 @@
---
"@trigger.dev/otlp-importer": patch
"trigger.dev": patch
---
Fix package builds and CLI commands on Windows
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/sdk": patch
---
Remove unimplemented batchOptions
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Fix CLI logout and add list-profiles command
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Fixing an issue with bundling @trigger.dev/core/v3 in dev when using pnpm
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Added DEBUG to the ignored env vars
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/core": patch
---
Make optional schedule object fields nullish
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Fixed an issue where the trigger.dev package was not being built before publishing to npm
+8
View File
@@ -0,0 +1,8 @@
---
"trigger.dev": major
"@trigger.dev/core": major
"@trigger.dev/otlp-importer": major
"@trigger.dev/sdk": major
---
Updates to support Trigger.dev v3
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/core": patch
---
Added JSDocs to the schedule SDK types
+7
View File
@@ -0,0 +1,7 @@
---
"@trigger.dev/sdk": patch
"trigger.dev": patch
"@trigger.dev/core": patch
---
Dynamically import superjson and fix some bundling issues
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Changed the binary name from trigger.dev to triggerdev to fix a Windows issue
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Stop swallowing deployment errors and display them better
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Init command was failing on Windows because of bad template paths
+6
View File
@@ -0,0 +1,6 @@
---
"@trigger.dev/sdk": patch
"@trigger.dev/core": patch
---
Added cancelRun to the SDK
+7
View File
@@ -0,0 +1,7 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---
- Add graceful exit for prod workers
- Prevent overflow in long waits
+7
View File
@@ -0,0 +1,7 @@
---
"@trigger.dev/sdk": patch
"trigger.dev": patch
"@trigger.dev/core": patch
---
Added a new global - Task Catalog - to better handle task metadata
+31 -1
View File
@@ -31,6 +31,9 @@ NODE_ENV=development
# FROM_EMAIL=
# REPLY_TO_EMAIL=
# Remove the following line to enable logging telemetry traces to the console
LOG_TELEMETRY="false"
# CLOUD VARIABLES
POSTHOG_PROJECT_KEY=
PLAIN_API_KEY=
@@ -42,4 +45,31 @@ CLOUD_LINEAR_CLIENT_ID=
CLOUD_LINEAR_CLIENT_SECRET=
CLOUD_SLACK_APP_HOST=
CLOUD_SLACK_CLIENT_ID=
CLOUD_SLACK_CLIENT_SECRET=
CLOUD_SLACK_CLIENT_SECRET=
# v3 variables
PROVIDER_SECRET=provider-secret # generate the actual secret with `openssl rand -hex 32`
COORDINATOR_SECRET=coordinator-secret # generate the actual secret with `openssl rand -hex 32`
# Uncomment the following line to enable the registry proxy
# ENABLE_REGISTRY_PROXY=true
# DEPOT_TOKEN=<Depot org token>
# DEPOT_PROJECT_ID=<Depot project id>
# DEPLOY_REGISTRY_HOST=${APP_ORIGIN} # This is the host that the deploy CLI will use to push images to the registry
# CONTAINER_REGISTRY_ORIGIN=<Container registry origin e.g. https://registry.digitalocean.com>
# CONTAINER_REGISTRY_USERNAME=<Container registry username e.g. Digital ocean email address>
# CONTAINER_REGISTRY_PASSWORD=<Container registry password e.g. Digital ocean PAT>
# DEV_OTEL_EXPORTER_OTLP_ENDPOINT="http://0.0.0.0:4318"
# These are needed for the object store (for handling large payloads/outputs)
# OBJECT_STORE_BASE_URL="https://{bucket}.{accountId}.r2.cloudflarestorage.com"
# OBJECT_STORE_ACCESS_KEY_ID=
# OBJECT_STORE_SECRET_ACCESS_KEY=
# RUNTIME_WAIT_THRESHOLD_IN_MS=10000
# These control the server-side internal telemetry
# INTERNAL_OTEL_TRACE_EXPORTER_URL=<URL to send traces to>
# INTERNAL_OTEL_TRACE_EXPORTER_AUTH_HEADER_NAME=<Header name for the auth token>
# INTERNAL_OTEL_TRACE_EXPORTER_AUTH_HEADER_VALUE=<Auth token value>
# INTERNAL_OTEL_TRACE_LOGGING_ENABLED=1
# INTERNAL_OTEL_TRACE_SAMPING_RATE=20 # this means 1/20 traces or 5% of traces will be sampled (sampled = recorded)
# INTERNAL_OTEL_TRACE_INSTRUMENT_PRISMA_ENABLED=0,
@@ -0,0 +1,21 @@
name: OpenTelemetry Auto-Instrumentation Request
description: Suggest an SDK that you'd like to be auto-instrumented in the Run log view
title: "auto-instrumentation: "
labels: ["🌟 enhancement"]
body:
- type: textarea
attributes:
label: What API or SDK would you to have automatic spans for?
description: A clear description of which API or SDK you'd like, and links to it.
validations:
required: true
- type: textarea
attributes:
label: Is there an existing OpenTelemetry auto-instrumentation package?
description: You can search for existing ones https://opentelemetry.io/ecosystem/registry/?component=instrumentation&language=js
validations:
required: true
- type: textarea
attributes:
label: Additional information
description: Add any other information related to the feature here. If your feature request is related to any issues or discussions, link them here.
+14 -6
View File
@@ -4,31 +4,36 @@ on:
jobs:
e2e:
name: "🧪 E2E Tests"
runs-on: buildjet-4vcpu-ubuntu-2204
runs-on: buildjet-16vcpu-ubuntu-2204
steps:
- name: 🐳 Login to Docker Hub
if: github.event_name == 'push'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
username: ${{ secrets.DOCKERHUB_USERNAME || vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN || vars.DOCKERHUB_TOKEN }}
- name: ⬇️ Checkout repo
uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v2.2.4
with:
version: 7.18
version: 8.15.5
- name: ⎔ Setup node
uses: buildjet/setup-node@v3
with:
node-version: 18
node-version: 20.11.1
cache: "pnpm"
- name: Install Protoc
uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: 📥 Download deps
run: pnpm install --frozen-lockfile
@@ -43,7 +48,10 @@ jobs:
# Build packages
pnpm run build --filter @references/nextjs-test^...
cd apps/webapp && pnpm run build:server
cd ../..
pnpm --filter @trigger.dev/database generate
pnpm --filter @trigger.dev/otlp-importer generate
# Move trigger-cli bin to correct place
pnpm install --frozen-lockfile
+14 -39
View File
@@ -3,23 +3,18 @@ on:
workflow_call:
jobs:
publish:
strategy:
fail-fast: true # when a job fails, all remaining ones will be cancelled
matrix:
runs-on: [buildjet-4vcpu-ubuntu-2204, buildjet-4vcpu-ubuntu-2204-arm]
name: ${{matrix.runs-on}}
runs-on: ${{matrix.runs-on}}
runs-on: ubuntu-latest
outputs:
version: ${{ steps.get_version.outputs.version }}
short_sha: ${{ steps.get_commit.outputs.sha_short }}
steps:
- name: 🐳 Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Setup Depot CLI
uses: depot/setup-action@v1
- name: ⬇️ Checkout repo
uses: actions/checkout@v3
with:
submodules: recursive
- name: 🆚 Get the version
id: get_version
@@ -41,19 +36,12 @@ jobs:
echo "Invalid reference: ${GITHUB_REF}"
exit 1
fi
if [[ ${{matrix.runs-on}} == *-arm ]]; then
IMAGE_TAG="${IMAGE_TAG}-arm"
fi
echo "::set-output name=version::${IMAGE_TAG}"
- name: 🔢 Get the commit hash
id: get_commit
run: |
echo ::set-output name=sha_short::$(echo ${{ github.sha }} | cut -c1-7)
- name: 🐳 Build Docker Image
run: |
docker build -t release_build_image -f ./docker/Dockerfile .
- name: 🐙 Login to GitHub Container Registry
uses: docker/login-action@v2
with:
@@ -61,24 +49,11 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: 🐙 Push to GitHub Container Registry
run: |
docker tag release_build_image $REGISTRY/$REPOSITORY:$IMAGE_TAG
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
env:
REGISTRY: ghcr.io/triggerdotdev
REPOSITORY: trigger.dev
IMAGE_TAG: ${{ steps.get_version.outputs.version }}
- name: 🐙 Push 'latest' to GitHub Container Registry
if: startsWith(github.ref, 'refs/tags/v.docker')
run: |
LATEST=latest
if [[ ${{matrix.runs-on}} == *-arm ]]; then
LATEST="${LATEST}-arm"
fi
docker tag release_build_image $REGISTRY/$REPOSITORY:$LATEST
docker push $REGISTRY/$REPOSITORY:$LATEST
env:
REGISTRY: ghcr.io/triggerdotdev
REPOSITORY: trigger.dev
- name: 🐳 Build image and push to GitHub Container Registry
uses: depot/build-push-action@v1
with:
file: ./docker/Dockerfile
platforms: linux/amd64,linux/arm64
tags: |
ghcr.io/triggerdotdev/trigger.dev:${{ steps.get_version.outputs.version }}
push: true
+94
View File
@@ -0,0 +1,94 @@
name: "🚢 Publish Infra Images"
on:
push:
tags:
- "infra-dev-*"
- "infra-test-*"
- "infra-prod-*"
paths:
- ".github/workflows/publish.yml"
- "packages/**"
- "!packages/**/*.md"
- "!packages/**/*.eslintrc"
- "apps/**"
- "!apps/**/*.md"
- "!apps/**/*.eslintrc"
- "integrations/**"
- "!integrations/**/*.md"
- "!integrations/**/*.eslintrc"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "turbo.json"
- "docker/Dockerfile"
- "docker/scripts/**"
- "tests/**"
permissions:
id-token: write
packages: write
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
env:
AWS_REGION: us-east-1
jobs:
build:
strategy:
matrix:
package: [coordinator, kubernetes-provider]
runs-on: buildjet-16vcpu-ubuntu-2204
env:
DOCKER_BUILDKIT: "1"
steps:
- uses: actions/checkout@v4
- name: Generate image reference
id: prep
# WARNING: This step expects the workflow to have been triggered by a specific tag format of: infra-${env}-*
run: |
env=$(echo ${{ github.ref_name }} | cut -d- -f2)
sha=${GITHUB_SHA::7}
ts=$(date +%s)
if [[ "${{ matrix.package }}" == *-provider ]]; then
provider_type=$(echo ${{ matrix.package }} | cut -d- -f1)
repository=provider/${provider_type}
else
repository=${{ matrix.package }}
fi
echo "IMAGE_TAG=${env}-${sha}-${ts}" >> "$GITHUB_OUTPUT"
echo "REPOSITORY=${repository}" >> "$GITHUB_OUTPUT"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# ..to avoid rate limits when pulling images
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: 🚢 Build Container Image
run: |
docker build -t infra_image -f ./apps/${{ matrix.package }}/Containerfile .
# ..to push image
- name: 🐙 Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: 🐙 Push to GitHub Container Registry
run: |
docker tag infra_image $REGISTRY/$REPOSITORY:$IMAGE_TAG
docker push $REGISTRY/$REPOSITORY:$IMAGE_TAG
env:
REGISTRY: ghcr.io/triggerdotdev
REPOSITORY: ${{ steps.prep.outputs.REPOSITORY }}
IMAGE_TAG: ${{ steps.prep.outputs.IMAGE_TAG }}
+4
View File
@@ -9,6 +9,10 @@ on:
- "build-*"
paths:
- ".github/workflows/publish.yml"
- ".github/workflows/typecheck.yml"
- ".github/workflows/unit-tests.yml"
- ".github/workflows/e2e.yml"
- ".github/workflows/publish-docker.yml"
- "packages/**"
- "!packages/**/*.md"
- "!packages/**/*.eslintrc"
+9 -5
View File
@@ -8,12 +8,11 @@ on:
- "**.md"
- ".github/CODEOWNERS"
- ".github/ISSUE_TEMPLATE/**"
jobs:
release:
name: 🦋 Changesets Release
runs-on: buildjet-4vcpu-ubuntu-2204
runs-on: buildjet-8vcpu-ubuntu-2204
if: |
github.repository == 'triggerdotdev/trigger.dev'
outputs:
@@ -31,14 +30,19 @@ jobs:
- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v2.2.4
with:
version: 7.18
version: 8.15.5
- name: ⎔ Setup node
uses: buildjet/setup-node@v3
with:
node-version: 18
node-version: 20.11.1
cache: "pnpm"
- name: Install Protoc
uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: 📥 Download deps
run: pnpm install --frozen-lockfile
@@ -46,7 +50,7 @@ jobs:
run: pnpm run generate
- name: 🔎 Type check
run: pnpm run typecheck
run: pnpm run typecheck --filter "@trigger.dev/*" --filter "trigger.dev"
- name: 🔐 Setup npm auth
run: |
+8 -3
View File
@@ -3,7 +3,7 @@ on:
workflow_call:
jobs:
typecheck:
runs-on: buildjet-4vcpu-ubuntu-2204
runs-on: buildjet-8vcpu-ubuntu-2204
steps:
- name: ⬇️ Checkout repo
@@ -14,14 +14,19 @@ jobs:
- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v2.2.4
with:
version: 7.18
version: 8.15.5
- name: ⎔ Setup node
uses: buildjet/setup-node@v3
with:
node-version: 18
node-version: 20.11.1
cache: "pnpm"
- name: Install Protoc
uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: 📥 Download deps
run: pnpm install --frozen-lockfile
+8 -3
View File
@@ -4,7 +4,7 @@ on:
jobs:
unitTests:
name: "🧪 Unit Tests"
runs-on: buildjet-4vcpu-ubuntu-2204
runs-on: buildjet-8vcpu-ubuntu-2204
steps:
- name: ⬇️ Checkout repo
uses: actions/checkout@v3
@@ -14,14 +14,19 @@ jobs:
- name: ⎔ Setup pnpm
uses: pnpm/action-setup@v2.2.4
with:
version: 7.18
version: 8.15.5
- name: ⎔ Setup node
uses: buildjet/setup-node@v3
with:
node-version: 18
node-version: 20.11.1
cache: "pnpm"
- name: Install Protoc
uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: ⎔ Setup Deno
uses: denoland/setup-deno@v1
with:
+2 -1
View File
@@ -53,4 +53,5 @@ apps/**/public/build
/playwright-report/
/playwright/.cache/
.cosine
.cosine
.trigger/
+3
View File
@@ -0,0 +1,3 @@
[submodule "packages/otlp-importer/protos"]
path = packages/otlp-importer/protos
url = https://github.com/open-telemetry/opentelemetry-proto.git
+1 -1
View File
@@ -1 +1 @@
v18.18.0
v20.11.1
+26 -2
View File
@@ -9,7 +9,7 @@
"request": "launch",
"name": "Debug WebApp",
"command": "pnpm run dev --filter webapp",
"envFile": "${workspaceFolder}/apps/webapp/.env",
"envFile": "${workspaceFolder}/.env",
"cwd": "${workspaceFolder}",
"sourceMaps": true
},
@@ -23,11 +23,35 @@
{
"type": "node-terminal",
"request": "launch",
"name": "Debug BYO Auth",
"name": "Debug v2 job catalog",
"command": "pnpm run byo-auth",
"envFile": "${workspaceFolder}/references/job-catalog/.env",
"cwd": "${workspaceFolder}/references/job-catalog",
"sourceMaps": true
},
{
"type": "node-terminal",
"request": "launch",
"name": "Debug V3 Dev CLI",
"command": "pnpm exec triggerdev dev --log-level debug",
"cwd": "${workspaceFolder}/references/v3-catalog",
"sourceMaps": true
},
{
"type": "node-terminal",
"request": "launch",
"name": "Debug V3 Deploy CLI",
"command": "pnpm exec triggerdev deploy",
"cwd": "${workspaceFolder}/references/v3-catalog",
"sourceMaps": true
},
{
"type": "node",
"request": "attach",
"name": "Attach to Trigger.dev CLI (v3)",
"port": 9229,
"restart": true,
"skipFiles": ["<node_internals>/**"]
}
]
}
+1 -1
View File
@@ -47,7 +47,7 @@ pnpm exec changeset version --snapshot prerelease
3. Build the packages:
```sh
pnpm run build --filter "@trigger.dev/*"
pnpm run build --filter "@trigger.dev/*" --filter "trigger.dev"
```
4. Publish the snapshot (replace "dev" with your tag)
+1 -1
View File
@@ -7,7 +7,7 @@
### The open source background jobs framework
[Discord](https://discord.gg/JtBAxBr2m3) | [Website](https://trigger.dev) | [Issues](https://github.com/triggerdotdev/trigger.dev/issues) | [Docs](https://trigger.dev/docs)
[Discord](https://trigger.dev/discord) | [Website](https://trigger.dev) | [Issues](https://github.com/triggerdotdev/trigger.dev/issues) | [Docs](https://trigger.dev/docs)
[![Twitter](https://img.shields.io/twitter/url/https/twitter.com/triggerdotdev.svg?style=social&label=Follow%20%40trigger.dev)](https://twitter.com/triggerdotdev)
[![GitHub Repo stars](https://img.shields.io/github/stars/triggerdotdev/trigger.dev?style=social)](https://github.com/triggerdotdev/trigger.dev)
+4
View File
@@ -0,0 +1,4 @@
HTTP_SERVER_PORT=8020
PLATFORM_ENABLED=true
PLATFORM_WS_PORT=3030
SECURE_CONNECTION=false
+3
View File
@@ -0,0 +1,3 @@
dist/
node_modules/
.env
+60
View File
@@ -0,0 +1,60 @@
# syntax=docker/dockerfile:labs
FROM node:18-bullseye-slim@sha256:a4edd54dcfdcacc8a4100fee71498e8671d99556a1acf5614539214a70092426 AS node-18
WORKDIR /app
FROM node-18 AS pruner
COPY --chown=node:node . .
RUN npx -q turbo@1.10.9 prune --scope=coordinator --docker
RUN find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
FROM node-18 AS base
RUN apt-get update \
&& apt-get install -y buildah ca-certificates dumb-init \
&& rm -rf /var/lib/apt/lists/*
COPY --chown=node:node .gitignore .gitignore
COPY --from=pruner --chown=node:node /app/out/json/ .
COPY --from=pruner --chown=node:node /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=pruner --chown=node:node /app/out/pnpm-workspace.yaml ./pnpm-workspace.yaml
FROM base AS dev-deps
RUN corepack enable
ENV NODE_ENV development
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm fetch --no-frozen-lockfile
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm install --ignore-scripts --no-frozen-lockfile
FROM base AS builder
RUN corepack enable
COPY --from=pruner --chown=node:node /app/out/full/ .
COPY --from=dev-deps --chown=node:node /app/ .
COPY --chown=node:node turbo.json turbo.json
RUN pnpm run -r --filter coordinator build:bundle
FROM alpine AS cri-tools
WORKDIR /cri-tools
ARG CRICTL_VERSION=v1.29.0
ARG CRICTL_CHECKSUM=sha256:d16a1ffb3938f5a19d5c8f45d363bd091ef89c0bc4d44ad16b933eede32fdcbb
ADD --checksum=${CRICTL_CHECKSUM} \
https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-amd64.tar.gz .
RUN tar zxvf crictl-${CRICTL_VERSION}-linux-amd64.tar.gz
FROM base AS runner
RUN corepack enable
ENV NODE_ENV production
COPY --from=cri-tools --chown=node:node /cri-tools/crictl /usr/local/bin
COPY --from=builder --chown=node:node /app/apps/coordinator/dist/index.mjs ./index.mjs
EXPOSE 8000
CMD [ "/usr/bin/dumb-init", "--", "/usr/local/bin/node", "./index.mjs" ]
+3
View File
@@ -0,0 +1,3 @@
# Coordinator
Sits between the platform and tasks. Facilitates communication and checkpointing, amongst other things.
+34
View File
@@ -0,0 +1,34 @@
{
"name": "coordinator",
"private": true,
"version": "0.0.1",
"description": "",
"main": "dist/index.cjs",
"scripts": {
"build": "npm run build:bundle",
"build:bundle": "esbuild src/index.ts --bundle --outfile=dist/index.mjs --platform=node --format=esm --target=esnext --banner:js=\"import { createRequire } from 'module';const require = createRequire(import.meta.url);\"",
"build:image": "docker build -f Containerfile . -t coordinator",
"dev": "tsx --no-warnings=ExperimentalWarning --require dotenv/config --watch src/index.ts",
"start": "tsx src/index.ts",
"typecheck": "tsc --noEmit"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@trigger.dev/core": "workspace:*",
"@trigger.dev/core-apps": "workspace:*",
"execa": "^8.0.1",
"nanoid": "^5.0.6",
"prom-client": "^15.1.0",
"socket.io": "^4.7.4",
"socket.io-client": "^4.7.4"
},
"devDependencies": {
"@types/node": "^18",
"dotenv": "^16.4.2",
"esbuild": "^0.19.11",
"tsx": "^4.7.0",
"typescript": "^5.3.3"
}
}
+983
View File
@@ -0,0 +1,983 @@
import { createServer } from "node:http";
import { $ } from "execa";
import { nanoid } from "nanoid";
import { Server } from "socket.io";
import {
CoordinatorToPlatformMessages,
CoordinatorToProdWorkerMessages,
PlatformToCoordinatorMessages,
ProdWorkerSocketData,
ProdWorkerToCoordinatorMessages,
ZodNamespace,
ZodSocketConnection,
} from "@trigger.dev/core/v3";
import { HttpReply, getTextBody, SimpleLogger } from "@trigger.dev/core-apps";
import { collectDefaultMetrics, register, Gauge } from "prom-client";
collectDefaultMetrics();
const HTTP_SERVER_PORT = Number(process.env.HTTP_SERVER_PORT || 8020);
const NODE_NAME = process.env.NODE_NAME || "coordinator";
const DEFAULT_RETRY_DELAY_THRESHOLD_IN_MS = 30_000;
const REGISTRY_HOST = process.env.REGISTRY_HOST || "localhost:5000";
const CHECKPOINT_PATH = process.env.CHECKPOINT_PATH || "/checkpoints";
const REGISTRY_TLS_VERIFY = process.env.REGISTRY_TLS_VERIFY === "false" ? "false" : "true";
const PLATFORM_ENABLED = ["1", "true"].includes(process.env.PLATFORM_ENABLED ?? "true");
const PLATFORM_HOST = process.env.PLATFORM_HOST || "127.0.0.1";
const PLATFORM_WS_PORT = process.env.PLATFORM_WS_PORT || 3030;
const PLATFORM_SECRET = process.env.PLATFORM_SECRET || "coordinator-secret";
const SECURE_CONNECTION = ["1", "true"].includes(process.env.SECURE_CONNECTION ?? "false");
const logger = new SimpleLogger(`[${NODE_NAME}]`);
type CheckpointerInitializeReturn = {
canCheckpoint: boolean;
willSimulate: boolean;
};
type CheckpointAndPushOptions = {
runId: string;
leaveRunning?: boolean;
projectRef: string;
deploymentVersion: string;
};
type CheckpointData = {
location: string;
docker: boolean;
};
class Checkpointer {
#initialized = false;
#canCheckpoint = false;
#dockerMode = !process.env.KUBERNETES_PORT;
#logger = new SimpleLogger("[checkptr]");
#abortControllers = new Map<string, AbortController>();
constructor(private opts = { forceSimulate: false }) {}
async initialize(): Promise<CheckpointerInitializeReturn> {
if (this.#initialized) {
return this.#getInitializeReturn();
}
this.#logger.log(`${this.#dockerMode ? "Docker" : "Kubernetes"} mode`);
if (this.#dockerMode) {
try {
await $`criu --version`;
} catch (error) {
this.#logger.error("No checkpoint support: Missing CRIU binary");
this.#logger.error("Will simulate instead");
this.#canCheckpoint = false;
this.#initialized = true;
return this.#getInitializeReturn();
}
try {
await $`docker checkpoint`;
} catch (error) {
this.#logger.error(
"No checkpoint support: Docker needs to have experimental features enabled"
);
this.#logger.error("Will simulate instead");
this.#canCheckpoint = false;
this.#initialized = true;
return this.#getInitializeReturn();
}
} else {
try {
await $`buildah login --get-login ${REGISTRY_HOST}`;
} catch (error) {
this.#logger.error(`No checkpoint support: Not logged in to registry ${REGISTRY_HOST}`);
this.#canCheckpoint = false;
this.#initialized = true;
return this.#getInitializeReturn();
}
}
this.#logger.log(
`Full checkpoint support${
this.#dockerMode && this.opts.forceSimulate ? " with forced simulation enabled." : "!"
}`
);
this.#initialized = true;
this.#canCheckpoint = true;
return this.#getInitializeReturn();
}
#getInitializeReturn(): CheckpointerInitializeReturn {
return {
canCheckpoint: this.#canCheckpoint,
willSimulate: this.#dockerMode && (!this.#canCheckpoint || this.opts.forceSimulate),
};
}
#getImageRef(projectRef: string, deploymentVersion: string, shortCode: string) {
return `${REGISTRY_HOST}/trigger/${projectRef}:${deploymentVersion}.prod-${shortCode}`;
}
#getExportLocation(projectRef: string, deploymentVersion: string, shortCode: string) {
const basename = `${projectRef}-${deploymentVersion}-${shortCode}`;
if (this.#dockerMode) {
return basename;
} else {
return `${CHECKPOINT_PATH}/${basename}.tar`;
}
}
async checkpointAndPush(opts: CheckpointAndPushOptions): Promise<CheckpointData | undefined> {
const start = performance.now();
logger.log(`checkpointAndPush() start`, { start, opts });
const result = await this.#checkpointAndPush(opts);
const end = performance.now();
logger.log(`checkpointAndPush() end`, {
start,
end,
diff: end - start,
opts,
success: !!result,
});
return result;
}
isCheckpointing(runId: string) {
return this.#abortControllers.has(runId);
}
cancelCheckpoint(runId: string) {
const controller = this.#abortControllers.get(runId);
if (!controller) {
logger.debug("Nothing to cancel", { runId });
return;
}
controller.abort("cancelCheckpointing()");
this.#abortControllers.delete(runId);
}
async #checkpointAndPush({
runId,
leaveRunning = true, // This mirrors kubernetes behaviour more accurately
projectRef,
deploymentVersion,
}: CheckpointAndPushOptions): Promise<CheckpointData | undefined> {
await this.initialize();
if (!this.#dockerMode && !this.#canCheckpoint) {
this.#logger.error("No checkpoint support. Simulation requires docker.");
return;
}
if (this.#abortControllers.has(runId)) {
logger.error("Checkpoint procedure already in progress", {
options: {
runId,
leaveRunning,
projectRef,
deploymentVersion,
},
});
return;
}
const controller = new AbortController();
this.#abortControllers.set(runId, controller);
const $$ = $({ signal: controller.signal });
try {
const shortCode = nanoid(8);
const imageRef = this.#getImageRef(projectRef, deploymentVersion, shortCode);
const exportLocation = this.#getExportLocation(projectRef, deploymentVersion, shortCode);
this.#logger.log("Checkpointing:", {
options: {
runId,
leaveRunning,
projectRef,
deploymentVersion,
},
});
const containterName = this.#getRunContainerName(runId);
// Create checkpoint (docker)
if (this.#dockerMode) {
try {
if (this.opts.forceSimulate || !this.#canCheckpoint) {
this.#logger.log("Simulating checkpoint");
this.#logger.debug(await $$`docker pause ${containterName}`);
} else {
if (leaveRunning) {
this.#logger.debug(
await $$`docker checkpoint create --leave-running ${containterName} ${exportLocation}`
);
} else {
this.#logger.debug(
await $$`docker checkpoint create ${containterName} ${exportLocation}`
);
}
}
} catch (error: any) {
this.#logger.error(error.stderr);
return;
}
this.#logger.log("checkpoint created:", {
runId,
location: exportLocation,
});
return {
location: exportLocation,
docker: true,
};
}
// Create checkpoint (CRI)
if (!this.#canCheckpoint) {
throw new Error("No checkpoint support in kubernetes mode.");
}
const containerId = this.#logger.debug(
// @ts-expect-error
await $$`crictl ps`
.pipeStdout($$({ stdin: "pipe" })`grep ${containterName}`)
.pipeStdout($$({ stdin: "pipe" })`cut -f1 ${"-d "}`)
);
if (!containerId.stdout) {
throw new Error("could not find container id");
}
this.#logger.debug(await $$`crictl checkpoint --export=${exportLocation} ${containerId}`);
// Create image from checkpoint
const container = this.#logger.debug(await $$`buildah from scratch`);
this.#logger.debug(await $$`buildah add ${container} ${exportLocation} /`);
this.#logger.debug(
await $$`buildah config --annotation=io.kubernetes.cri-o.annotations.checkpoint.name=counter ${container}`
);
this.#logger.debug(await $$`buildah commit ${container} ${imageRef}`);
this.#logger.debug(await $$`buildah rm ${container}`);
// Push checkpoint image
this.#logger.debug(await $$`buildah push --tls-verify=${REGISTRY_TLS_VERIFY} ${imageRef}`);
this.#logger.log("Checkpointed and pushed image to:", { location: imageRef });
try {
await $$`rm ${exportLocation}`;
this.#logger.log("Deleted checkpoint archive", { exportLocation });
// Disabled for now as this will increase restore time by having to pull the image again
// await $`buildah rmi ${imageRef}`;
// this.#logger.log("Deleted checkpoint image", { imageRef });
} catch (error) {
this.#logger.error("Failed during checkpoint cleanup", { exportLocation });
this.#logger.debug(error);
}
return {
location: imageRef,
docker: false,
};
} catch (error) {
this.#logger.error("checkpoint failed", {
options: {
runId,
leaveRunning,
projectRef,
deploymentVersion,
},
error,
});
return;
} finally {
this.#abortControllers.delete(runId);
}
}
#getRunContainerName(suffix: string) {
return `task-run-${suffix}`;
}
}
class TaskCoordinator {
#httpServer: ReturnType<typeof createServer>;
#checkpointer = new Checkpointer({ forceSimulate: true });
#prodWorkerNamespace: ZodNamespace<
typeof ProdWorkerToCoordinatorMessages,
typeof CoordinatorToProdWorkerMessages,
typeof ProdWorkerSocketData
>;
#platformSocket?: ZodSocketConnection<
typeof CoordinatorToPlatformMessages,
typeof PlatformToCoordinatorMessages
>;
#checkpointableTasks = new Map<
string,
{ resolve: (value: void) => void; reject: (err?: any) => void }
>();
#delayThresholdInMs: number;
constructor(
private port: number,
private host = "0.0.0.0"
) {
this.#httpServer = this.#createHttpServer();
this.#checkpointer.initialize();
this.#delayThresholdInMs = this.#getDelayThreshold();
if (process.env.DELAY_THRESHOLD_IN_MS) {
this.#delayThresholdInMs = this.#getDelayThreshold();
}
const io = new Server(this.#httpServer);
this.#prodWorkerNamespace = this.#createProdWorkerNamespace(io);
this.#platformSocket = this.#createPlatformSocket();
const connectedTasksTotal = new Gauge({
name: "daemon_connected_tasks_total", // don't change this without updating dashboard config
help: "The number of tasks currently connected.",
collect: () => {
connectedTasksTotal.set(this.#prodWorkerNamespace.namespace.sockets.size);
},
});
register.registerMetric(connectedTasksTotal);
}
#getDelayThreshold() {
if (!process.env.RETRY_DELAY_THRESHOLD_IN_MS) {
return DEFAULT_RETRY_DELAY_THRESHOLD_IN_MS;
}
const threshold = parseInt(process.env.RETRY_DELAY_THRESHOLD_IN_MS);
if (isNaN(threshold)) {
logger.log(
"RETRY_DELAY_THRESHOLD_IN_MS parses as NaN, must supply integer. Will use default instead.",
{
RETRY_DELAY_THRESHOLD_IN_MS: process.env.RETRY_DELAY_THRESHOLD_IN_MS,
DEFAULT_DELAY_THRESHOLD_IN_MS: DEFAULT_RETRY_DELAY_THRESHOLD_IN_MS,
}
);
return DEFAULT_RETRY_DELAY_THRESHOLD_IN_MS;
}
return threshold;
}
#createPlatformSocket() {
if (!PLATFORM_ENABLED) {
console.log("INFO: platform connection disabled");
return;
}
const platformConnection = new ZodSocketConnection({
namespace: "coordinator",
host: PLATFORM_HOST,
port: Number(PLATFORM_WS_PORT),
secure: SECURE_CONNECTION,
clientMessages: CoordinatorToPlatformMessages,
serverMessages: PlatformToCoordinatorMessages,
authToken: PLATFORM_SECRET,
handlers: {
RESUME_AFTER_DEPENDENCY: async (message) => {
const taskSocket = await this.#getAttemptSocket(message.attemptFriendlyId);
if (!taskSocket) {
logger.log("Socket for attempt not found", {
attemptFriendlyId: message.attemptFriendlyId,
});
return;
}
// In case the task resumed faster than we could checkpoint
this.#cancelCheckpoint(message.runId);
taskSocket.emit("RESUME_AFTER_DEPENDENCY", message);
},
RESUME_AFTER_DURATION: async (message) => {
const taskSocket = await this.#getAttemptSocket(message.attemptFriendlyId);
if (!taskSocket) {
logger.log("Socket for attempt not found", {
attemptFriendlyId: message.attemptFriendlyId,
});
return;
}
taskSocket.emit("RESUME_AFTER_DURATION", message);
},
REQUEST_ATTEMPT_CANCELLATION: async (message) => {
const taskSocket = await this.#getAttemptSocket(message.attemptFriendlyId);
if (!taskSocket) {
logger.log("Socket for attempt not found", {
attemptFriendlyId: message.attemptFriendlyId,
});
return;
}
taskSocket.emit("REQUEST_ATTEMPT_CANCELLATION", message);
},
READY_FOR_RETRY: async (message) => {
const taskSocket = await this.#getRunSocket(message.runId);
if (!taskSocket) {
logger.log("Socket for attempt not found", {
runId: message.runId,
});
return;
}
taskSocket.emit("READY_FOR_RETRY", message);
},
},
});
return platformConnection;
}
async #getRunSocket(runId: string) {
const sockets = await this.#prodWorkerNamespace.fetchSockets();
for (const socket of sockets) {
if (socket.data.runId === runId) {
return socket;
}
}
}
async #getAttemptSocket(attemptFriendlyId: string) {
const sockets = await this.#prodWorkerNamespace.fetchSockets();
for (const socket of sockets) {
if (socket.data.attemptFriendlyId === attemptFriendlyId) {
return socket;
}
}
}
#createProdWorkerNamespace(io: Server) {
const provider = new ZodNamespace({
io,
name: "prod-worker",
clientMessages: ProdWorkerToCoordinatorMessages,
serverMessages: CoordinatorToProdWorkerMessages,
socketData: ProdWorkerSocketData,
postAuth: async (socket, next, logger) => {
function setSocketDataFromHeader(
dataKey: keyof typeof socket.data,
headerName: string,
required: boolean = true
) {
const value = socket.handshake.headers[headerName];
if (value) {
socket.data[dataKey] = Array.isArray(value) ? value[0] : value;
return;
}
if (required) {
logger.error("missing required header", { headerName });
throw new Error("missing header");
}
}
try {
setSocketDataFromHeader("podName", "x-pod-name");
setSocketDataFromHeader("contentHash", "x-trigger-content-hash");
setSocketDataFromHeader("projectRef", "x-trigger-project-ref");
setSocketDataFromHeader("runId", "x-trigger-run-id");
setSocketDataFromHeader("attemptFriendlyId", "x-trigger-attempt-friendly-id", false);
setSocketDataFromHeader("envId", "x-trigger-env-id");
setSocketDataFromHeader("deploymentId", "x-trigger-deployment-id");
setSocketDataFromHeader("deploymentVersion", "x-trigger-deployment-version");
} catch (error) {
logger.error("setSocketDataFromHeader error", { error });
socket.disconnect(true);
return;
}
logger.debug("success", socket.data);
next();
},
onConnection: async (socket, handler, sender) => {
const logger = new SimpleLogger(`[prod-worker][${socket.id}]`);
const checkpointInProgress = () => {
return this.#checkpointableTasks.has(socket.data.runId);
};
const readyToCheckpoint = async (): Promise<
{ success: true } | { success: false; reason?: string }
> => {
if (checkpointInProgress()) {
return {
success: false,
reason: "checkpoint in progress",
};
}
const isCheckpointable = new Promise((resolve, reject) => {
// We set a reasonable timeout to prevent waiting forever
// TODO: We may also want to cancel the task as it's unlikely to recover
setTimeout(() => reject("timeout"), 10_000);
this.#checkpointableTasks.set(socket.data.runId, { resolve, reject });
});
try {
await isCheckpointable;
this.#checkpointableTasks.delete(socket.data.runId);
return {
success: true,
};
} catch (error) {
logger.error("Error while waiting for checkpointable state", { error });
return {
success: false,
reason: typeof error === "string" ? error : "unknown",
};
}
};
this.#platformSocket?.send("LOG", {
metadata: socket.data,
text: "connected",
});
socket.on("LOG", (message, callback) => {
logger.log("[LOG]", message.text);
callback();
this.#platformSocket?.send("LOG", {
version: "v1",
metadata: socket.data,
text: message.text,
});
});
socket.on("READY_FOR_EXECUTION", async (message) => {
logger.log("[READY_FOR_EXECUTION]", message);
try {
const executionAck = await this.#platformSocket?.sendWithAck(
"READY_FOR_EXECUTION",
message
);
if (!executionAck) {
logger.error("no execution ack", { runId: socket.data.runId });
socket.emit("REQUEST_EXIT", {
version: "v1",
});
return;
}
if (!executionAck.success) {
logger.error("failed to get execution payload", { runId: socket.data.runId });
socket.emit("REQUEST_EXIT", {
version: "v1",
});
return;
}
socket.emit("EXECUTE_TASK_RUN", {
version: "v1",
executionPayload: executionAck.payload,
});
socket.data.attemptFriendlyId = executionAck.payload.execution.attempt.id;
} catch (error) {
logger.error("Error", { error });
}
});
socket.on("READY_FOR_RESUME", async (message) => {
logger.log("[READY_FOR_RESUME]", message);
socket.data.attemptFriendlyId = message.attemptFriendlyId;
this.#platformSocket?.send("READY_FOR_RESUME", message);
});
socket.on("TASK_RUN_COMPLETED", async ({ completion, execution }, callback) => {
logger.log("completed task", { completionId: completion.id });
const completeWithoutCheckpoint = (shouldExit: boolean) => {
this.#platformSocket?.send("TASK_RUN_COMPLETED", {
version: "v1",
execution,
completion,
});
callback({ willCheckpointAndRestore: false, shouldExit });
};
if (completion.ok) {
completeWithoutCheckpoint(true);
return;
}
if (
completion.error.type === "INTERNAL_ERROR" &&
completion.error.code === "TASK_RUN_CANCELLED"
) {
completeWithoutCheckpoint(true);
return;
}
if (completion.retry === undefined) {
completeWithoutCheckpoint(true);
return;
}
if (completion.retry.delay < this.#delayThresholdInMs) {
completeWithoutCheckpoint(false);
return;
}
const { canCheckpoint, willSimulate } = await this.#checkpointer.initialize();
const willCheckpointAndRestore = canCheckpoint || willSimulate;
if (!willCheckpointAndRestore) {
completeWithoutCheckpoint(false);
return;
}
// The worker will then put itself in a checkpointable state
callback({ willCheckpointAndRestore: true, shouldExit: false });
const ready = await readyToCheckpoint();
if (!ready.success) {
logger.error("Failed to become checkpointable", {
runId: socket.data.runId,
reason: ready.reason,
});
return;
}
const checkpoint = await this.#checkpointer.checkpointAndPush({
runId: socket.data.runId,
projectRef: socket.data.projectRef,
deploymentVersion: socket.data.deploymentVersion,
});
if (!checkpoint) {
logger.error("Failed to checkpoint", { runId: socket.data.runId });
completeWithoutCheckpoint(false);
return;
}
this.#platformSocket?.send("TASK_RUN_COMPLETED", {
version: "v1",
execution,
completion,
checkpoint,
});
if (!checkpoint.docker || !willSimulate) {
socket.emit("REQUEST_EXIT", {
version: "v1",
});
}
});
socket.on("READY_FOR_CHECKPOINT", async (message) => {
logger.log("[READY_FOR_CHECKPOINT]", message);
const checkpointable = this.#checkpointableTasks.get(socket.data.runId);
if (!checkpointable) {
logger.error("No checkpoint scheduled", { runId: socket.data.runId });
return;
}
checkpointable.resolve();
});
socket.on("CANCEL_CHECKPOINT", async (message) => {
logger.log("[CANCEL_CHECKPOINT]", message);
this.#cancelCheckpoint(socket.data.runId);
});
socket.on("WAIT_FOR_DURATION", async (message, callback) => {
logger.log("[WAIT_FOR_DURATION]", message);
if (checkpointInProgress()) {
logger.error("Checkpoint already in progress", { runId: socket.data.runId });
callback({ willCheckpointAndRestore: false });
return;
}
const { canCheckpoint, willSimulate } = await this.#checkpointer.initialize();
const willCheckpointAndRestore = canCheckpoint || willSimulate;
callback({ willCheckpointAndRestore });
if (!willCheckpointAndRestore) {
return;
}
const ready = await readyToCheckpoint();
if (!ready.success) {
logger.error("Failed to become checkpointable", {
runId: socket.data.runId,
reason: ready.reason,
});
return;
}
const checkpoint = await this.#checkpointer.checkpointAndPush({
runId: socket.data.runId,
projectRef: socket.data.projectRef,
deploymentVersion: socket.data.deploymentVersion,
});
if (!checkpoint) {
// The task container will keep running until the wait duration has elapsed
logger.error("Failed to checkpoint", { runId: socket.data.runId });
return;
}
if (!checkpoint.docker || !willSimulate) {
socket.emit("REQUEST_EXIT", {
version: "v1",
});
}
this.#platformSocket?.send("CHECKPOINT_CREATED", {
version: "v1",
attemptFriendlyId: message.attemptFriendlyId,
docker: checkpoint.docker,
location: checkpoint.location,
reason: {
type: "WAIT_FOR_DURATION",
ms: message.ms,
now: message.now,
},
});
});
socket.on("WAIT_FOR_TASK", async (message, callback) => {
logger.log("[WAIT_FOR_TASK]", message);
const { canCheckpoint, willSimulate } = await this.#checkpointer.initialize();
const willCheckpointAndRestore = canCheckpoint || willSimulate;
callback({ willCheckpointAndRestore });
if (!willCheckpointAndRestore) {
return;
}
const checkpoint = await this.#checkpointer.checkpointAndPush({
runId: socket.data.runId,
projectRef: socket.data.projectRef,
deploymentVersion: socket.data.deploymentVersion,
});
if (!checkpoint) {
logger.error("Failed to checkpoint", { runId: socket.data.runId });
return;
}
if (!checkpoint.docker || !willSimulate) {
socket.emit("REQUEST_EXIT", {
version: "v1",
});
}
this.#platformSocket?.send("CHECKPOINT_CREATED", {
version: "v1",
attemptFriendlyId: message.attemptFriendlyId,
docker: checkpoint.docker,
location: checkpoint.location,
reason: {
type: "WAIT_FOR_TASK",
friendlyId: message.friendlyId,
},
});
});
socket.on("WAIT_FOR_BATCH", async (message, callback) => {
logger.log("[WAIT_FOR_BATCH]", message);
const { canCheckpoint, willSimulate } = await this.#checkpointer.initialize();
const willCheckpointAndRestore = canCheckpoint || willSimulate;
callback({ willCheckpointAndRestore });
if (!willCheckpointAndRestore) {
return;
}
const checkpoint = await this.#checkpointer.checkpointAndPush({
runId: socket.data.runId,
projectRef: socket.data.projectRef,
deploymentVersion: socket.data.deploymentVersion,
});
if (!checkpoint) {
logger.error("Failed to checkpoint", { runId: socket.data.runId });
return;
}
if (!checkpoint.docker || !willSimulate) {
socket.emit("REQUEST_EXIT", {
version: "v1",
});
}
this.#platformSocket?.send("CHECKPOINT_CREATED", {
version: "v1",
attemptFriendlyId: message.attemptFriendlyId,
docker: checkpoint.docker,
location: checkpoint.location,
reason: {
type: "WAIT_FOR_BATCH",
batchFriendlyId: message.batchFriendlyId,
runFriendlyIds: message.runFriendlyIds,
},
});
});
socket.on("INDEX_TASKS", async (message, callback) => {
logger.log("[INDEX_TASKS]", message);
const workerAck = await this.#platformSocket?.sendWithAck("CREATE_WORKER", {
version: "v1",
projectRef: socket.data.projectRef,
envId: socket.data.envId,
deploymentId: message.deploymentId,
metadata: {
contentHash: socket.data.contentHash,
packageVersion: message.packageVersion,
tasks: message.tasks,
},
});
if (!workerAck) {
logger.debug("no worker ack while indexing", message);
}
callback({ success: !!workerAck?.success });
});
socket.on("INDEXING_FAILED", async (message) => {
logger.log("[INDEXING_FAILED]", message);
this.#platformSocket?.send("INDEXING_FAILED", {
version: "v1",
deploymentId: message.deploymentId,
error: message.error,
});
});
},
onDisconnect: async (socket, handler, sender, logger) => {
this.#platformSocket?.send("LOG", {
metadata: socket.data,
text: "disconnect",
});
},
handlers: {
TASK_HEARTBEAT: async (message) => {
this.#platformSocket?.send("TASK_HEARTBEAT", message);
},
},
});
return provider;
}
#cancelCheckpoint(runId: string) {
const checkpointWait = this.#checkpointableTasks.get(runId);
if (checkpointWait) {
// Stop waiting for task to reach checkpointable state
checkpointWait.reject("Checkpoint cancelled");
}
// Cancel checkpointing procedure
this.#checkpointer.cancelCheckpoint(runId);
}
#createHttpServer() {
const httpServer = createServer(async (req, res) => {
logger.log(`[${req.method}]`, req.url);
const reply = new HttpReply(res);
switch (req.url) {
case "/health": {
return reply.text("ok");
}
case "/metrics": {
return reply.text(await register.metrics(), 200, register.contentType);
}
case "/whoami": {
return reply.text(NODE_NAME);
}
case "/checkpoint": {
const body = await getTextBody(req);
// await this.#checkpointer.checkpointAndPush(body);
return reply.text(`sent restore request: ${body}`);
}
default: {
return reply.empty(404);
}
}
});
httpServer.on("clientError", (err, socket) => {
socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
});
httpServer.on("listening", () => {
logger.log("server listening on port", HTTP_SERVER_PORT);
});
return httpServer;
}
listen() {
this.#httpServer.listen(this.port, this.host);
}
}
const coordinator = new TaskCoordinator(HTTP_SERVER_PORT);
coordinator.listen();
+19
View File
@@ -0,0 +1,19 @@
{
"include": ["./src/**/*.ts"],
"exclude": ["node_modules"],
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"paths": {
"@trigger.dev/core/v3": ["../../packages/core/src/v3"],
"@trigger.dev/core/v3/*": ["../../packages/core/src/v3/*"],
"@trigger.dev/core-apps": ["../../packages/core-apps/src"],
"@trigger.dev/core-apps/*": ["../../packages/core-apps/src/*"]
}
}
}
+9
View File
@@ -0,0 +1,9 @@
HTTP_SERVER_PORT=8050
PLATFORM_WS_PORT=3030
PLATFORM_SECRET=provider-secret
SECURE_CONNECTION=false
# Use this if you are on macOS
# COORDINATOR_HOST="host.docker.internal"
# OTEL_EXPORTER_OTLP_ENDPOINT="http://host.docker.internal:4318"
+3
View File
@@ -0,0 +1,3 @@
dist/
node_modules/
.env
+16
View File
@@ -0,0 +1,16 @@
# syntax=docker/dockerfile:labs
FROM node:18-slim AS base
RUN apt-get update \
&& apt-get install -y dumb-init
FROM base
WORKDIR /app
COPY --chown=node dist/index.mjs /app/
EXPOSE 8000
ENTRYPOINT [ "/usr/bin/dumb-init", "--", "/usr/local/bin/node", "/app/index.mjs" ]
+3
View File
@@ -0,0 +1,3 @@
# Docker provider
The `docker-provider` allows the platform to be orchestrator-agnostic. The platform can perform actions such as `INDEX_TASKS` or `INVOKE_TASK` which the provider translates into Docker actions.
+31
View File
@@ -0,0 +1,31 @@
{
"name": "docker-provider",
"private": true,
"version": "0.0.1",
"description": "",
"main": "dist/index.cjs",
"scripts": {
"build": "npm run build:bundle",
"build:bundle": "esbuild src/index.ts --bundle --outfile=dist/index.mjs --platform=node --format=esm --target=esnext --banner:js=\"import { createRequire } from 'module';const require = createRequire(import.meta.url);\"",
"build:image": "docker build -f Containerfile . -t docker-provider",
"dev": "tsx --no-warnings=ExperimentalWarning --require dotenv/config --watch src/index.ts",
"start": "tsx src/index.ts",
"typecheck": "tsc --noEmit"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@trigger.dev/core": "workspace:*",
"@trigger.dev/core-apps": "workspace:*",
"execa": "^8.0.1",
"socket.io-client": "^4.7.4"
},
"devDependencies": {
"@types/node": "^18.19.8",
"dotenv": "^16.4.2",
"esbuild": "^0.19.11",
"tsx": "^4.7.0",
"typescript": "^5.3.3"
}
}
+302
View File
@@ -0,0 +1,302 @@
import { $, type ExecaChildProcess, execa } from "execa";
import {
SimpleLogger,
TaskOperations,
ProviderShell,
TaskOperationsRestoreOptions,
TaskOperationsCreateOptions,
TaskOperationsIndexOptions,
} from "@trigger.dev/core-apps";
import { setTimeout } from "node:timers/promises";
import { PostStartCauses, PreStopCauses } from "@trigger.dev/core/v3";
const MACHINE_NAME = process.env.MACHINE_NAME || "local";
const COORDINATOR_PORT = process.env.COORDINATOR_PORT || 8020;
const COORDINATOR_HOST = process.env.COORDINATOR_HOST || "127.0.0.1";
const OTEL_EXPORTER_OTLP_ENDPOINT =
process.env.OTEL_EXPORTER_OTLP_ENDPOINT || "http://0.0.0.0:4318";
const logger = new SimpleLogger(`[${MACHINE_NAME}]`);
type InitializeReturn = {
canCheckpoint: boolean;
willSimulate: boolean;
};
function isExecaChildProcess(maybeExeca: unknown): maybeExeca is Awaited<ExecaChildProcess> {
return typeof maybeExeca === "object" && maybeExeca !== null && "escapedCommand" in maybeExeca;
}
class DockerTaskOperations implements TaskOperations {
#initialized = false;
#canCheckpoint = false;
constructor(private opts = { forceSimulate: false }) {}
async #initialize(): Promise<InitializeReturn> {
if (this.#initialized) {
return this.#getInitializeReturn();
}
logger.log("Initializing task operations");
if (this.opts.forceSimulate) {
logger.log("Forced simulation enabled. Will simulate regardless of checkpoint support.");
}
try {
await $`criu --version`;
} catch (error) {
logger.error("No checkpoint support: Missing CRIU binary. Will simulate instead.");
this.#canCheckpoint = false;
this.#initialized = true;
return this.#getInitializeReturn();
}
try {
await $`docker checkpoint`;
} catch (error) {
logger.error("No checkpoint support: Docker needs to have experimental features enabled");
logger.error("Will simulate instead");
this.#canCheckpoint = false;
this.#initialized = true;
return this.#getInitializeReturn();
}
logger.log("Full checkpoint support!");
this.#initialized = true;
this.#canCheckpoint = true;
return this.#getInitializeReturn();
}
#getInitializeReturn(): InitializeReturn {
return {
canCheckpoint: this.#canCheckpoint,
willSimulate: !this.#canCheckpoint || this.opts.forceSimulate,
};
}
async index(opts: TaskOperationsIndexOptions) {
await this.#initialize();
const containerName = this.#getIndexContainerName(opts.shortCode);
logger.log(`Indexing task ${opts.imageRef}`, {
host: COORDINATOR_HOST,
port: COORDINATOR_PORT,
});
try {
logger.debug(
await execa("docker", [
"run",
"--network=host",
"--rm",
`--env=INDEX_TASKS=true`,
`--env=TRIGGER_SECRET_KEY=${opts.apiKey}`,
`--env=TRIGGER_API_URL=${opts.apiUrl}`,
`--env=TRIGGER_ENV_ID=${opts.envId}`,
`--env=OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT}`,
`--env=POD_NAME=${containerName}`,
`--env=COORDINATOR_HOST=${COORDINATOR_HOST}`,
`--env=COORDINATOR_PORT=${COORDINATOR_PORT}`,
`--name=${containerName}`,
`${opts.imageRef}`,
])
);
} catch (error: any) {
if (!isExecaChildProcess(error)) {
throw error;
}
logger.error("Index failed:", {
opts,
exitCode: error.exitCode,
escapedCommand: error.escapedCommand,
stdout: error.stdout,
stderr: error.stderr,
});
}
}
async create(opts: TaskOperationsCreateOptions) {
await this.#initialize();
const containerName = this.#getRunContainerName(opts.runId);
try {
logger.debug(
await execa("docker", [
"run",
"--network=host",
"--detach",
`--env=TRIGGER_ENV_ID=${opts.envId}`,
`--env=TRIGGER_RUN_ID=${opts.runId}`,
`--env=OTEL_EXPORTER_OTLP_ENDPOINT=${OTEL_EXPORTER_OTLP_ENDPOINT}`,
`--env=POD_NAME=${containerName}`,
`--env=COORDINATOR_HOST=${COORDINATOR_HOST}`,
`--env=COORDINATOR_PORT=${COORDINATOR_PORT}`,
`--name=${containerName}`,
`${opts.image}`,
])
);
} catch (error) {
if (!isExecaChildProcess(error)) {
throw error;
}
logger.error("Create failed:", {
opts,
exitCode: error.exitCode,
escapedCommand: error.escapedCommand,
stdout: error.stdout,
stderr: error.stderr,
});
}
}
async restore(opts: TaskOperationsRestoreOptions) {
await this.#initialize();
const containerName = this.#getRunContainerName(opts.runId);
if (!this.#canCheckpoint || this.opts.forceSimulate) {
logger.log("Simulating restore");
const unpause = logger.debug(await $`docker unpause ${containerName}`);
if (unpause.exitCode !== 0) {
throw new Error("docker unpause command failed");
}
await this.#sendPostStart(containerName);
return;
}
const { exitCode } = logger.debug(
await $`docker start --checkpoint=${opts.checkpointRef} ${containerName}`
);
if (exitCode !== 0) {
throw new Error("docker start command failed");
}
await this.#sendPostStart(containerName);
}
async delete(opts: { runId: string }) {
await this.#initialize();
const containerName = this.#getRunContainerName(opts.runId);
await this.#sendPreStop(containerName);
logger.log("noop: delete");
}
async get(opts: { runId: string }) {
await this.#initialize();
logger.log("noop: get");
}
#getIndexContainerName(suffix: string) {
return `task-index-${suffix}`;
}
#getRunContainerName(suffix: string) {
return `task-run-${suffix}`;
}
async #sendPostStart(containerName: string): Promise<void> {
try {
const port = await this.#getHttpServerPort(containerName);
logger.debug(await this.#runLifecycleCommand(containerName, port, "postStart", "restore"));
} catch (error) {
logger.error("postStart error", { error });
throw new Error("postStart command failed");
}
}
async #sendPreStop(containerName: string): Promise<void> {
try {
const port = await this.#getHttpServerPort(containerName);
logger.debug(await this.#runLifecycleCommand(containerName, port, "preStop", "terminate"));
} catch (error) {
logger.error("preStop error", { error });
throw new Error("preStop command failed");
}
}
async #getHttpServerPort(containerName: string): Promise<number> {
// We first get the correct port, which is random during dev as we run with host networking and need to avoid clashes
// FIXME: Skip this in prod
const logs = logger.debug(await $`docker logs ${containerName}`);
const matches = logs.stdout.match(/http server listening on port (?<port>[0-9]+)/);
const port = Number(matches?.groups?.port);
if (!port) {
throw new Error("failed to extract port from logs");
}
return port;
}
async #runLifecycleCommand<THookType extends "postStart" | "preStop">(
containerName: string,
port: number,
type: THookType,
cause: THookType extends "postStart" ? PostStartCauses : PreStopCauses,
retryCount = 0
): Promise<ExecaChildProcess> {
try {
return await execa("docker", [
"exec",
containerName,
"busybox",
"wget",
"-q",
"-O-",
`127.0.0.1:${port}/${type}?cause=${cause}`,
]);
} catch (error: any) {
if (type === "postStart" && retryCount < 6) {
logger.debug(`retriable ${type} error`, { retryCount, message: error?.message });
await setTimeout(exponentialBackoff(retryCount + 1, 2, 50, 1150, 50));
return this.#runLifecycleCommand(containerName, port, type, cause, retryCount + 1);
}
logger.error(`final ${type} error`, { message: error?.message });
throw new Error(`${type} command failed after ${retryCount - 1} retries`);
}
}
}
const provider = new ProviderShell({
tasks: new DockerTaskOperations({ forceSimulate: true }),
type: "docker",
});
provider.listen();
function exponentialBackoff(
retryCount: number,
exponential: number,
minDelay: number,
maxDelay: number,
jitter: number
): number {
// Calculate the delay using the exponential backoff formula
const delay = Math.min(Math.pow(exponential, retryCount) * minDelay, maxDelay);
// Calculate the jitter
const jitterValue = Math.random() * jitter;
// Return the calculated delay with jitter
return delay + jitterValue;
}
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"strict": true,
"skipLibCheck": true,
"paths": {
"@trigger.dev/core/v3": ["../../packages/core/src/v3"],
"@trigger.dev/core/v3/*": ["../../packages/core/src/v3/*"],
"@trigger.dev/core-apps": ["../../packages/core-apps/src"],
"@trigger.dev/core-apps/*": ["../../packages/core-apps/src/*"]
}
}
}
+9
View File
@@ -0,0 +1,9 @@
HTTP_SERVER_PORT=8060
PLATFORM_WS_PORT=3030
PLATFORM_SECRET=provider-secret
SECURE_CONNECTION=false
# Use this if you are on macOS
# COORDINATOR_HOST="host.docker.internal"
# OTEL_EXPORTER_OTLP_ENDPOINT="http://host.docker.internal:4318"
+3
View File
@@ -0,0 +1,3 @@
dist/
node_modules/
.env
+47
View File
@@ -0,0 +1,47 @@
FROM node:18-alpine@sha256:ca9f6cb0466f9638e59e0c249d335a07c867cd50c429b5c7830dda1bed584649 AS node-18-alpine
WORKDIR /app
FROM node-18-alpine AS pruner
COPY --chown=node:node . .
RUN npx -q turbo@1.10.9 prune --scope=kubernetes-provider --docker
RUN find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
FROM node-18-alpine AS base
RUN apk add --no-cache dumb-init
COPY --chown=node:node .gitignore .gitignore
COPY --from=pruner --chown=node:node /app/out/json/ .
COPY --from=pruner --chown=node:node /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
COPY --from=pruner --chown=node:node /app/out/pnpm-workspace.yaml ./pnpm-workspace.yaml
FROM base AS dev-deps
RUN corepack enable
ENV NODE_ENV development
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm fetch --no-frozen-lockfile
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm install --ignore-scripts --no-frozen-lockfile
FROM base AS builder
RUN corepack enable
COPY --from=pruner --chown=node:node /app/out/full/ .
COPY --from=dev-deps --chown=node:node /app/ .
COPY --chown=node:node turbo.json turbo.json
RUN pnpm run -r --filter kubernetes-provider build:bundle
FROM base AS runner
RUN corepack enable
ENV NODE_ENV production
COPY --from=builder --chown=node:node /app/apps/kubernetes-provider/dist/index.mjs ./index.mjs
EXPOSE 8000
USER node
CMD [ "/usr/bin/dumb-init", "--", "/usr/local/bin/node", "./index.mjs" ]
+3
View File
@@ -0,0 +1,3 @@
# Kubernetes provider
The `kubernetes-provider` allows the platform to be orchestrator-agnostic. The platform can perform actions such as `INDEX_TASKS` or `INVOKE_TASK` which the provider translates into Kubernetes actions.
+31
View File
@@ -0,0 +1,31 @@
{
"name": "kubernetes-provider",
"private": true,
"version": "0.0.1",
"description": "",
"main": "dist/index.cjs",
"scripts": {
"build": "npm run build:bundle",
"build:bundle": "esbuild src/index.ts --bundle --outfile=dist/index.mjs --platform=node --format=esm --target=esnext --banner:js=\"import { createRequire } from 'module';const require = createRequire(import.meta.url);\"",
"build:image": "docker build -f Containerfile . -t kubernetes-provider",
"dev": "tsx --no-warnings=ExperimentalWarning --require dotenv/config --watch src/index.ts",
"start": "tsx src/index.ts",
"typecheck": "tsc --noEmit"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@kubernetes/client-node": "^0.20.0",
"@trigger.dev/core": "workspace:*",
"@trigger.dev/core-apps": "workspace:*",
"p-queue": "^8.0.1",
"socket.io-client": "^4.7.4"
},
"devDependencies": {
"dotenv": "^16.4.2",
"esbuild": "^0.19.11",
"tsx": "^4.7.0",
"typescript": "^5.3.3"
}
}
+544
View File
@@ -0,0 +1,544 @@
import * as k8s from "@kubernetes/client-node";
import {
ProviderShell,
SimpleLogger,
TaskOperations,
TaskOperationsCreateOptions,
TaskOperationsIndexOptions,
TaskOperationsRestoreOptions,
} from "@trigger.dev/core-apps";
import { Machine, PostStartCauses, PreStopCauses, EnvironmentType } from "@trigger.dev/core/v3";
import { randomUUID } from "crypto";
import { TaskMonitor } from "./taskMonitor";
const RUNTIME_ENV = process.env.KUBERNETES_PORT ? "kubernetes" : "local";
const NODE_NAME = process.env.NODE_NAME || "local";
const OTEL_EXPORTER_OTLP_ENDPOINT =
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://0.0.0.0:4318";
const logger = new SimpleLogger(`[${NODE_NAME}]`);
logger.log(`running in ${RUNTIME_ENV} mode`);
type Namespace = {
metadata: {
name: string;
};
};
type ComputeResources = {
[K in "cpu" | "memory" | "ephemeral-storage"]?: string;
};
class KubernetesTaskOperations implements TaskOperations {
#namespace: Namespace;
#k8sApi: {
core: k8s.CoreV1Api;
batch: k8s.BatchV1Api;
};
constructor(namespace = "default") {
this.#namespace = {
metadata: {
name: namespace,
},
};
this.#k8sApi = this.#createK8sApi();
}
async index(opts: TaskOperationsIndexOptions) {
await this.#createJob(
{
metadata: {
name: this.#getIndexContainerName(opts.shortCode),
namespace: this.#namespace.metadata.name,
},
spec: {
completions: 1,
backoffLimit: 0,
ttlSecondsAfterFinished: 300,
template: {
metadata: {
labels: {
...this.#getSharedLabels(opts),
app: "task-index",
"app.kubernetes.io/part-of": "trigger-worker",
"app.kubernetes.io/component": "index",
deployment: opts.deploymentId,
},
},
spec: {
...this.#defaultPodSpec,
containers: [
{
name: this.#getIndexContainerName(opts.shortCode),
image: opts.imageRef,
ports: [
{
containerPort: 8000,
},
],
resources: {
limits: {
cpu: "250m",
memory: "0.5G",
"ephemeral-storage": "2Gi",
},
},
lifecycle: {
preStop: {
exec: {
command: this.#getLifecycleCommand("preStop", "terminate"),
},
},
},
env: [
...this.#getSharedEnv(opts.envId),
{
name: "INDEX_TASKS",
value: "true",
},
{
name: "TRIGGER_SECRET_KEY",
value: opts.apiKey,
},
{
name: "TRIGGER_API_URL",
value: opts.apiUrl,
},
],
},
],
},
},
},
},
this.#namespace
);
}
async create(opts: TaskOperationsCreateOptions) {
await this.#createPod(
{
metadata: {
name: this.#getRunContainerName(opts.runId),
namespace: this.#namespace.metadata.name,
labels: {
...this.#getSharedLabels(opts),
app: "task-run",
"app.kubernetes.io/part-of": "trigger-worker",
"app.kubernetes.io/component": "create",
run: opts.runId,
},
},
spec: {
...this.#defaultPodSpec,
terminationGracePeriodSeconds: 60 * 60,
containers: [
{
name: this.#getRunContainerName(opts.runId),
image: opts.image,
ports: [
{
containerPort: 8000,
},
],
resources: {
requests: {
...this.#defaultResourceRequests,
},
limits: {
...this.#defaultResourceLimits,
...this.#getResourcesFromMachineConfig(opts.machine),
},
},
lifecycle: {
preStop: {
exec: {
command: this.#getLifecycleCommand("preStop", "terminate"),
},
},
},
env: [
...this.#getSharedEnv(opts.envId),
{
name: "TRIGGER_RUN_ID",
value: opts.runId,
},
],
volumeMounts: [
{
name: "taskinfo",
mountPath: "/etc/taskinfo",
},
],
},
],
volumes: [
{
name: "taskinfo",
emptyDir: {},
},
],
},
},
this.#namespace
);
}
async restore(opts: TaskOperationsRestoreOptions) {
await this.#createPod(
{
metadata: {
name: `${this.#getRunContainerName(opts.runId)}-${randomUUID().slice(0, 8)}`,
namespace: this.#namespace.metadata.name,
labels: {
...this.#getSharedLabels(opts),
app: "task-run",
"app.kubernetes.io/part-of": "trigger-worker",
"app.kubernetes.io/component": "restore",
run: opts.runId,
checkpoint: opts.checkpointId,
},
},
spec: {
...this.#defaultPodSpec,
initContainers: [
{
name: "pull-base-image",
image: opts.imageRef,
command: ["sleep", "0"],
},
{
name: "populate-taskinfo",
image: "busybox",
command: ["/bin/sh", "-c"],
args: ["printenv COORDINATOR_HOST | tee /etc/taskinfo/coordinator-host"],
env: [
{
name: "COORDINATOR_HOST",
valueFrom: {
fieldRef: {
fieldPath: "status.hostIP",
},
},
},
],
volumeMounts: [
{
name: "taskinfo",
mountPath: "/etc/taskinfo",
},
],
},
],
containers: [
{
name: this.#getRunContainerName(opts.runId),
image: opts.checkpointRef,
ports: [
{
containerPort: 8000,
},
],
resources: {
requests: {
...this.#defaultResourceRequests,
},
limits: {
...this.#defaultResourceLimits,
...this.#getResourcesFromMachineConfig(opts.machine),
},
},
lifecycle: {
postStart: {
exec: {
command: this.#getLifecycleCommand("postStart", "restore"),
},
},
preStop: {
exec: {
command: this.#getLifecycleCommand("preStop", "terminate"),
},
},
},
volumeMounts: [
{
name: "taskinfo",
mountPath: "/etc/taskinfo",
},
],
},
],
volumes: [
{
name: "taskinfo",
emptyDir: {},
},
],
},
},
this.#namespace
);
}
async delete(opts: { runId: string }) {
await this.#deletePod({
runId: opts.runId,
namespace: this.#namespace,
});
}
async get(opts: { runId: string }) {
await this.#getPod(opts.runId, this.#namespace);
}
#envTypeToLabelValue(type: EnvironmentType) {
switch (type) {
case "PRODUCTION":
return "prod";
case "STAGING":
return "stg";
case "DEVELOPMENT":
return "dev";
case "PREVIEW":
return "preview";
}
}
get #defaultPodSpec(): Omit<k8s.V1PodSpec, "containers"> {
return {
restartPolicy: "Never",
automountServiceAccountToken: false,
imagePullSecrets: [
{
name: "registry-trigger",
},
],
nodeSelector: {
nodetype: "worker",
},
};
}
get #defaultResourceRequests(): ComputeResources {
return {
"ephemeral-storage": "2Gi",
};
}
get #defaultResourceLimits(): ComputeResources {
return {
"ephemeral-storage": "10Gi",
};
}
#getSharedEnv(envId: string): k8s.V1EnvVar[] {
return [
{
name: "TRIGGER_ENV_ID",
value: envId,
},
{
name: "DEBUG",
value: process.env.DEBUG ? "1" : "0",
},
{
name: "HTTP_SERVER_PORT",
value: "8000",
},
{
name: "OTEL_EXPORTER_OTLP_ENDPOINT",
value: OTEL_EXPORTER_OTLP_ENDPOINT,
},
{
name: "POD_NAME",
valueFrom: {
fieldRef: {
fieldPath: "metadata.name",
},
},
},
{
name: "COORDINATOR_HOST",
valueFrom: {
fieldRef: {
fieldPath: "status.hostIP",
},
},
},
{
name: "MACHINE_NAME",
valueFrom: {
fieldRef: {
fieldPath: "spec.nodeName",
},
},
},
];
}
#getSharedLabels(
opts: TaskOperationsIndexOptions | TaskOperationsCreateOptions | TaskOperationsRestoreOptions
): Record<string, string> {
return {
env: opts.envId,
envtype: this.#envTypeToLabelValue(opts.envType),
org: opts.orgId,
project: opts.projectId,
};
}
#getResourcesFromMachineConfig(config: Machine): ComputeResources {
return {
cpu: `${config.cpu}`,
memory: `${config.memory}G`,
};
}
#getLifecycleCommand<THookType extends "postStart" | "preStop">(
type: THookType,
cause: THookType extends "postStart" ? PostStartCauses : PreStopCauses
) {
const retries = 5;
// This will retry sending the lifecycle hook up to `retries` times
// The sleep is required as this may start running before the HTTP server is up
const exec = [
"/bin/sh",
"-c",
`for i in $(seq ${retries}); do sleep 1; busybox wget -q -O- 127.0.0.1:8000/${type}?cause=${cause} && break; done`,
];
logger.debug("getLifecycleCommand()", { exec });
return exec;
}
#getIndexContainerName(suffix: string) {
return `task-index-${suffix}`;
}
#getRunContainerName(suffix: string) {
return `task-run-${suffix}`;
}
#createK8sApi() {
const kubeConfig = new k8s.KubeConfig();
if (RUNTIME_ENV === "local") {
kubeConfig.loadFromDefault();
} else if (RUNTIME_ENV === "kubernetes") {
kubeConfig.loadFromCluster();
} else {
throw new Error(`Unsupported runtime environment: ${RUNTIME_ENV}`);
}
return {
core: kubeConfig.makeApiClient(k8s.CoreV1Api),
batch: kubeConfig.makeApiClient(k8s.BatchV1Api),
};
}
async #createPod(pod: k8s.V1Pod, namespace: Namespace) {
try {
const res = await this.#k8sApi.core.createNamespacedPod(namespace.metadata.name, pod);
logger.debug(res.body);
} catch (err: unknown) {
this.#handleK8sError(err);
}
}
async #deletePod(opts: { runId: string; namespace: Namespace }) {
try {
const res = await this.#k8sApi.core.deleteNamespacedPod(
opts.runId,
opts.namespace.metadata.name
);
logger.debug(res.body);
} catch (err: unknown) {
this.#handleK8sError(err);
}
}
async #getPod(runId: string, namespace: Namespace) {
try {
const res = await this.#k8sApi.core.readNamespacedPod(runId, namespace.metadata.name);
logger.debug(res.body);
return res.body;
} catch (err: unknown) {
this.#handleK8sError(err);
}
}
async #createJob(job: k8s.V1Job, namespace: Namespace) {
try {
const res = await this.#k8sApi.batch.createNamespacedJob(namespace.metadata.name, job);
logger.debug(res.body);
} catch (err: unknown) {
this.#handleK8sError(err);
}
}
#throwUnlessRecord(candidate: unknown): asserts candidate is Record<string, unknown> {
if (typeof candidate !== "object" || candidate === null) {
throw candidate;
}
}
#handleK8sError(err: unknown) {
this.#throwUnlessRecord(err);
if ("body" in err && err.body) {
logger.error(err.body);
this.#throwUnlessRecord(err.body);
if (typeof err.body.message === "string") {
throw new Error(err.body?.message);
} else {
throw err.body;
}
} else {
logger.error(err);
throw err;
}
}
}
const provider = new ProviderShell({
tasks: new KubernetesTaskOperations(),
type: "kubernetes",
});
provider.listen();
const taskMonitor = new TaskMonitor({
runtimeEnv: RUNTIME_ENV,
onIndexFailure: async (deploymentId, failureInfo) => {
logger.log("Indexing failed", { deploymentId, failureInfo });
try {
provider.platformSocket.send("INDEXING_FAILED", {
deploymentId,
error: {
name: `Crashed with exit code ${failureInfo.exitCode}`,
message: failureInfo.reason,
stack: failureInfo.logs,
},
});
} catch (error) {
logger.error(error);
}
},
onRunFailure: async (runId, failureInfo) => {
logger.log("Run failed:", { runId, failureInfo });
try {
provider.platformSocket.send("WORKER_CRASHED", { runId, ...failureInfo });
} catch (error) {
logger.error(error);
}
},
});
taskMonitor.start();
+442
View File
@@ -0,0 +1,442 @@
import * as k8s from "@kubernetes/client-node";
import { SimpleLogger } from "@trigger.dev/core-apps";
import { setTimeout } from "timers/promises";
import PQueue from "p-queue";
type IndexFailureHandler = (
deploymentId: string,
failureInfo: {
exitCode: number;
reason: string;
logs: string;
}
) => Promise<any>;
type RunFailureHandler = (
runId: string,
failureInfo: {
exitCode: number;
reason: string;
logs: string;
}
) => Promise<any>;
type TaskMonitorOptions = {
runtimeEnv: "local" | "kubernetes";
onIndexFailure?: IndexFailureHandler;
onRunFailure?: RunFailureHandler;
namespace?: string;
};
export class TaskMonitor {
#enabled = false;
#logger = new SimpleLogger("[TaskMonitor]");
#taskInformer: ReturnType<typeof k8s.makeInformer<k8s.V1Pod>>;
#processedPods = new Map<string, number>();
#queue = new PQueue({ concurrency: 10 });
#k8sClient: {
core: k8s.CoreV1Api;
kubeConfig: k8s.KubeConfig;
};
private namespace = "default";
private fieldSelector = "status.phase=Failed";
private labelSelector = "app in (task-index, task-run)";
constructor(private opts: TaskMonitorOptions) {
this.#k8sClient = this.#createK8sClient();
this.#taskInformer = this.#createTaskInformer();
this.#taskInformer.on("connect", this.#onInformerConnected.bind(this));
this.#taskInformer.on("error", this.#onInformerError.bind(this));
this.#taskInformer.on("update", this.#enqueueOnPodUpdated.bind(this));
}
#createTaskInformer() {
const listTasks = () =>
this.#k8sClient.core.listNamespacedPod(
this.namespace,
undefined,
undefined,
undefined,
this.fieldSelector,
this.labelSelector
);
// Uses watch with local caching
// https://kubernetes.io/docs/reference/using-api/api-concepts/#efficient-detection-of-changes
const informer = k8s.makeInformer(
this.#k8sClient.kubeConfig,
`/api/v1/namespaces/${this.namespace}/pods`,
listTasks,
this.labelSelector,
this.fieldSelector
);
return informer;
}
async #onInformerConnected() {
this.#logger.log("Connected");
}
async #onInformerError(error: any) {
this.#logger.error("Error:", error);
// Automatic reconnect
await setTimeout(2_000);
this.#taskInformer.start();
}
#enqueueOnPodUpdated(pod: k8s.V1Pod) {
this.#queue.add(async () => {
try {
// It would be better to only pass the cache key, but the pod may already be removed from the cache by the time we process it
await this.#onPodUpdated(pod);
} catch (error) {
this.#logger.error("Caught onPodUpdated() error:", error);
}
});
}
async #onPodUpdated(pod: k8s.V1Pod) {
this.#logger.debug(`Updated: ${pod.metadata?.name}`);
this.#logger.debug("Updated", JSON.stringify(pod, null, 2));
// We only care about failures
if (pod.status?.phase !== "Failed") {
return;
}
const podName = pod.metadata?.name;
if (!podName) {
this.#logger.error("Pod is nameless", { pod });
return;
}
const containerStatus = pod.status.containerStatuses?.[0];
if (!containerStatus?.state) {
this.#logger.error("Pod failed, but container status doesn't have state", {
status: pod.status,
});
return;
}
if (this.#processedPods.has(podName)) {
this.#logger.debug("Pod update already processed", {
podName,
timestamp: this.#processedPods.get(podName),
});
return;
}
this.#processedPods.set(podName, Date.now());
const podStatus = this.#getPodStatusSummary(pod.status);
const containerState = this.#getContainerStateSummary(containerStatus.state);
const exitCode = containerState.exitCode ?? -1;
// We use this special exit code to signal any errors were already handled elsewhere
if (exitCode === 111) {
return;
}
const rawLogs = await this.#getLogTail(podName);
this.#logger.log(`${podName} failed with:`, {
podStatus,
containerState,
rawLogs,
});
const rawReason = podStatus.reason ?? containerState.reason ?? "";
const message = podStatus.message ?? containerState.message ?? "";
let reason = rawReason || "Unknown error";
let logs = rawLogs || "";
switch (rawReason) {
case "Error":
reason = "Unknown error.";
break;
case "Evicted":
if (message.startsWith("Pod ephemeral local storage usage")) {
reason = "Storage limit exceeded.";
} else if (message) {
reason = `Evicted: ${message}`;
} else {
reason = "Evicted for unknown reason.";
}
if (logs.startsWith("failed to try resolving symlinks")) {
logs = "";
}
break;
case "OOMKilled":
reason = "Out of memory! Try increasing the memory on this task.";
break;
default:
break;
}
const failureInfo = {
exitCode,
reason,
logs,
};
const app = pod.metadata?.labels?.app;
switch (app) {
case "task-index":
const deploymentId = pod.metadata?.labels?.deployment;
if (!deploymentId) {
this.#logger.error("Index is missing ID", { pod });
return;
}
if (this.opts.onIndexFailure) {
await this.opts.onIndexFailure(deploymentId, failureInfo);
}
break;
case "task-run":
const runId = pod.metadata?.labels?.run;
if (!runId) {
this.#logger.error("Run is missing ID", { pod });
return;
}
if (this.opts.onRunFailure) {
await this.opts.onRunFailure(runId, failureInfo);
}
break;
default:
this.#logger.error("Pod has invalid app label", { pod });
return;
}
await this.#deletePod(podName);
}
async #getLogTail(podName: string) {
try {
const logs = await this.#k8sClient.core.readNamespacedPodLog(
podName,
this.namespace,
undefined,
undefined,
undefined,
1024, // limitBytes
undefined,
undefined,
undefined,
20 // tailLines
);
const responseBody = logs.body ?? "";
if (responseBody.startsWith("unable to retrieve container logs")) {
return "";
}
// Type is wrong, body may be undefined
return responseBody;
} catch (error) {
this.#logger.error("Log tail error:", error instanceof Error ? error.message : "unknown");
return "";
}
}
#getPodStatusSummary(status: k8s.V1PodStatus) {
return {
reason: status.reason,
message: status.message,
};
}
#getContainerStateSummary(state: k8s.V1ContainerState) {
return {
reason: state.terminated?.reason,
exitCode: state.terminated?.exitCode,
message: state.terminated?.message,
};
}
#createK8sClient() {
const kubeConfig = new k8s.KubeConfig();
if (this.opts.runtimeEnv === "local") {
kubeConfig.loadFromDefault();
} else if (this.opts.runtimeEnv === "kubernetes") {
kubeConfig.loadFromCluster();
} else {
throw new Error(`Unsupported runtime environment: ${this.opts.runtimeEnv}`);
}
return {
core: kubeConfig.makeApiClient(k8s.CoreV1Api),
kubeConfig: kubeConfig,
};
}
#isRecord(candidate: unknown): candidate is Record<string, unknown> {
if (typeof candidate !== "object" || candidate === null) {
return false;
} else {
return true;
}
}
#logK8sError(err: unknown, debugOnly = false) {
if (debugOnly) {
this.#logger.debug("K8s API Error", err);
} else {
this.#logger.error("K8s API Error", err);
}
}
#handleK8sError(err: unknown) {
if (!this.#isRecord(err) || !this.#isRecord(err.body)) {
this.#logK8sError(err);
return;
}
this.#logK8sError(err, true);
if (typeof err.body.message === "string") {
this.#logK8sError({ message: err.body.message });
return;
}
this.#logK8sError({ body: err.body });
}
#printStats(includeMoreDetails = false) {
this.#logger.log("Stats:", {
cacheSize: this.#taskInformer.list().length,
totalProcessed: this.#processedPods.size,
...(includeMoreDetails && {
processedPods: this.#processedPods,
}),
});
}
async #deletePod(name: string) {
this.#logger.debug("Deleting pod:", name);
await this.#k8sClient.core
.deleteNamespacedPod(name, this.namespace)
.catch(this.#handleK8sError.bind(this));
}
async start() {
this.#enabled = true;
const interval = setInterval(() => {
if (!this.#enabled) {
clearInterval(interval);
return;
}
this.#printStats();
}, 300_000);
await this.#taskInformer.start();
// this.#launchTests();
}
async stop() {
if (!this.#enabled) {
return;
}
this.#enabled = false;
this.#logger.log("Shutting down..");
await this.#taskInformer.stop();
this.#printStats(true);
}
async #launchTests() {
const createPod = async (
container: k8s.V1Container,
name: string,
labels?: Record<string, string>
) => {
this.#logger.log("Creating pod:", name);
const pod = {
metadata: {
name,
labels,
},
spec: {
restartPolicy: "Never",
automountServiceAccountToken: false,
terminationGracePeriodSeconds: 1,
containers: [container],
},
} satisfies k8s.V1Pod;
await this.#k8sClient.core
.createNamespacedPod(this.namespace, pod)
.catch(this.#handleK8sError.bind(this));
};
const createOomPod = async (name: string, labels?: Record<string, string>) => {
const container = {
name,
image: "polinux/stress",
resources: {
limits: {
memory: "100Mi",
},
},
command: ["stress"],
args: ["--vm", "1", "--vm-bytes", "150M", "--vm-hang", "1"],
} satisfies k8s.V1Container;
await createPod(container, name, labels);
};
const createNonZeroExitPod = async (name: string, labels?: Record<string, string>) => {
const container = {
name,
image: "busybox",
command: ["sh"],
args: ["-c", "exit 1"],
} satisfies k8s.V1Container;
await createPod(container, name, labels);
};
const createOoDiskPod = async (name: string, labels?: Record<string, string>) => {
const container = {
name,
image: "busybox",
command: ["sh"],
args: [
"-c",
"echo creating huge-file..; head -c 1000m /dev/zero > huge-file; ls -lh huge-file; sleep infinity",
],
resources: {
limits: {
"ephemeral-storage": "500Mi",
},
},
} satisfies k8s.V1Container;
await createPod(container, name, labels);
};
await createNonZeroExitPod("non-zero-exit-task", { app: "task-run", run: "123" });
await createOomPod("oom-task", { app: "task-index", deployment: "456" });
await createOoDiskPod("ood-task", { app: "task-run", run: "abc" });
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"strict": true,
"skipLibCheck": true,
"paths": {
"@trigger.dev/core/v3": ["../../packages/core/src/v3"],
"@trigger.dev/core/v3/*": ["../../packages/core/src/v3/*"],
"@trigger.dev/core-apps": ["../../packages/core-apps/src"],
"@trigger.dev/core-apps/*": ["../../packages/core-apps/src/*"]
}
}
}
-49
View File
@@ -1,49 +0,0 @@
import type { StorybookConfig } from "@storybook/react-webpack5";
import path from "path";
const root = path.resolve(__dirname, "../app");
const config: StorybookConfig = {
webpackFinal: async (config) => {
return {
...config,
resolve: {
...config.resolve,
alias: {
...(config.resolve?.alias ?? {}),
"~": root,
},
extensions: [
...(config.resolve?.extensions ?? []),
...[".ts", ".tsx", ".js", ".jsx", ".mdx"],
],
},
};
},
stories: ["../app/**/stories/*.mdx", "../app/**/stories/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
"storybook-addon-variants",
"storybook-addon-designs",
"@storybook/addon-docs",
{
name: "@storybook/addon-styling",
options: {
// Check out https://github.com/storybookjs/addon-styling/blob/main/docs/api.md
// For more details on this addon's options.
postCss: true,
},
},
],
framework: {
name: "@storybook/react-webpack5",
options: {},
},
docs: {
autodocs: "tag",
},
staticDirs: [path.resolve("public")],
};
export default config;
-50
View File
@@ -1,50 +0,0 @@
import type { Preview } from "@storybook/react";
import "../app/tailwind.css";
import { createRemixStub } from "@remix-run/testing";
import React from "react";
import { LocaleContextProvider } from "../app/components/primitives/LocaleProvider";
import { OperatingSystemContextProvider } from "../app/components/primitives/OperatingSystemProvider";
const preview: Preview = {
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
backgrounds: {
default: "App background",
values: [
{
name: "App background",
value: "#0B1018",
},
],
},
layout: "fullscreen",
},
decorators: [
(Story) => {
const RemixStub = createRemixStub([
{
path: "/*",
action: () => ({ redirect: "/" }),
loader: () => ({ redirect: "/" }),
Component: Story,
},
]);
return (
<OperatingSystemContextProvider platform="mac">
<LocaleContextProvider locales={window.navigator.languages as string[]}>
<RemixStub initialEntries={["/"]} />
</LocaleContextProvider>
</OperatingSystemContextProvider>
);
},
],
};
export default preview;
@@ -0,0 +1,53 @@
export function AISparkleIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M14.9806 0.803884C14.8871 0.33646 14.4767 0 14 0C13.5233 0 13.1129 0.33646 13.0194 0.803884L12.7809 1.99644C12.7017 2.3923 12.3923 2.70174 11.9964 2.78091L10.8039 3.01942C10.3365 3.1129 10 3.52332 10 4C10 4.47668 10.3365 4.8871 10.8039 4.98058L11.9964 5.21909C12.3923 5.29826 12.7017 5.6077 12.7809 6.00356L13.0194 7.19612C13.1129 7.66354 13.5233 8 14 8C14.4767 8 14.8871 7.66354 14.9806 7.19612L15.2191 6.00356C15.2983 5.6077 15.6077 5.29826 16.0036 5.21909L17.1961 4.98058C17.6635 4.8871 18 4.47668 18 4C18 3.52332 17.6635 3.1129 17.1961 3.01942L16.0036 2.78091C15.6077 2.70174 15.2983 2.3923 15.2191 1.99644L14.9806 0.803884Z"
fill="url(#paint0_linear_11402_36656)"
/>
<path
d="M5.94868 4.68377C5.81257 4.27543 5.43043 4 5 4C4.56957 4 4.18743 4.27543 4.05132 4.68377L3.36754 6.73509C3.26801 7.03369 3.03369 7.26801 2.73509 7.36754L0.683772 8.05132C0.27543 8.18743 0 8.56957 0 9C0 9.43043 0.27543 9.81257 0.683772 9.94868L2.73509 10.6325C3.03369 10.732 3.26801 10.9663 3.36754 11.2649L4.05132 13.3162C4.18743 13.7246 4.56957 14 5 14C5.43043 14 5.81257 13.7246 5.94868 13.3162L6.63246 11.2649C6.73199 10.9663 6.96631 10.732 7.26491 10.6325L9.31623 9.94868C9.72457 9.81257 10 9.43043 10 9C10 8.56957 9.72457 8.18743 9.31623 8.05132L7.26491 7.36754C6.96631 7.26801 6.73199 7.03369 6.63246 6.73509L5.94868 4.68377Z"
fill="url(#paint1_linear_11402_36656)"
/>
<path
d="M12.9487 12.6838C12.8126 12.2754 12.4304 12 12 12C11.5696 12 11.1874 12.2754 11.0513 12.6838L10.8675 13.2351C10.768 13.5337 10.5337 13.768 10.2351 13.8675L9.68377 14.0513C9.27543 14.1874 9 14.5696 9 15C9 15.4304 9.27543 15.8126 9.68377 15.9487L10.2351 16.1325C10.5337 16.232 10.768 16.4663 10.8675 16.7649L11.0513 17.3162C11.1874 17.7246 11.5696 18 12 18C12.4304 18 12.8126 17.7246 12.9487 17.3162L13.1325 16.7649C13.232 16.4663 13.4663 16.232 13.7649 16.1325L14.3162 15.9487C14.7246 15.8126 15 15.4304 15 15C15 14.5696 14.7246 14.1874 14.3162 14.0513L13.7649 13.8675C13.4663 13.768 13.232 13.5337 13.1325 13.2351L12.9487 12.6838Z"
fill="url(#paint2_linear_11402_36656)"
/>
<defs>
<linearGradient
id="paint0_linear_11402_36656"
x1="9"
y1="0"
x2="9"
y2="18"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#E543FF" />
<stop offset="1" stopColor="#286399" />
</linearGradient>
<linearGradient
id="paint1_linear_11402_36656"
x1="9"
y1="0"
x2="9"
y2="18"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#E543FF" />
<stop offset="1" stopColor="#286399" />
</linearGradient>
<linearGradient
id="paint2_linear_11402_36656"
x1="9"
y1="0"
x2="9"
y2="18"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#E543FF" />
<stop offset="1" stopColor="#286399" />
</linearGradient>
</defs>
</svg>
);
}
@@ -0,0 +1,29 @@
export function AttemptIcon({ className }: { className?: string }) {
return (
<svg
className={className}
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_9964_113464)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M16 0H0V16H16V0ZM7.09906 4.4L4.53906 11.5H6.11906L6.63906 10H9.35906L9.87906 11.5H11.4591L8.89906 4.4H7.09906ZM7.99906 6L8.92906 8.73H7.06906L7.99906 6Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_9964_113464">
<path
d="M0 2C0 0.895431 0.895431 0 2 0H14C15.1046 0 16 0.895431 16 2V14C16 15.1046 15.1046 16 14 16H2C0.895431 16 0 15.1046 0 14V2Z"
fill="white"
/>
</clipPath>
</defs>
</svg>
);
}
+14
View File
@@ -0,0 +1,14 @@
export function ExitIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="3.5" y1="8" x2="11.5" y2="8" stroke="currentColor" strokeLinecap="round" />
<line x1="15.5" y1="1.5" x2="15.5" y2="14.5" stroke="currentColor" strokeLinecap="round" />
<path
d="M8.5 4.5L12 8L8.5 11.5"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
@@ -0,0 +1,25 @@
export function ShowParentIcon({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
d="M3.125 16H3.625H4.125V15H3.125H2C1.89733 15 1.8 14.9848 1.70919 14.9573L1.19765 14.8024L1.04267 14.2908C1.01515 14.2 1 14.1027 1 14V12.875V11.875H0V12.375V12.875V14C0 14.202 0.0299409 14.397 0.0856265 14.5808C0.135047 14.7439 0.204744 14.8982 0.291786 15.0407C0.363386 15.158 0.446722 15.2673 0.540161 15.3671C0.570065 15.399 0.601004 15.4299 0.632924 15.4598C0.732663 15.5533 0.841982 15.6366 0.95925 15.7082C1.10181 15.7953 1.25612 15.865 1.41923 15.9144C1.60303 15.9701 1.79802 16 2 16H3.125ZM10.125 15V16H9.625H9.125H6.875H6.375H5.875V15H6.875H9.125H10.125ZM15 5.875H16V6.375V6.875V9.125V9.625V10.125H15V9.125V6.875V5.875ZM5.875 1V0H6.375H6.875H9.125H9.625H10.125V1H9.125H6.875H5.875ZM1 10.125H0V9.625V9.125V6.875V6.375V5.875H1V6.875V9.125V10.125ZM0 3.625V4.125H1V3.125V2C1 1.89733 1.01515 1.8 1.04267 1.70919L1.19765 1.19765L1.70919 1.04267C1.8 1.01515 1.89733 1 2 1H3.125H4.125V0H3.625H3.125H2C1.79802 0 1.60303 0.0299409 1.41923 0.0856264C1.25612 0.135047 1.10181 0.204744 0.95925 0.291786C0.841983 0.363386 0.732663 0.446722 0.632924 0.540161C0.601004 0.570065 0.570065 0.601004 0.540161 0.632924C0.446722 0.732663 0.363386 0.841982 0.291786 0.95925C0.204744 1.10181 0.135047 1.25612 0.0856265 1.41923C0.0299409 1.60303 0 1.79802 0 2V3.125V3.625ZM12.375 0H11.875V1H12.875H14C14.1027 1 14.2 1.01515 14.2908 1.04267L14.8024 1.19765L14.9573 1.70919C14.9848 1.8 15 1.89733 15 2V3.125V4.125H16V3.625V3.125V2C16 1.79802 15.9701 1.60303 15.9144 1.41923C15.865 1.25612 15.7953 1.10181 15.7082 0.95925C15.6366 0.841982 15.5533 0.732663 15.4598 0.632924C15.4299 0.601004 15.399 0.570065 15.3671 0.540161C15.2673 0.446722 15.158 0.363386 15.0407 0.291786C14.8982 0.204744 14.7439 0.135047 14.5808 0.0856265C14.397 0.0299409 14.202 0 14 0H12.875H12.375ZM16 12.375V11.875H15V12.875V14C15 14.1027 14.9848 14.2 14.9573 14.2908L14.8024 14.8024L14.2908 14.9573C14.2 14.9848 14.1027 15 14 15H12.875H11.875V16H12.375H12.875H14C14.202 16 14.397 15.9701 14.5808 15.9144C14.7439 15.865 14.8982 15.7953 15.0407 15.7082C15.158 15.6366 15.2673 15.5533 15.3671 15.4598C15.399 15.4299 15.4299 15.399 15.4598 15.3671C15.5533 15.2673 15.6366 15.158 15.7082 15.0407C15.7953 14.8982 15.865 14.7439 15.9144 14.5808C15.9701 14.397 16 14.202 16 14V12.875V12.375ZM8.75 6.26758L10.4523 7.96991C10.7452 8.26281 11.2201 8.26281 11.513 7.96991C11.8059 7.67702 11.8059 7.20215 11.513 6.90925L8.59099 3.98725C8.43431 3.83057 8.22556 3.7577 8.02045 3.76865C7.81604 3.75834 7.60821 3.83124 7.45209 3.98736L4.53009 6.90937C4.23719 7.20226 4.23719 7.67713 4.53009 7.97003C4.82298 8.26292 5.29785 8.26292 5.59075 7.97003L7.25 6.31077L7.25 11.25C7.25 11.6642 7.58579 12 8 12C8.41421 12 8.75 11.6642 8.75 11.25L8.75 6.26758Z"
/>
</svg>
);
}
export function ShowParentIconSelected({ className }: { className?: string }) {
return (
<svg className={className} viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
d="M2 0C0.895431 0 0 0.895431 0 2V14C0 15.1046 0.895431 16 2 16H14C15.1046 16 16 15.1046 16 14V2C16 0.895431 15.1046 0 14 0H2ZM8.75 6.26758L10.4523 7.96991C10.7452 8.26281 11.2201 8.26281 11.513 7.96991C11.8059 7.67702 11.8059 7.20215 11.513 6.90925L8.59099 3.98725C8.43344 3.8297 8.22324 3.7569 8.01703 3.76884C7.8138 3.75958 7.60752 3.83254 7.45234 3.98773L4.53033 6.90973C4.23744 7.20263 4.23744 7.6775 4.53033 7.97039C4.82322 8.26329 5.2981 8.26329 5.59099 7.97039L7.25 6.31138L7.25 11.25C7.25 11.6642 7.58579 12 8 12C8.41421 12 8.75 11.6642 8.75 11.25L8.75 6.26758Z"
/>
</svg>
);
}
+29
View File
@@ -0,0 +1,29 @@
export function TaskIcon({ className }: { className?: string }) {
return (
<svg
className={className}
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_9221_99732)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M16 0H0V16H16V0ZM10.8901 5.73995V4.44995H5.11011V5.73995H7.23011V11.55H8.77011V5.73995H10.8901Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_9221_99732">
<path
d="M0 2C0 0.895431 0.895431 0 2 0H14C15.1046 0 16 0.895431 16 2V14C16 15.1046 15.1046 16 14 16H2C0.895431 16 0 15.1046 0 14V2Z"
fill="white"
/>
</clipPath>
</defs>
</svg>
);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

@@ -0,0 +1,21 @@
export function ATAndTLogo({ className }: { className?: string }) {
return (
<svg
className={className}
width="66"
height="33"
viewBox="0 0 66 33"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M57.0405 21.156C57.0064 21.1581 56.9723 21.1529 56.9403 21.1408C56.9084 21.1287 56.8794 21.1099 56.8552 21.0858C56.8311 21.0616 56.8123 21.0326 56.8002 21.0007C56.7881 20.9687 56.7829 20.9346 56.785 20.9005V13.4102H54.2407C54.2068 13.4114 54.173 13.4056 54.1415 13.3932C54.1099 13.3808 54.0813 13.362 54.0573 13.338C54.0333 13.3141 54.0145 13.2854 54.0021 13.2538C53.9897 13.2223 53.984 13.1885 53.9852 13.1546V12.1226C53.9839 12.0887 53.9896 12.0549 54.002 12.0233C54.0143 11.9916 54.0331 11.9629 54.0571 11.9389C54.0811 11.9148 54.1098 11.896 54.1414 11.8836C54.173 11.8711 54.2068 11.8653 54.2407 11.8666H61.0127C61.0468 11.8646 61.0809 11.8698 61.1129 11.882C61.1448 11.8942 61.1738 11.913 61.1979 11.9372C61.222 11.9614 61.2407 11.9904 61.2527 12.0224C61.2647 12.0544 61.2698 12.0886 61.2677 12.1226V13.1557C61.2689 13.1896 61.2632 13.2233 61.2508 13.2548C61.2384 13.2863 61.2197 13.315 61.1958 13.3389C61.1718 13.3629 61.1432 13.3817 61.1118 13.3941C61.0803 13.4066 61.0465 13.4124 61.0127 13.4112H58.4684V20.9005C58.4696 20.9344 58.4639 20.9681 58.4515 20.9997C58.4391 21.0313 58.4203 21.0599 58.3963 21.0839C58.3723 21.1079 58.3437 21.1266 58.3121 21.1391C58.2805 21.1515 58.2468 21.1572 58.2129 21.156H57.0394M37.5459 17.4163L36.2102 13.5845L34.8629 17.4163H37.5459ZM40.5081 20.8528C40.5662 21.0041 40.4735 21.1555 40.3107 21.1555H39.1087C39.0327 21.1609 38.9572 21.1394 38.8955 21.0947C38.8338 21.0499 38.7899 20.9848 38.7715 20.9109L38.0861 18.9369H34.3343L33.6489 20.9109C33.6305 20.9847 33.5867 21.0498 33.5251 21.0945C33.4635 21.1392 33.3881 21.1608 33.3122 21.1555H32.1744C32.0231 21.1555 31.9183 21.0041 31.977 20.8528L35.1245 12.0985C35.1826 11.9357 35.2873 11.866 35.4612 11.866H37.0184C37.1928 11.866 37.3091 11.9351 37.3672 12.0985L40.5147 20.8528M49.5381 19.9014C50.2811 19.9014 50.7812 19.5422 51.1875 18.9265L49.3067 16.9058C48.5862 17.3127 48.1212 17.7185 48.1212 18.5316C48.1212 19.3322 48.7716 19.9025 49.5387 19.9025M50.0613 13.1031C49.4581 13.1031 49.1088 13.4869 49.1088 13.9969C49.1088 14.3917 49.3172 14.7399 49.7942 15.2509C50.6189 14.7739 50.9677 14.4843 50.9677 13.9733C50.9677 13.4962 50.6661 13.096 50.0618 13.096M55.2025 20.8303C55.3533 20.9931 55.2606 21.1555 55.0742 21.1555H53.5937C53.511 21.1627 53.4278 21.1483 53.3524 21.1136C53.277 21.0788 53.212 21.025 53.1638 20.9575L52.2864 19.9826C51.6942 20.7722 50.869 21.3644 49.4981 21.3644C47.8021 21.3644 46.4658 20.3428 46.4658 18.5898C46.4658 17.2425 47.1863 16.5225 48.2786 15.9194C47.744 15.3041 47.5 14.6538 47.5 14.0852C47.5 12.6447 48.5106 11.6582 50.0316 11.6582C51.5889 11.6582 52.5408 12.5761 52.5408 13.9338C52.5408 15.0952 51.7046 15.7444 50.8218 16.2325L52.123 17.6379L52.8545 16.3602C52.9477 16.2094 53.0519 16.1519 53.2383 16.1519H54.3646C54.5511 16.1519 54.6553 16.2802 54.5401 16.477L53.2449 18.706L55.208 20.8314M43.6161 21.1566C43.65 21.1579 43.6838 21.1521 43.7155 21.1398C43.7471 21.1274 43.7758 21.1086 43.7998 21.0846C43.8239 21.0607 43.8427 21.032 43.8552 21.0004C43.8676 20.9688 43.8734 20.935 43.8721 20.901V13.4107H46.4164C46.4503 13.4119 46.4841 13.4062 46.5157 13.3938C46.5472 13.3813 46.5759 13.3626 46.5999 13.3386C46.6238 13.3146 46.6426 13.2859 46.655 13.2544C46.6674 13.2228 46.6732 13.1891 46.672 13.1552V12.1226C46.6733 12.0887 46.6676 12.0549 46.6552 12.0233C46.6428 11.9916 46.624 11.9629 46.6001 11.9389C46.5761 11.9148 46.5474 11.896 46.5158 11.8836C46.4842 11.8711 46.4504 11.8653 46.4164 11.8666H39.6406C39.6067 11.8653 39.5728 11.8711 39.5412 11.8836C39.5097 11.896 39.481 11.9148 39.457 11.9389C39.433 11.9629 39.4142 11.9916 39.4019 12.0233C39.3895 12.0549 39.3838 12.0887 39.3851 12.1226V13.1557C39.3838 13.1896 39.3896 13.2234 39.402 13.2549C39.4144 13.2865 39.4332 13.3152 39.4572 13.3391C39.4812 13.3631 39.5098 13.3819 39.5414 13.3943C39.5729 13.4067 39.6067 13.4125 39.6406 13.4112H42.1849V20.9005C42.1837 20.9344 42.1894 20.9681 42.2018 20.9997C42.2143 21.0313 42.233 21.0599 42.257 21.0839C42.281 21.1079 42.3096 21.1266 42.3412 21.1391C42.3728 21.1515 42.4065 21.1572 42.4404 21.156L43.6161 21.1566Z"
fill="currentColor"
/>
<path
d="M9.2351 25.6737C11.2728 27.2567 13.7799 28.1157 16.3602 28.1149C19.296 28.1149 21.9725 27.0248 24.0151 25.2361C24.0397 25.2142 24.0277 25.1999 24.003 25.2142C23.0862 25.8261 20.4739 27.1624 16.3602 27.1624C12.7851 27.1624 10.5259 26.3646 9.2499 25.6529C9.22523 25.6408 9.217 25.6583 9.23455 25.6748M17.1487 26.2686C20.0083 26.2686 23.1503 25.4889 25.0295 23.9464C25.5438 23.5258 26.033 22.9665 26.4716 22.2148C26.74 21.7483 26.9742 21.263 27.1724 20.7628C27.1812 20.7381 27.1669 20.726 27.1477 20.754C25.4002 23.3306 20.339 24.9296 15.1188 24.9296C11.4252 24.9296 7.45135 23.7485 5.89571 21.4931C5.88035 21.4723 5.865 21.4811 5.87377 21.5052C7.3181 24.5863 11.7152 26.2686 17.1476 26.2686M14.0232 21.1581C8.07591 21.1581 5.2717 18.389 4.76338 16.4983C4.7579 16.4709 4.73926 16.4764 4.73926 16.5016C4.73926 17.1377 4.80287 17.9591 4.91253 18.5041C4.96463 18.7695 5.1867 19.1857 5.49706 19.5186C6.9392 21.0166 10.5286 23.1212 16.7468 23.1212C25.2187 23.1212 27.1554 20.2994 27.5508 19.3705C27.8337 18.7125 27.9801 17.5073 27.9801 16.5C27.9803 16.29 27.9751 16.0801 27.9648 15.8705C27.9648 15.8392 27.9467 15.8376 27.9406 15.8672C27.5173 18.1373 20.2792 21.157 14.0249 21.157M5.85458 11.5172C5.49236 12.2876 5.21385 13.0946 5.02385 13.9244C4.96901 14.1766 4.99533 14.2989 5.07868 14.4876C5.79152 15.9999 9.39686 18.4192 17.8068 18.4192C22.9376 18.4192 26.9235 17.158 27.5689 14.8582C27.6878 14.4349 27.6939 13.9875 27.5414 13.3854C27.3769 12.712 27.0512 11.9268 26.7803 11.3757C26.7716 11.3576 26.7557 11.3604 26.7584 11.3812C26.8588 14.3982 18.4456 16.3426 14.2003 16.3426C9.60194 16.3426 5.76685 14.5111 5.76685 12.1971C5.76685 11.9751 5.81291 11.7585 5.87652 11.521C5.882 11.4991 5.86445 11.4964 5.85458 11.5156M24.0348 7.81261C24.0859 7.89265 24.1114 7.9864 24.1077 8.0813C24.1077 9.37209 20.158 11.6548 13.8702 11.6548C9.25045 11.6548 8.38572 9.94127 8.38572 8.85117C8.38572 8.46733 8.53487 8.0632 8.86442 7.65798C8.88252 7.63385 8.86716 7.62508 8.84633 7.64262C8.24498 8.15186 7.6971 8.72105 7.21118 9.34138C6.97978 9.63365 6.83666 9.89246 6.83666 10.0476C6.83666 12.3068 12.4999 13.9441 17.7958 13.9441C23.4437 13.9441 25.9562 12.1017 25.9562 10.4896C25.9562 9.91111 25.7369 9.57388 25.1556 8.91861C24.7816 8.49255 24.428 8.15094 24.0589 7.80438C24.0408 7.78958 24.0282 7.80164 24.0408 7.81974M22.3108 6.52949C20.5693 5.48545 18.547 4.8916 16.3668 4.8916C14.1713 4.8916 12.0887 5.50574 10.3351 6.57775C9.81086 6.90017 9.51585 7.15899 9.51585 7.49128C9.51585 8.47117 11.8057 9.52453 15.8673 9.52453C19.8866 9.52453 23.005 8.37082 23.005 7.25988C23.005 6.99503 22.7731 6.80915 22.3042 6.52949"
fill="currentColor"
/>
</svg>
);
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,50 @@
export function CalComLogo({ className }: { className?: string }) {
return (
<svg
className={className}
width="64"
height="15"
viewBox="0 0 64 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_9632_116886)">
<path
d="M6.38629 13.7111C2.75645 13.7111 0.0224609 10.8382 0.0224609 7.29138C0.0224609 3.73276 2.61685 0.836182 6.38629 0.836182C8.38733 0.836182 9.77174 1.45096 10.8537 2.85787L9.1086 4.31207C8.37568 3.53177 7.49149 3.14162 6.38629 3.14162C3.93149 3.14162 2.58194 5.02143 2.58194 7.29138C2.58194 9.56139 4.05946 11.4057 6.38629 11.4057C7.47984 11.4057 8.41061 11.0156 9.14353 10.2353L10.8654 11.7486C9.82995 13.0963 8.41061 13.7111 6.38629 13.7111Z"
fill="currentColor"
/>
<path
d="M18.3806 4.26438H20.7307V13.4861H18.3806V12.1383C17.892 13.096 17.0776 13.7344 15.5187 13.7344C13.029 13.7344 11.0396 11.5709 11.0396 8.91074C11.0396 6.2506 13.029 4.08704 15.5187 4.08704C17.066 4.08704 17.892 4.72547 18.3806 5.68311V4.26438ZM18.4504 8.91074C18.4504 7.46837 17.4615 6.27425 15.9026 6.27425C14.4018 6.27425 13.4245 7.48014 13.4245 8.91074C13.4245 10.3058 14.4018 11.5472 15.9026 11.5472C17.4499 11.5472 18.4504 10.3413 18.4504 8.91074Z"
fill="currentColor"
/>
<path d="M22.395 0.540283H24.7451V13.4744H22.395V0.540283Z" fill="currentColor" />
<path
d="M25.791 12.2569C25.791 11.5003 26.396 10.8618 27.222 10.8618C28.048 10.8618 28.6298 11.5003 28.6298 12.2569C28.6298 13.0372 28.0364 13.6756 27.222 13.6756C26.4076 13.6756 25.791 13.0372 25.791 12.2569Z"
fill="currentColor"
/>
<path
d="M37.6222 11.9965C36.7496 13.0605 35.4234 13.7344 33.8527 13.7344C31.0489 13.7344 28.9897 11.5709 28.9897 8.91074C28.9897 6.2506 31.0489 4.08704 33.8527 4.08704C35.3651 4.08704 36.6798 4.72547 37.5524 5.7304L35.7375 7.25553C35.2837 6.68804 34.6904 6.26242 33.8527 6.26242C32.352 6.26242 31.3747 7.46837 31.3747 8.8989C31.3747 10.3294 32.352 11.5354 33.8527 11.5354C34.7602 11.5354 35.3884 11.0625 35.8538 10.424L37.6222 11.9965Z"
fill="currentColor"
/>
<path
d="M37.8208 8.91074C37.8208 6.2506 39.88 4.08704 42.6838 4.08704C45.4876 4.08704 47.5468 6.2506 47.5468 8.91074C47.5468 11.5709 45.4876 13.7344 42.6838 13.7344C39.88 13.7225 37.8208 11.5709 37.8208 8.91074ZM45.1618 8.91074C45.1618 7.46837 44.1846 6.27425 42.6838 6.27425C41.183 6.26242 40.2058 7.46837 40.2058 8.91074C40.2058 10.3413 41.183 11.5472 42.6838 11.5472C44.1846 11.5472 45.1618 10.3413 45.1618 8.91074Z"
fill="currentColor"
/>
<path
d="M63.4389 7.84684V13.4745H61.089V8.42614C61.089 6.83009 60.3444 6.14437 59.2276 6.14437C58.1805 6.14437 57.4359 6.66457 57.4359 8.42614V13.4745H55.0859V8.42614C55.0859 6.83009 54.3296 6.14437 53.2244 6.14437C52.1774 6.14437 51.2582 6.66457 51.2582 8.42614V13.4745H48.9082V4.25273H51.2582V5.52958C51.7469 4.53647 52.6311 4.03992 53.9923 4.03992C55.2837 4.03992 56.3656 4.67834 56.9589 5.75421C57.5523 4.6547 58.4248 4.03992 60.007 4.03992C61.9383 4.05174 63.4389 5.51776 63.4389 7.84684Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_9632_116886">
<rect
width="63.9027"
height="13.9194"
fill="currentColor"
transform="translate(0.0224609 0.540283)"
/>
</clipPath>
</defs>
</svg>
);
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,39 @@
export function ShopifyLogo({ className }: { className?: string }) {
return (
<svg
className={className}
width="85"
height="25"
viewBox="0 0 85 25"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_9632_116870)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.6613 1.00225C10.5632 0.945637 10.4859 0.900988 10.3639 0.900988C8.19769 0.900988 7.03127 3.73373 6.44806 5.40004L8.28101 4.90015C8.7809 2.5673 9.78069 1.4842 10.7805 1.06762C10.7358 1.0453 10.6972 1.02297 10.6613 1.00225ZM11.6136 3.73373C11.6136 2.81725 11.447 2.23404 11.2804 1.73415C10.5305 2.06741 9.69738 2.81725 9.19748 4.56688L11.6136 3.90036V3.73373ZM12.3635 3.56709L13.4466 3.23383C13.1966 2.40067 12.6967 1.65083 12.1135 1.4842C12.2802 1.98409 12.3635 2.5673 12.3635 3.31715V3.56709ZM18.6955 4.40025C18.7788 4.40025 18.8621 4.48357 18.8621 4.56688C18.8621 4.73352 21.3616 21.7299 21.3616 21.7299L14.1964 23.3129L0.949219 20.8135C0.949219 20.8135 2.61553 7.64957 2.69885 7.23299C2.70036 7.22241 2.70184 7.212 2.7033 7.20175C2.78239 6.64673 2.79425 6.56344 3.44869 6.39983C3.64023 6.33599 3.82633 6.27214 4.00838 6.20968C4.53904 6.02763 5.03532 5.85737 5.53159 5.7333C6.03148 3.81704 7.44785 0.0678302 10.4472 0.151146C10.8638 0.151146 11.3637 0.317778 11.697 0.817672H11.7803C13.03 0.817672 13.7799 1.90078 14.1964 3.0672C14.613 2.90057 14.863 2.81725 14.863 2.81725C14.8796 2.81725 14.9029 2.81392 14.9309 2.80992C15.0429 2.79392 15.2295 2.76726 15.3629 2.90057C15.5295 2.98388 16.7792 4.23362 16.7792 4.23362C16.7792 4.23362 18.5289 4.40025 18.6955 4.40025ZM10.8622 11.0656L11.7786 8.39946C11.7786 8.39946 11.1954 7.98288 9.77906 8.06619C6.69638 8.06619 5.11338 10.1491 5.11338 12.3153C5.11338 13.764 5.92592 14.4263 6.63951 15.008C7.19816 15.4633 7.69617 15.8693 7.69617 16.5644C7.69617 16.981 7.44622 17.4809 6.7797 17.4809C5.77991 17.4809 4.61348 16.4811 4.61348 16.4811L4.03027 18.4807C4.03027 18.4807 5.19669 19.897 7.44622 19.897C9.36249 19.897 10.7789 18.4807 10.7789 16.2311C10.7789 14.5056 9.60415 13.6457 8.68583 12.9735C8.09656 12.5421 7.61285 12.188 7.61285 11.7321C7.61285 11.4821 7.69617 10.649 9.11254 10.649C10.1123 10.649 10.8622 11.0656 10.8622 11.0656Z"
fill="currentColor"
/>
<path
d="M18.6111 4.40038C18.5277 4.40038 16.7781 4.23375 16.7781 4.23375C16.7781 4.23375 15.5284 2.98401 15.3617 2.9007C15.2784 2.81738 15.2784 2.81738 15.1951 2.81738L14.1953 23.3131L21.3605 21.7301C21.3605 21.7301 18.861 4.73365 18.861 4.56701C18.861 4.4837 18.6944 4.40038 18.6111 4.40038Z"
fill="currentColor"
/>
<path
d="M29.7751 13.3151C29.0253 12.8985 28.692 12.5653 28.692 12.1487C28.692 11.5655 29.1919 11.2322 30.0251 11.2322C30.9416 11.2322 31.7747 11.6488 31.7747 11.6488L32.4412 9.64922C32.4412 9.64922 31.858 9.14932 30.1084 9.14932C27.6089 9.14932 25.9426 10.5657 25.9426 12.5653C25.9426 13.7317 26.7758 14.5648 27.7756 15.1481C28.6087 15.648 28.942 15.9812 28.942 16.4811C28.942 16.981 28.5254 17.4809 27.6922 17.4809C26.5258 17.4809 25.3594 16.8977 25.3594 16.8977L24.6929 18.8973C24.6929 18.8973 25.6927 19.5638 27.4423 19.5638C29.9418 19.5638 31.7747 18.3141 31.7747 16.0645C31.7747 14.8148 30.8582 13.8983 29.7751 13.3151ZM39.8563 9.06601C38.6066 9.06601 37.6068 9.64922 36.857 10.5657L37.9401 4.90022H35.1073L32.3579 19.3138H35.1907L36.1071 14.3982C36.4404 12.5653 37.4402 11.3988 38.3567 11.3988C39.0232 11.3988 39.2731 11.8154 39.2731 12.482C39.2731 12.8985 39.2731 13.3151 39.1898 13.7317L38.1067 19.3972H40.9395L42.0226 13.5651C42.1059 12.9818 42.1892 12.232 42.1892 11.7321C42.1892 10.0658 41.356 9.06601 39.8563 9.06601ZM47.1048 17.3143C46.105 17.3143 45.7718 16.4811 45.7718 15.4813C45.7718 13.8983 46.6049 11.2322 48.1046 11.2322C49.1044 11.2322 49.4377 12.0654 49.4377 12.8985C49.4377 14.7315 48.6045 17.3143 47.1048 17.3143ZM48.5212 9.06601C45.1052 9.06601 42.8557 12.1487 42.8557 15.5646C42.8557 17.7308 44.1888 19.4805 46.7716 19.4805C50.1042 19.4805 52.3537 16.4811 52.3537 12.9818C52.3537 10.9823 51.1873 9.06601 48.5212 9.06601ZM56.8528 17.3976C56.1029 17.3976 55.6863 16.981 55.6863 16.981L56.1862 14.3149C56.5195 12.5653 57.436 11.3988 58.4358 11.3988C59.2689 11.3988 59.6022 12.232 59.6022 12.9818C59.5189 14.8148 58.4358 17.3976 56.8528 17.3976ZM59.6022 9.06601C57.6859 9.06601 56.6028 10.7323 56.6028 10.7323L56.7695 9.23264H54.27C54.1867 10.2324 53.9367 11.8154 53.6868 12.9818L51.6872 23.313H54.5199L55.2698 19.1472H55.3531C55.3531 19.1472 55.9363 19.4805 57.0194 19.4805C60.352 19.4805 62.5182 16.0645 62.5182 12.6486C62.5182 10.8156 61.6851 9.06601 59.6022 9.06601ZM66.5174 5.06685C65.6009 5.06685 64.9344 5.81669 64.9344 6.73316C64.9344 7.56632 65.4343 8.14953 66.2675 8.14953C67.1839 8.14953 67.9338 7.56632 67.9338 6.48322C67.9338 5.65006 67.3506 5.06685 66.5174 5.06685ZM62.6016 19.3138H65.4343L67.3506 9.31595H64.5178L62.6016 19.3138ZM74.5157 9.31595H72.5161L72.5995 8.81606C72.7661 7.81627 73.3493 6.98311 74.2658 6.98311C74.7657 6.98311 75.1822 7.14974 75.1822 7.14974L75.7655 4.90022C75.7655 4.90022 75.2656 4.65027 74.2658 4.65027C73.266 4.65027 72.2662 4.90022 71.5163 5.56674C70.5166 6.3999 70.1 7.56632 69.85 8.73274L69.7667 9.23264H68.4337L68.0171 11.3988H69.3501L67.8505 19.3138H70.6832L72.1829 11.3988H74.0991L74.5157 9.31595ZM81.2643 9.31595C81.2643 9.31595 79.5147 13.7317 78.6815 16.1478C78.5982 15.398 78.015 9.31595 78.015 9.31595H75.0156L76.6819 18.4807C76.6819 18.6473 76.6819 18.814 76.5986 18.9806C76.2654 19.6471 75.6821 20.2303 75.0989 20.6469C74.599 21.0635 73.9325 21.3134 73.5159 21.4801L74.2658 23.8962C74.849 23.8129 76.0154 23.313 77.0152 22.3965C78.2649 21.2301 79.5147 19.3138 80.6811 16.8144L84.097 9.39927L81.2643 9.31595Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_9632_116870">
<rect
width="83.3158"
height="23.8283"
fill="currentColor"
transform="translate(0.949219 0.585938)"
/>
</clipPath>
</defs>
</svg>
);
}
@@ -0,0 +1,33 @@
export function VerizonLogo({ className }: { className?: string }) {
return (
<svg
className={className}
width="82"
height="19"
viewBox="0 0 82 19"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_9632_116867)">
<path
d="M79.373 0.50708L74.9504 9.96817L73.2874 6.3833H71.4888L74.2236 12.2472H75.6773L81.1593 0.50708H79.373Z"
fill="currentColor"
/>
<path
d="M60.4008 18.0743H63.961V11.5575C63.961 10.0669 64.8234 9.01976 66.1046 9.01976C67.3365 9.01976 67.9771 9.8821 67.9771 11.1263V18.0743H71.5373V10.4488C71.5373 7.86176 69.9851 6.03853 67.472 6.03853C65.8705 6.03853 64.7495 6.7284 63.9118 7.98495H63.8379V6.38347H60.3885L60.4008 18.0743ZM53.4036 6.06317C49.6832 6.06317 47.1947 8.73642 47.1947 12.2597C47.1947 15.7583 49.6832 18.4562 53.4036 18.4562C57.1239 18.4562 59.6124 15.7583 59.6124 12.2597C59.6247 8.73642 57.1239 6.06317 53.4036 6.06317ZM53.3789 15.8199C51.6912 15.8199 50.7919 14.4032 50.7919 12.2597C50.7919 10.0915 51.6789 8.69946 53.3789 8.69946C55.0666 8.69946 56.0029 10.0915 56.0029 12.2597C56.0152 14.4032 55.079 15.8199 53.3789 15.8199ZM37.0438 18.0743H47.0469V15.2656H41.3801V15.1916L46.8005 9.11831V6.37115H37.0438V9.19223H42.5258V9.26614L37.0438 15.3887V18.0743ZM32.4734 18.0743H36.0582V6.38347H32.4734V18.0743ZM24.158 18.0743H27.7305V12.7032C27.7305 10.264 29.1965 9.17991 31.4262 9.48789H31.5002V6.40811C31.3154 6.33419 31.1059 6.32187 30.7733 6.32187C29.3813 6.32187 28.445 6.96247 27.6443 8.32989H27.5704V6.38347H24.158V18.0743ZM17.6042 15.8322C16.0273 15.8322 15.0664 14.8097 14.8817 13.2329H23.3079C23.3326 10.7198 22.6673 8.71178 21.2753 7.45523C20.2898 6.54362 18.9962 6.03853 17.3455 6.03853C13.8222 6.03853 11.383 8.71178 11.383 12.2104C11.383 15.7337 13.7113 18.4193 17.5795 18.4193C19.0455 18.4193 20.2035 18.025 21.1644 17.3721C22.1869 16.6699 22.9261 15.6351 23.1232 14.6126H19.6984C19.3658 15.3764 18.6513 15.8322 17.6042 15.8322ZM17.4194 8.58859C18.676 8.58859 19.5383 9.52484 19.6861 10.8923H14.9063C15.1896 9.51253 15.9411 8.58859 17.4194 8.58859ZM4.26258 18.0743H8.00759L12.0729 6.38347H8.48804L6.209 13.8981H6.15972L3.88068 6.38347H0.197266L4.26258 18.0743ZM32.4734 2.2196H36.0582V5.39794H32.4734V2.2196Z"
fill="currentColor"
/>
</g>
<defs>
<clipPath id="clip0_9632_116867">
<rect
width="81.0845"
height="17.9859"
fill="white"
transform="translate(0.074707 0.50708)"
/>
</clipPath>
</defs>
</svg>
);
}
+6 -4
View File
@@ -17,13 +17,13 @@ export function ActiveBadge({ active, className, badgeSize = "normal" }: ActiveB
switch (active) {
case true:
return (
<span className={cn(variant[badgeSize], "bg-slate-800 text-green-500", className)}>
<span className={cn(variant[badgeSize], "bg-charcoal-800 text-green-500", className)}>
Active
</span>
);
case false:
return (
<span className={cn(variant[badgeSize], "bg-slate-800 text-dimmed", className)}>
<span className={cn(variant[badgeSize], "bg-charcoal-800 text-text-dimmed", className)}>
Disabled
</span>
);
@@ -38,7 +38,7 @@ export function MissingIntegrationBadge({
badgeSize?: keyof typeof variant;
}) {
return (
<span className={cn(variant[badgeSize], "bg-rose-600 text-white", className)}>
<span className={cn(variant[badgeSize], "bg-error text-white", className)}>
Missing Integration
</span>
);
@@ -52,6 +52,8 @@ export function NewBadge({
badgeSize?: keyof typeof variant;
}) {
return (
<span className={cn(variant[badgeSize], "bg-green-600 text-background", className)}>New!</span>
<span className={cn(variant[badgeSize], "bg-green-600 text-background-dimmed", className)}>
New!
</span>
);
}
@@ -0,0 +1,23 @@
import { cn } from "~/utils/cn";
import { Header2 } from "./primitives/Headers";
export function BlankstateInstructions({
children,
className,
title,
}: {
children: React.ReactNode;
className?: string;
title?: string;
}) {
return (
<div className={cn("flex flex-col gap-2", className)}>
{title && (
<div className="flex h-10 items-center border-b border-grid-bright">
<Header2>{title}</Header2>
</div>
)}
{children}
</div>
);
}

Some files were not shown because too many files have changed in this diff Show More