Skip to content

fix(release): scope ambiguous-scope check to active release group's projects#35745

Merged
leosvelperez merged 2 commits into
nrwl:masterfrom
jmclellan-crexi:fix-release-scope-ambiguity-per-group
Jun 9, 2026
Merged

fix(release): scope ambiguous-scope check to active release group's projects#35745
leosvelperez merged 2 commits into
nrwl:masterfrom
jmclellan-crexi:fix-release-scope-ambiguity-per-group

Conversation

@jmclellan-crexi

Copy link
Copy Markdown
Contributor

Current Behavior

nx release --group=<group> walks the full commit range since the last matching tag and resolves each commit's conventional-commit scope against the entire project graph (findMatchingProjects over projectGraph.nodes). If a scope is ambiguous against any projects in the workspace, Nx throws unconditionally — even when those projects aren't in the release group currently being processed.

In workspaces with slash-style subpath project names (e.g. a meta package @scope/cui plus subpath packages @scope/cui/forms, @scope/cui/select, …), a single commit with a short-form scope like fix(cui): … permanently blocks every release group whose tag-range includes it — including unrelated groups that don't contain any of the matching projects.

Closes #35744.

Expected Behavior

The ambiguity check should be scoped to the active release group's projects. A scope that only collides with projects outside the group should fall through to the file-affectedness path that's already used when a commit has no scope at all.

Fix

