Skip to content

Commit 5be5579

Browse files
authored
fix(release): ensure dependents that are both direct and transitive are not bumped twice (#28878)
1 parent 37e31be commit 5be5579

File tree

2 files changed

+173
-1
lines changed

2 files changed

+173
-1
lines changed

packages/js/src/generators/release-version/release-version.spec.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,6 +1757,139 @@ Valid values are: "auto", "", "~", "^", "="`,
17571757
}
17581758
`);
17591759
});
1760+
1761+
it('should not double patch transitive dependents that are already direct dependents', async () => {
1762+
projectGraph = createWorkspaceWithPackageDependencies(tree, {
1763+
'@slateui/core': {
1764+
projectRoot: 'packages/core',
1765+
packageName: '@slateui/core',
1766+
version: '1.0.0',
1767+
packageJsonPath: 'packages/core/package.json',
1768+
localDependencies: [],
1769+
},
1770+
// buttons depends on core
1771+
'@slateui/buttons': {
1772+
projectRoot: 'packages/buttons',
1773+
packageName: '@slateui/buttons',
1774+
version: '1.0.0',
1775+
packageJsonPath: 'packages/buttons/package.json',
1776+
localDependencies: [
1777+
{
1778+
projectName: '@slateui/core',
1779+
dependencyCollection: 'dependencies',
1780+
version: '1.0.0',
1781+
},
1782+
],
1783+
},
1784+
// forms depends on both core and buttons, making it both a direct and transitive dependent of core
1785+
'@slateui/forms': {
1786+
projectRoot: 'packages/forms',
1787+
packageName: '@slateui/forms',
1788+
version: '1.0.0',
1789+
packageJsonPath: 'packages/forms/package.json',
1790+
localDependencies: [
1791+
{
1792+
projectName: '@slateui/core',
1793+
dependencyCollection: 'dependencies',
1794+
version: '1.0.0',
1795+
},
1796+
{
1797+
projectName: '@slateui/buttons',
1798+
dependencyCollection: 'dependencies',
1799+
version: '1.0.0',
1800+
},
1801+
],
1802+
},
1803+
});
1804+
1805+
expect(
1806+
await releaseVersionGenerator(tree, {
1807+
projects: [projectGraph.nodes['@slateui/core']],
1808+
releaseGroup: createReleaseGroup('independent'),
1809+
projectGraph,
1810+
// Bump core to 2.0.0, which will cause buttons and forms to be patched to 1.0.1
1811+
// This prevents a regression against an issue where forms would end up being patched twice to 1.0.2 in this scenario
1812+
specifier: '2.0.0',
1813+
currentVersionResolver: 'disk',
1814+
specifierSource: 'prompt',
1815+
})
1816+
).toMatchInlineSnapshot(`
1817+
{
1818+
"callback": [Function],
1819+
"data": {
1820+
"@slateui/buttons": {
1821+
"currentVersion": "1.0.0",
1822+
"dependentProjects": [
1823+
{
1824+
"dependencyCollection": "dependencies",
1825+
"rawVersionSpec": "1.0.0",
1826+
"source": "@slateui/forms",
1827+
"target": "@slateui/buttons",
1828+
"type": "static",
1829+
},
1830+
],
1831+
"newVersion": "1.0.1",
1832+
},
1833+
"@slateui/core": {
1834+
"currentVersion": "1.0.0",
1835+
"dependentProjects": [
1836+
{
1837+
"dependencyCollection": "dependencies",
1838+
"rawVersionSpec": "1.0.0",
1839+
"source": "@slateui/buttons",
1840+
"target": "@slateui/core",
1841+
"type": "static",
1842+
},
1843+
{
1844+
"dependencyCollection": "dependencies",
1845+
"rawVersionSpec": "1.0.0",
1846+
"source": "@slateui/forms",
1847+
"target": "@slateui/core",
1848+
"type": "static",
1849+
},
1850+
],
1851+
"newVersion": "2.0.0",
1852+
},
1853+
"@slateui/forms": {
1854+
"currentVersion": "1.0.0",
1855+
"dependentProjects": [],
1856+
"newVersion": "1.0.1",
1857+
},
1858+
},
1859+
}
1860+
`);
1861+
1862+
expect(readJson(tree, 'packages/core/package.json'))
1863+
.toMatchInlineSnapshot(`
1864+
{
1865+
"name": "@slateui/core",
1866+
"version": "2.0.0",
1867+
}
1868+
`);
1869+
1870+
expect(readJson(tree, 'packages/buttons/package.json'))
1871+
.toMatchInlineSnapshot(`
1872+
{
1873+
"dependencies": {
1874+
"@slateui/core": "2.0.0",
1875+
},
1876+
"name": "@slateui/buttons",
1877+
"version": "1.0.1",
1878+
}
1879+
`);
1880+
1881+
expect(readJson(tree, 'packages/forms/package.json'))
1882+
.toMatchInlineSnapshot(`
1883+
{
1884+
"dependencies": {
1885+
"@slateui/buttons": "1.0.1",
1886+
"@slateui/core": "2.0.0",
1887+
},
1888+
"name": "@slateui/forms",
1889+
"version": "1.0.1",
1890+
}
1891+
`);
1892+
});
17601893
});
17611894
});
17621895

