Skip to content

fix(publish): continue publishing workspace after a package fails#35133

Open
divybot wants to merge 4 commits into
mainfrom
orch/divybot-562
Open

fix(publish): continue publishing workspace after a package fails#35133
divybot wants to merge 4 commits into
mainfrom
orch/divybot-562

Conversation

@divybot

@divybot divybot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

When running deno publish for a workspace containing multiple packages, a failure while publishing one package caused the entire publish process to fast-fail. The remaining (independent) packages — and steps like provenance attestation for already-published packages — were skipped.

The fast-fail was the ? in perform_publish (cli/tools/publish/mod.rs):

let package_name = result?;
publish_order_graph.finish_package(&package_name);

Fix

Instead of aborting on the first error, the publish loop now:

  • records each failure, preserving its full anyhow context chain (the Failed to publish <package> chain) rather than discarding it,
  • keeps publishing the remaining in-flight and queued packages,
  • and exits with a non-zero status at the end, reporting all collected errors.

A package that fails is intentionally not passed to finish_package, so any packages depending on it stay blocked and are never published against a missing version. Independent packages publish as normal.

When a single package fails its original error (and context chain) is returned unchanged; when multiple fail they are combined into one error that lists every failure with its full chain.

Test

Adds the publish/workspace_continue_on_error spec test: a workspace with one package that publishes successfully and two that fail at the registry. It asserts the successful package is still published, that both failures are reported with their context chains, and that the command exits non-zero. The test registry mock (tests/util/server/servers/jsr_registry.rs) now simulates a publish failure for packages named publish-fails*.

Background: originally attempted in #26829, which went stale after the code moved from cli/tools/registry/mod.rs to cli/tools/publish/mod.rs.

Closes #35091

Closes denoland/divybot#562

When publishing a workspace with multiple packages, a failure while
publishing one package would fast-fail the whole process via the `?`
operator in `perform_publish`, aborting before the remaining in-flight
and queued packages (and steps like provenance attestation) were handled.

Collect publish errors instead of propagating the first one: keep
publishing the other independent packages, preserve each error's context
chain, and exit non-zero at the end reporting all collected failures.
Packages depending on a failed package are still skipped (we don't call
`finish_package` for a failure) so they're never published against a
missing version.

Co-Authored-By: Divy Srivastava <me@littledivy.com>
@bartlomieju

Copy link
Copy Markdown
Member

Reviewed the change — the core logic is correct and the dependent-blocking behavior holds up (a failed package isn't finish_package'd, so its dependents stay blocked, and the retained ensure_no_pending()? runs ensure_no_cycles on the static packages graph, so it won't spuriously error on the drain-with-pending path that this change newly exercises). A few non-blocking suggestions:

  • The blocked-dependent case is untested. The new spec test uses three independent packages, so the PR's strongest claim — that packages depending on a failed one stay blocked and are never published against a missing version — has no coverage. A second test where b depends on a failing a would lock in the most subtle part of this change and guard the ensure_no_pending interaction against future regressions.

  • Silently skipped dependents. In the blocked case, dependents are neither published nor reported; only the root failure surfaces. Reasonable default, but a "skipped N package(s) that depended on a failed package" note would improve UX. Fine as a follow-up.

  • Consider a comment on the retained ?. Previously a failure aborted immediately, so ensure_no_pending()? was never reached with pending packages still in the graph. Now it can be. A one-line comment explaining why it's safe (cycle check runs on the static graph, blocked-but-acyclic dependents return Ok) would help the next reader who wonders whether that ? can swallow the collected errors.

Address review feedback on #35133:
- Add a spec test where a package depends on a failing one, locking in
  that blocked dependents stay unpublished and ensure_no_pending() does
  not swallow the collected errors.
- Report skipped (blocked) dependents instead of silently dropping them.
- Document why the retained ensure_no_pending()? is safe.
@divybot

divybot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review! Addressed all three points in 50df891:

  • Blocked-dependent test — added tests/specs/publish/workspace_continue_blocked_dependent, where @foo/dependent depends on the failing @foo/publish-fails. It locks in that the dependent stays blocked (never published against a missing version) and that the retained ensure_no_pending()? returns Ok on the drain-with-pending path rather than swallowing the collected failure.
  • Silently skipped dependents — the blocked-but-unattempted packages (those still left in prepared_package_by_name when there were errors) are now reported via a Warning Skipped publishing N package(s) that depended on a package that failed to publish: note listing each one, instead of disappearing.
  • Comment on the retained ? — added a comment explaining why it's safe (cycle check runs on the static graph; blocked-but-acyclic dependents return Ok, so collected errors aren't lost; a genuine cycle still surfaces).

@divybot

divybot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

CI note: the test specs (…) debug failures on serve::request_signal_streaming are not related to this PR — that test is currently broken on main itself (it fails on every main commit since #34397, a semantic conflict with the test added in #35049). I opened #35191 to fix it. Once that lands, this PR's specs jobs go green on re-merge.

The lint debug windows-x86_64 failure is also unrelated: a spurious promote_to_release.generated.yml is out of date from gagen's network SHA resolution — running the generator locally produces no diff, and this PR touches no .github/ files.

All publish-related tests (including the two new workspace_continue_* spec tests) pass, verified locally against current main including the #35134 publish refactor.

divybot added 2 commits June 13, 2026 15:12
…streaming

This test (added in #35049) and the legacy-abort deprecation warning (added in
#34397) landed in parallel and collided on main: the test exercises legacy
abort with a handler that accesses request.signal and returns successfully,
which is exactly what #34397 warns about, but its main.out wasn't updated.
main has been failing this spec test on every commit since #34397, which blocks
CI for this PR (and every other) once merged with main.

The warning is correct for this scenario, so expect it in the test output.
Folded in here (rather than the standalone #35191) to make this PR's CI
self-sufficient; happy to split back out if preferred.
@divybot

divybot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Pushed 1dc97f9: merged latest main and folded in the one-line fix for the serve::request_signal_streaming spec test. That test is broken on main itself (semantic conflict between #35049 and #34397 — it fails on every main commit since #34397) and was the sole cause of the test specs (2/2) failures here; workspace_continue_* always passed. I'd opened #35191 as a standalone fix but closed it and folded the single line in here so this PR's CI is self-sufficient — happy to split it back out if you prefer. The remaining lint red is the unrelated promote_to_release.generated.yml gagen flake (no .github/ changes here; generator yields no diff locally), which needs a re-run.

littledivy pushed a commit that referenced this pull request Jun 13, 2026
…ming

Merging main brings in #34397, which emits a legacy-abort deprecation
warning when a Deno.serve handler uses legacy request.signal abort on a
successful response. The request_signal_streaming spec exercises exactly
that path, so its expected output must include the warning line. Same fix
as the closed #35191 (folded into #35133); included here so this PR's CI
is self-sufficient. Unrelated to the blob-worker change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

deno publish: workspace publish fast-fails on first package error, skipping remaining packages

2 participants