Skip to content

fix: make workspace plugins release correctly with separate-pull-requests - #2847

Open
slukes wants to merge 1 commit into
googleapis:mainfrom
slukes:fix/separate-pull-requests-workspace
Open

fix: make workspace plugins release correctly with separate-pull-requests#2847
slukes wants to merge 1 commit into
googleapis:mainfrom
slukes:fix/separate-pull-requests-workspace

Conversation

@slukes

@slukes slukes commented Jul 15, 2026

Copy link
Copy Markdown

Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:

  • Make sure to open an issue as a bug/issue before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea
  • Ensure the tests and linter pass
  • Code coverage does not decrease (if any source code was changed)
  • Appropriate docs were updated (if necessary)

Fixes #2172 🦕

Problem

Since #2310, workspace plugins default to merge: false when separate-pull-requests: true is configured. On that code path, a candidate created for a dependency-only version bump ("cascade") is broken in two ways — both leftovers from when in-scope candidates were always merged into a single pull request:

  1. No release labels. newCandidate() hardcodes labels: [] (and passes [] to strategy.buildReleasePullRequest). Under merge: true this was harmless because the Merge plugin unions labels from the real candidates, but as a standalone PR the cascade never carries autorelease: pending. Consequences:
    • findMergedReleasePullRequests filters merged PRs by these labels, so after the cascade PR is merged, no tag or GitHub release is ever created for it.
    • findOpenReleasePullRequests uses the same filter, so release-please does not recognize its own open cascade PRs and force-pushes their branches on every run.
  2. Manifest entries attached to the wrong pull request. The manifest update covering all force-bumped paths is pushed onto newCandidates[0] only (introduced in fix: workspace plugins should update manifest versions #1429, when newCandidates[0] was by construction the single merged PR). With separate PRs it lands on whichever candidate sorts first alphabetically. Merging any other cascade PR leaves .release-please-manifest.json out of sync, and each subsequent run re-bumps from the package manifest and opens another doomed PR one patch higher. (If the first candidate's body happens to be unchanged, maybeUpdateExistingPullRequest skips the push entirely and the manifest update is silently dropped.)

Observed in production on a pnpm monorepo: dependency-only releases escalated three patch versions in a single day without a single tag or publish, while the manifest entries for those packages accumulated on an unrelated package's release PR.

Fix

  • Pass the manifest's configured labels through buildPlugin to the workspace plugins (node-workspace, cargo-workspace, maven-workspace), and use them in newCandidate() — both the strategy.buildReleasePullRequest path and the fallback literal. Under merge: true this is a no-op (label union is unchanged).
  • In WorkspacePlugin.run(), keep the existing single combined manifest update when merge is enabled; when it is disabled, attach each forced bump's manifest entry to its own candidate pull request. Candidates built from real releases already receive their manifest entry from the manifest PR builder, so nothing is duplicated.

Tests

Extended the plugin-compatibility test added in #2310 (test/plugins/compatibility/separate-pull-requests-workspace.ts) to also assert that each separate pull request carries the pending-release label and the manifest entry for its own path. Without the fix these assertions fail (expected [] to deeply equal [ 'autorelease: pending' ]); with it, the full suite passes (1201 tests).

…ests

When a workspace plugin builds a new candidate for a dependency-only
version bump and candidate merging is disabled (the default since googleapis#2310
when separate-pull-requests is true), two things break:

- The candidate pull request is created without the configured release
  labels (hardcoded `labels: []`), so after it is merged,
  findMergedReleasePullRequests filters it out and no tag or GitHub
  release is ever created for it. The open-PR search is also label
  filtered, so release-please does not even recognize its own open
  cascade pull requests.
- The manifest entries for every force-bumped path are attached to the
  first candidate only (a leftover from when candidates were always
  merged into a single pull request), so independently merged pull
  requests leave .release-please-manifest.json permanently out of sync,
  and each subsequent run re-bumps from package.json and opens another
  doomed pull request one patch higher.

Pass the manifest's labels through the plugin factory to the workspace
plugins so new candidates carry them, and, when not merging, attach each
forced bump's manifest entry to its own candidate pull request instead
of attaching all of them to the first one.

Fixes googleapis#2172
@product-auto-label product-auto-label Bot added the size: m Pull request size is medium. label Jul 15, 2026
@slukes
slukes marked this pull request as ready for review July 15, 2026 09:34
@slukes
slukes requested review from a team as code owners July 15, 2026 09:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: m Pull request size is medium.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Not updating .release-please-manifest.json

1 participant