[pull] main from vercel:main#348
Merged
Merged
Conversation
…ia start() (#2059) * test(e2e): cover WritableStream passed as start() argument Adds an e2e workflow + test where a parent workflow gets a WritableStream via getWritable(), forwards it through start() to a child workflow, and the child step writes raw bytes to it. Asserts the external reader on the parent's stream observes the exact bytes the child wrote. * fix(core): avoid double-framing when WritableStream is forwarded via start() When a workflow's getWritable() handle is passed across start() to a child workflow, the parent step's reviver wraps it in a serialize transform that pipes into a workflow server stream. Until now, getExternalReducers.WritableStream then installed a second serialize transform on top of that — so every chunk the child step wrote got devalue-framed twice but only deframed once on the reader side, and external consumers saw the inner frame instead of the original bytes. Fix: tag every user-visible writable that's already backed by a workflow server stream with its (runId, name). When the external reducer recognizes those tags during dehydration, it bridges bytes straight from the new child-side server stream to the original server stream instead of piping through the user's writable. That leaves the producer-side serialize transform (installed once by the child's step reviver) as the only framing layer in the chain. * fix(core): forward (runId, name) when a tagged WritableStream crosses start() Replaces the previous in-process bridge with first-class writable forwarding at the descriptor level. When a parent workflow's getWritable() handle is passed as an argument to a child workflow, the dehydrated descriptor now carries the original (runId, name). The child run's step-side reviver opens the writable against the parent's server stream directly and resolves the parent run's encryption key (encrypt-only) via getEncryptionKeyForRun. This removes the architectural limitation that the bridge could only stay alive for the duration of the parent step process — on Vercel that capped forwarding at ~15 minutes regardless of the child run's lifetime, dropping any writes the child made after the parent step process exited. importKey() now accepts a usages parameter, defaulting to ['encrypt', 'decrypt']. The cross-run forwarding path imports with ['encrypt'] only so a compromised child run cannot decrypt any existing data on the parent's stream — only contribute new writes. * test: rename writable-forwarded workflows and cover step-context getWritable() Addresses PR review: - Rename writableForwardedToChildChildWorkflow → writableForwardedChildWorkflow (drops the duplicated 'Child' segment). - Split writableForwardedToChildWorkflow into two variants covered by a test.each: writableForwardedFromWorkflowWorkflow (workflow-context getWritable, the original test) and writableForwardedFromStepWorkflow (step-context getWritable passed directly into start() from the same step that called getWritable()). - Terser changeset description.
GitHub Actions doesn't currently support prefilling workflow_dispatch inputs via URL query params (community/community#51159), so the "override via workflow_dispatch with this commit SHA" instruction in the no-backport comment required the reader to go look up the SHA themselves. Paste the full 40-char SHA into the comment in a fenced code block so it's one click to copy into the "Commit SHA" input on the workflow run page.
…tion` (#2065) * ci: extract wait-for-vercel-project to vercel/wait-for-deployment-action The action's logic was duplicated between this repo and vercel/workflow-server, which is annoying to keep in sync. Move it to a standalone repository so both can consume the same pinned build. Changes: - Delete .github/actions/wait-for-vercel-project entirely. - Replace all five `uses: ./.github/actions/wait-for-vercel-project` references with `uses: vercel/wait-for-deployment-action@<sha>` in: benchmarks.yml, dispatch-front-workflow-release-pr.yml, docs-checks.yml, tarballs-checks.yml, tests.yml - All `with:` inputs (project-slug, environment, timeout, check-interval, github-token) are unchanged — the new action's input contract is backwards-compatible. The new action is ESM-only, targets Node 24, ships a ~12KB bundle (down from ~830KB in the old in-repo version) by dropping @actions/core and its transitive undici dependency, and is unit-tested. See https://github.com/vercel/wait-for-deployment-action. * ci: bump wait-for-deployment-action to fix/status-context-auto for verification Repinning to vercel/wait-for-deployment-action#fix/status-context-auto (SHA 04d46ef) which fixes the broken 'opt-out' heuristic that made status-context resolution silently disabled for every consumer. Reproduced in this repo's E2E logs: Looking for GitHub deployment in environment "Preview – example-workflow" Deployment ID resolution disabled (status-context is empty) Deployment ready: https://example-workflow-...labs.vercel.dev Run E2E Tests: VERCEL_DEPLOYMENT_ID= <-- empty Will repin to the post-merge main SHA once CI is green. * ci: bump wait-for-deployment-action pin to merged main SHA Repinning from the fix/status-context-auto branch (04d46ef) to the post-merge main SHA (0e2b0c5, vercel/wait-for-deployment-action#4). The deployment-id resolution fix verified against the prior fix-branch pin (E2E tests now read VERCEL_DEPLOYMENT_ID=dpl_... correctly across the matrix; only flaky/unrelated Vercel deployment failures remain). * ci: grant statuses:read alongside deployments:read The wait-for-deployment-action also reads the 'Vercel – <slug>' combined commit status to resolve the dpl_xxx ID. The official permissions table lists statuses:read for GET /repos/{owner}/{repo}/commits/{ref}/status.
* [core] Exclude inline step execution from replay timeout The v5 combined workflow+step handler wraps inline step bodies in the same setTimeout(..., REPLAY_TIMEOUT_MS) guard that previously only bounded the v4 'workflows' function's fast deterministic replay. As a result, any workflow with a single step exceeding 240s hard-fails with FatalError: Workflow replay exceeded maximum duration (240s) after 4 attempts — even though the step could legitimately run for the full function maxDuration (up to 800s on Pro Fluid). Replace the setTimeout guard with a per-invocation budget that only accumulates non-step time. pauseReplayBudget() / resumeReplayBudget() bracket each executeStep() call, and the loop checks the budget at iteration boundaries. The retry-then-fail semantics from #1567 are preserved verbatim for the pure-replay case. Also adds a WORKFLOW_REPLAY_TIMEOUT_MS env var override (clamped to 30s..780s) so operators can adjust the bare-replay ceiling without patching @workflow/core. Fixes #2009. * Address PR review feedback - Extract budget bookkeeping into ReplayBudget class (replay-budget.ts) with sentinel-protected idempotent pause()/resume() to avoid double-counting in future refactors that nest step execution - Restore VERCEL_URL gate around process.exit(1) so a long pure-replay in local dev/non-Vercel runtimes can't hard-kill the host process - Warn (once per distinct raw value) when WORKFLOW_REPLAY_TIMEOUT_MS is clamped or rejected, so misconfiguration is observable - Correct Hobby maxDuration comment (60s standard / 300s Fluid) - Document budget-check responsiveness trade-off vs. old setTimeout - Tighten describe-error test assertions to match the full new hint - Shorten changeset description - Add ReplayBudget unit tests (9) including 8-minute step regression - Add warn-once tests for getReplayTimeoutMs (extended) * Replace VERCEL_URL gate with World capability Per review feedback, gating runtime behavior on process.env.VERCEL_URL leaks deployment-environment concerns into @workflow/core. Replace the check with a new optional capability on the World interface: processExitTriggersQueueRedelivery (default false). - @workflow/world: declare the new optional capability on World - @workflow/world-vercel: set it to true (Vercel fails the function invocation on non-zero exit and VQS redelivers via fresh invocation) - @workflow/core: handleReplayBudgetExhausted reads world.processExitTriggersQueueRedelivery instead of process.env.VERCEL_URL; behavior is otherwise unchanged - Add 4 unit tests for handleReplayBudgetExhausted covering both branches (exit-for-redelivery and best-effort run_failed) --------- Co-authored-by: Peter Wielander <mittgfu@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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )