feat(eve): external cron mode for self-hosted builds (EVE_EXTERNAL_CRON=1)#710
Open
warmwind wants to merge 2 commits into
Open
feat(eve): external cron mode for self-hosted builds (EVE_EXTERNAL_CRON=1)#710warmwind wants to merge 2 commits into
warmwind wants to merge 2 commits into
Conversation
Self-hosted (node preset) builds fire schedules from Nitro's in-process runner with no off switch: replicas duplicate every tick, downtime loses ticks silently, and production exposes no dispatch endpoint, so a hosting platform can neither pause a schedule nor trigger one. Building with EVE_EXTERNAL_CRON=1 now registers the schedule task handlers without any scheduledTasks entries — the in-process runner never starts — and instead mounts the same unguessable token cron route the Vercel preset uses. The route mirrors Nitro's Vercel cron handler contract (x-vercel-cron-schedule header, optional CRON_SECRET bearer check) and dispatches through dispatchScheduleTask. The build writes .output/eve/cron-manifest.json (route path plus each schedule's name and cron) — the self-hosted equivalent of the config.crons[] Vercel reads from build output. No effect on Vercel builds or eve dev. Closes vercel#707 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0164WUaSv9mN58wgnnoN2vKG Signed-off-by: Oscar Jiang <pengj0520@gmail.com>
Contributor
|
@warmwind is attempting to deploy a commit to the Vercel Team on Vercel. A member of the Team first needs to authorize it. |
The dispatch route fires every schedule registered for the posted expression, so a manifest entry per schedule invites schedulers to create one job per schedule and double-dispatch shared expressions. Mirror the Vercel preset, which keys config.crons on scheduledTasks: one entry per distinct expression, listing the schedules it dispatches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0164WUaSv9mN58wgnnoN2vKG Signed-off-by: Oscar Jiang <pengj0520@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #707
Problem
Self-hosted (node preset) builds fire schedules from Nitro's in-process runner with no off switch: N replicas fire every schedule N times, ticks that land while the process is down are silently lost, and production exposes no dispatch endpoint — so a hosting platform can neither pause a schedule nor trigger one manually, and the container must stay always-on just to keep the clock. None of this applies on Vercel, where the platform owns the clock through the unguessable token cron route.
Change
Building with
EVE_EXTERNAL_CRON=1(node preset, production only — no effect on Vercel builds oreve dev):registerScheduleTaskHandlersgainsregisterCronSchedules: false: task handlers are registered as before, but nothing is written toscheduledTasks, sostartScheduleRunnerin the node-server entry no-ops.POST /eve/v1/cron/<token>(reusingcreateEveCronHandlerRoute). The handler mirrors Nitro's Vercel cron handler contract — the due cron expression arrives inx-vercel-cron-schedule, and a configuredCRON_SECRETis enforced as a bearer token with a timing-safe compare — and dispatches every schedule registered for that expression throughdispatchScheduleTask(registrations are baked into the virtual handler, sincescheduledTasksis deliberately empty in this mode).compiledwrites.output/eve/cron-manifest.jsonwith the route path and one entry per distinct cron expression, each listing the schedules it dispatches — the self-hosted equivalent of theconfig.crons[]Vercel reads from build output. The path is the credential, so the manifest lives only in build output (readable by whoever ran the build) and is never served.An external scheduler then drives the deployment exactly like Vercel Cron does: on each due tick, POST the route with the cron expression in the header. Keeping the request contract identical means one scheduler client works against both Vercel and self-hosted deployments.
Testing
registerCronSchedules: falseregisters tasks/virtual modules withscheduledTasksleft empty; route registration bakes in the schedule map at an unguessable path;isExternalCronEnabledparsing; handler tests for dispatch-by-expression, empty match, missing header (400), andCRON_SECRETenforcement (401/200).compiledhook writes the expected manifest JSON into the output directory.pnpm test:unit(4439),pnpm test:integration(386), packagetypecheck,lint,guard:invariants,docs:checkall pass.🤖 Generated with Claude Code
https://claude.ai/code/session_0164WUaSv9mN58wgnnoN2vKG