In getCommitsRelevantToProjects, projects: string[] (already in the function signature as the active release group's projects, materialized as projectSet at the top) is now applied to the per-pattern ambiguity check and to the scopedProjects set:

  1. Per-pattern check filters to the group. inGroupMatches = perPatternMatches.filter(p => projectSet.has(p)). Throw only when inGroupMatches.length > 1 — ambiguity within the group is still a real error.
  2. scopedProjects is intersected with the group. Cross-group matches no longer bleed into isProjectScopedCommit downstream.

When the filtered set is empty or singleton, the commit is treated identically to a commit with no scope for this group's processing — isProjectScopedCommit falls back to false for projects whose affectedGraph includes them via files, matching the existing scope-less behavior.

Tests

packages/nx/src/command-line/release/utils/shared.spec.ts:

  • Existing test for in-group ambiguity (should throw when commit scope matches multiple projects (ambiguous scope)) — converted from a try { … } catch (err) { expect(…) } pattern (which silently passed if no throw happened) to await expect(…).rejects.toThrow(…). Existing behavior preserved: throws when @foo/graph + @bar/graph are both in the active group with scope graph.
  • New test for cross-group ambiguity — active group ['lib-a'], scope graph matches @foo/graph + @bar/graph (both outside the group). No throw. Commit is included via file-affectedness, isProjectScopedCommit: false.
  • New test for partial cross-group ambiguity — active group ['@foo/graph'], scope graph matches @foo/graph (in group) + @bar/graph (out). Filtered to one match. No throw, treated as scoped to @foo/graph (isProjectScopedCommit: true).

Also extended createMockCommit to accept an explicit scope argument so tests actually exercise the scope-parsing path. The existing ambiguity test relied on feat(graph): … in the commit message, but commit.scope itself was hardcoded to '', so scopePatterns was always empty and the throw never fired in the test — the assertion lived in a catch block that was never reached. The conversion to rejects.toThrow plus the explicit scope argument makes the original test actually verify what its name says.

Verification

pnpm exec nx test nx --testPathPatterns=shared.spec
# Tests: 38 passed, 38 total

pnpm exec nx test nx --testPathPatterns="release"
# Tests: 1 skipped, 503 passed, 504 total

pnpm exec nx lint nx
# 0 errors (2 pre-existing warnings, unrelated)

Minimal real-world repro repo: https://github.com/jmclellan-crexi/nx-release-scope-ambiguity-repro

PR Checklist

  • Bug-fix only — no API surface changes
  • Tests added for new behavior + existing test converted to assert what it claims
  • Backwards compatible — in-group ambiguity still throws; scope-less commits unchanged; unambiguous scopes unchanged
  • No documentation changes needed (behavior is now closer to what the existing docs imply)

…rojects

The conventional-commit scope ambiguity check in getCommitsRelevantToProjects fires unconditionally on the global project graph, blocking unrelated release groups whose tag-range includes a commit whose scope happens to substring-match multiple projects outside the active group.

Filter the per-pattern match set to the active release group's projects before counting; in-group ambiguity still throws, cross-group matches fall through to the existing file-affectedness path. Also restrict scopedProjects to the active group so cross-group matches don't bleed into isProjectScopedCommit.

Closes nrwl#35744
@jmclellan-crexi jmclellan-crexi requested a review from a team as a code owner May 20, 2026 06:00
@jmclellan-crexi jmclellan-crexi requested a review from lourw May 20, 2026 06:00
@netlify

netlify Bot commented May 20, 2026

Copy link
Copy Markdown

👷 Deploy request for nx-docs pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 1dc83ad

@netlify

netlify Bot commented May 20, 2026

Copy link
Copy Markdown

👷 Deploy request for nx-dev pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 1dc83ad

@jmclellan-crexi

Copy link
Copy Markdown
Contributor Author

Reproduces using the minimal repo at https://github.com/jmclellan-crexi/nx-release-scope-ambiguity-repropnpm exec nx release --group=code-style --yes throws on master, succeeds on this branch.

@nx-cloud

nx-cloud Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit 1dc83ad

Command Status Duration Result
nx affected --targets=lint,test,build,e2e,e2e-c... ✅ Succeeded 39m 57s View ↗
nx run-many -t check-imports check-lock-files c... ✅ Succeeded 3s View ↗
nx-cloud record -- pnpm nx-cloud conformance:check ✅ Succeeded 59s View ↗
nx build workspace-plugin ✅ Succeeded <1s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded 18s View ↗
nx-cloud record -- nx format:check ✅ Succeeded 5s View ↗

☁️ Nx Cloud last updated this comment at 2026-06-09 16:00:00 UTC

@nx-cloud nx-cloud Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nx Cloud has identified a flaky task in your failed CI:

Since the failure was identified as flaky, the solution is to rerun CI. Because this branch comes from a fork, it is not possible for us to push directly, but you can rerun by pushing an empty commit:

git commit --allow-empty -m "chore: trigger rerun"
git push

Nx Cloud View detailed reasoning in Nx Cloud ↗


🎓 Learn more about Self-Healing CI on nx.dev

@leosvelperez leosvelperez left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking the time to contribute!

The changes are in the right direction. Let's address one issue with projectsRelationship: 'independent'.

projectGraph.nodes
);
const inGroupMatches = perPatternMatches.filter((p) =>
projectSet.has(p)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For release groups with projectsRelationship: "independent", projectSet only contains the single/independent project currently being processed, not all the projects in the release group (see packages/nx/src/command-line/release/version/derive-specifier-from-conventional-commits.ts:37). So, we need to forward the projects in the release group to this function so we can account for independently released projects and properly match against the release group here.

Let's fix that and add unit tests to cover that scenario as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 1dc83ad.

The full release group is now forwarded down the chain as a separate releaseGroupProjects argument and used for scope matching, while projects continues to drive which projects get commits assigned:

  • getCommitsRelevantToProjects (shared.ts) — added releaseGroupProjects: string[] = projects. The per-pattern ambiguity check and the scopedProjects filter now intersect against releaseGroupProjectSet instead of projectSet. The commit-assignment loop still uses projectSet. Defaults to projects, so fixed-group behavior is unchanged.
  • resolveSemverSpecifierFromConventionalCommits — threaded the param through (defaults to projectNames).
  • deriveSpecifierFromConventionalCommits — passes releaseGroup.projects regardless of relationship, so even when a single independent project drives affectedProjects, scopes are matched against the whole group.

Added two unit tests for the independent scenario:

  • intra-group ambiguity (projects: ['@foo/graph'], releaseGroupProjects: ['@foo/graph', '@bar/graph'], scope graph) → still throws.
  • collision only with a project outside the group (release group is just ['@foo/graph']) → no throw, treated as scoped to @foo/graph.

shared.spec is now 40 passing; the full release suite is 505 passing / 1 skipped.

…roup

For release groups with projectsRelationship: 'independent', only the
single project being processed was passed to getCommitsRelevantToProjects,
so the scope ambiguity check could not see sibling projects in the same
group. Forward the full release group's projects separately so genuine
intra-group ambiguity is still detected for independent groups, while
cross-group collisions continue to fall through to file-affectedness.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@leosvelperez leosvelperez left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@leosvelperez leosvelperez merged commit d3bf911 into nrwl:master Jun 9, 2026
17 checks passed
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.

nx release: ambiguous-scope check fires globally, blocks unrelated release groups

2 participants