Environment
- release-please 17.3.0 (bundled in release-please-action v4.4.1), manifest mode
- Behavior confirmed unchanged by reading the source at v17.10.3
- Monorepo: pnpm workspace, one shared library plus two services that depend on it via
workspace:*
- Config (trimmed):
{
"separate-pull-requests": true,
"plugins": [{ "type": "node-workspace", "merge": false }],
"packages": {
"packages/shared": { "release-type": "node", "component": "shared" },
"services/a": { "release-type": "node", "component": "a" },
"services/b": { "release-type": "node", "component": "b" }
}
}
Steps to reproduce
- Land releasable commits that touch only
packages/shared (the two services have no releasable commits of their own).
- Let release-please run. It computes a release for shared, and the node-workspace plugin creates dependency-bump candidates for both services.
- Inspect the three resulting pull requests.
Bug 1: all dependency-bump manifest entries are attached to the first candidate only
WorkspacePlugin.run pushes the whole updatedPathVersions map onto newCandidates[0] unconditionally, after the if (this.merge) block:
https://github.com/googleapis/release-please/blob/v17.10.3/src/plugins/workspace.ts#L179-L187
With merge: true this is correct (there is only one merged candidate). With merge: false there are multiple candidates, and newCandidates[0] is simply the first package in the post-order graph walk (in practice the alphabetically first dependent). Result, observed in real runs:
- Service A's release PR contains the
.release-please-manifest.json bump for service A and service B.
- Service B's release PR contains no manifest update at all (plugin-created candidates only get package.json, package-lock.json and CHANGELOG updates; the per-candidate manifest update from
buildPullRequests is only attached to strategy candidates).
- The library's own PR is fine (strategy candidate).
Consequences depend on merge order and are all bad:
- Merge A's PR while B's PR is closed or abandoned: the manifest now claims a version of B that was never released (a phantom version). On the next run release-please looks up the tag for that phantom version, cannot find it, and generates a release PR with an unbounded commit range.
- Merge B's PR: the manifest is not updated, so release-please immediately re-proposes the same version for B in a new PR.
Bug 2: plugin-created candidates get labels: [], so the PRs never receive autorelease: pending
NodeWorkspace.newCandidate builds the pull request with an empty labels array:
https://github.com/googleapis/release-please/blob/v17.10.3/src/plugins/node-workspace.ts#L342
Strategy-built candidates get the configured labels, but dependency-bump-only PRs are created without autorelease: pending. In a run that created three PRs (library plus two dependents), the action log contains exactly one labeling call, for the strategy candidate only:
✔ Successfully added labels autorelease: pending to issue: <n>
Consequences:
buildReleases / createReleases filter merged PRs by that label (see findMergedReleasePullRequests), so merging an unlabeled dependency-bump PR silently creates no tag and no GitHub release, and the same version is re-proposed on the next run.
findOpenReleasePullRequests also filters by the label, so the open PR is invisible to the bot: it is never force-push refreshed after other merges and stays permanently conflicted.
Expected behavior
With merge: false:
- Each candidate PR should carry the manifest entry for its own path (and only that entry).
- Plugin-created candidates should receive the same labels as strategy candidates, so tagging and PR maintenance work.
Workaround
We removed the node-workspace plugin and replaced the cascade with a small workflow: when the library publishes a release, the workflow pushes one fix(deps): bump <library> to X.Y.Z commit that touches a marker file inside each consuming service's directory. Path attribution then produces normal strategy-built per-service release PRs with correct labels and manifest lines.
Environment
workspace:*{ "separate-pull-requests": true, "plugins": [{ "type": "node-workspace", "merge": false }], "packages": { "packages/shared": { "release-type": "node", "component": "shared" }, "services/a": { "release-type": "node", "component": "a" }, "services/b": { "release-type": "node", "component": "b" } } }Steps to reproduce
packages/shared(the two services have no releasable commits of their own).Bug 1: all dependency-bump manifest entries are attached to the first candidate only
WorkspacePlugin.runpushes the wholeupdatedPathVersionsmap ontonewCandidates[0]unconditionally, after theif (this.merge)block:https://github.com/googleapis/release-please/blob/v17.10.3/src/plugins/workspace.ts#L179-L187
With
merge: truethis is correct (there is only one merged candidate). Withmerge: falsethere are multiple candidates, andnewCandidates[0]is simply the first package in the post-order graph walk (in practice the alphabetically first dependent). Result, observed in real runs:.release-please-manifest.jsonbump for service A and service B.buildPullRequestsis only attached to strategy candidates).Consequences depend on merge order and are all bad:
Bug 2: plugin-created candidates get
labels: [], so the PRs never receiveautorelease: pendingNodeWorkspace.newCandidatebuilds the pull request with an empty labels array:https://github.com/googleapis/release-please/blob/v17.10.3/src/plugins/node-workspace.ts#L342
Strategy-built candidates get the configured labels, but dependency-bump-only PRs are created without
autorelease: pending. In a run that created three PRs (library plus two dependents), the action log contains exactly one labeling call, for the strategy candidate only:Consequences:
buildReleases/createReleasesfilter merged PRs by that label (seefindMergedReleasePullRequests), so merging an unlabeled dependency-bump PR silently creates no tag and no GitHub release, and the same version is re-proposed on the next run.findOpenReleasePullRequestsalso filters by the label, so the open PR is invisible to the bot: it is never force-push refreshed after other merges and stays permanently conflicted.Expected behavior
With
merge: false:Workaround
We removed the node-workspace plugin and replaced the cascade with a small workflow: when the library publishes a release, the workflow pushes one
fix(deps): bump <library> to X.Y.Zcommit that touches a marker file inside each consuming service's directory. Path attribution then produces normal strategy-built per-service release PRs with correct labels and manifest lines.