packages/js/src/generators/release-version/release-version.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,13 @@ To fix this you will either need to add a package.json file at that location, or
735735
updateDependents === 'auto' &&
736736
options.releaseGroup.projectsRelationship === 'independent'
737737
? allDependentProjects.length +
738-
transitiveLocalPackageDependents.length -
738+
// Only count transitive dependents that aren't already direct dependents
739+
transitiveLocalPackageDependents.filter(
740+
(transitive) =>
741+
!allDependentProjects.some(
742+
(direct) => direct.source === transitive.source
743+
)
744+
).length -
739745
// There are two entries per circular dep
740746
circularDependencies.size / 2
741747
: dependentProjectsInCurrentBatch.length;
@@ -895,6 +901,39 @@ To fix this you will either need to add a package.json file at that location, or
895901
}
896902
}
897903
for (const transitiveDependentProject of transitiveLocalPackageDependents) {
904+
const isAlreadyDirectDependent = allDependentProjects.some(
905+
(dep) => dep.source === transitiveDependentProject.source
906+
);
907+
if (isAlreadyDirectDependent) {
908+
// Don't continue directly in this scenario - we still need to update the dependency version
909+
// but we don't want to bump the project's own version as it will end up being double patched
910+
const dependencyProjectName = transitiveDependentProject.target;
911+
const dependencyPackageRoot = projectNameToPackageRootMap.get(
912+
dependencyProjectName
913+
);
914+
if (!dependencyPackageRoot) {
915+
throw new Error(
916+
`The project "${dependencyProjectName}" does not have a packageRoot available. Please report this issue on https://github.com/nrwl/nx`
917+
);
918+
}
919+
const dependencyPackageJsonPath = joinPathFragments(
920+
dependencyPackageRoot,
921+
'package.json'
922+
);
923+
const dependencyPackageJson = readJson(
924+
tree,
925+
dependencyPackageJsonPath
926+
);
927+
928+
updateDependentProjectAndAddToVersionData({
929+
dependentProject: transitiveDependentProject,
930+
dependencyPackageName: dependencyPackageJson.name,
931+
newDependencyVersion: dependencyPackageJson.version,
932+
forceVersionBump: false, // Never bump version for direct dependents
933+
});
934+
continue;
935+
}
936+
898937
// Check if the transitive dependent originates from a circular dependency
899938
const isFromCircularDependency = circularDependencies.has(
900939
`${transitiveDependentProject.source}:${transitiveDependentProject.target}`

0 commit comments

Comments
 (0)