feat(manager:nuget): support central version management (#15698)

This commit is contained in:
Michael Kriese 2022-05-24 16:29:28 +02:00 committed by GitHub
parent c3acca8877
commit c1da6b948d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 140 additions and 13 deletions

View file

@ -0,0 +1,8 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Serilog" Version="2.10.0" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,13 @@
{
"version": 1,
"dependencies": {
".NETCoreApp,Version=v5.0": {
"Serilog": {
"type": "Direct",
"requested": "[2.10.0, )",
"resolved": "2.10.0",
"contentHash": "+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA=="
}
}
}
}

View file

@ -0,0 +1,19 @@
{
"version": 1,
"dependencies": {
".NETCoreApp,Version=v5.0": {
"Serilog": {
"type": "Direct",
"requested": "[2.10.0, )",
"resolved": "2.10.0",
"contentHash": "+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA=="
},
"one": {
"type": "Project",
"dependencies": {
"Serilog": "2.10.0"
}
}
}
}
}

View file

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../one/one.csproj" />
</ItemGroup>
</Project>

View file

@ -22,7 +22,11 @@ import type {
UpdateArtifactsConfig, UpdateArtifactsConfig,
UpdateArtifactsResult, UpdateArtifactsResult,
} from '../types'; } from '../types';
import { getDependentPackageFiles } from './package-tree'; import {
MSBUILD_CENTRAL_FILE,
NUGET_CENTRAL_FILE,
getDependentPackageFiles,
} from './package-tree';
import { import {
getConfiguredRegistries, getConfiguredRegistries,
getDefaultRegistries, getDefaultRegistries,
@ -116,7 +120,18 @@ export async function updateArtifacts({
}: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> { }: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> {
logger.debug(`nuget.updateArtifacts(${packageFileName})`); logger.debug(`nuget.updateArtifacts(${packageFileName})`);
if (!regEx(/(?:cs|vb|fs)proj$/i).test(packageFileName)) { // https://github.com/NuGet/Home/wiki/Centrally-managing-NuGet-package-versions
// https://github.com/microsoft/MSBuildSdks/tree/main/src/CentralPackageVersions
const isCentralManament =
packageFileName === NUGET_CENTRAL_FILE ||
packageFileName === MSBUILD_CENTRAL_FILE ||
packageFileName.endsWith(`/${NUGET_CENTRAL_FILE}`) ||
packageFileName.endsWith(`/${MSBUILD_CENTRAL_FILE}`);
if (
!isCentralManament &&
!regEx(/(?:cs|vb|fs)proj$/i).test(packageFileName)
) {
// This could be implemented in the future if necessary. // This could be implemented in the future if necessary.
// It's not that easy though because the questions which // It's not that easy though because the questions which
// project file to restore how to determine which lock files // project file to restore how to determine which lock files
@ -129,10 +144,13 @@ export async function updateArtifacts({
} }
const packageFiles = [ const packageFiles = [
...(await getDependentPackageFiles(packageFileName)), ...(await getDependentPackageFiles(packageFileName, isCentralManament)),
packageFileName,
]; ];
if (!isCentralManament) {
packageFiles.push(packageFileName);
}
logger.trace( logger.trace(
{ packageFiles }, { packageFiles },
`Found ${packageFiles.length} dependent package files` `Found ${packageFiles.length} dependent package files`

View file

@ -63,6 +63,25 @@ describe('modules/manager/nuget/package-tree', () => {
]); ]);
}); });
it('returns projects for two projects with one reference and central versions', async () => {
git.getFileList.mockResolvedValue(['one/one.csproj', 'two/two.csproj']);
Fixtures.mock({
'/tmp/repo/one/one.csproj': Fixtures.get(
'two-one-reference-with-central-versions/one/one.csproj'
),
'/tmp/repo/two/two.csproj': Fixtures.get(
'two-one-reference-with-central-versions/two/two.csproj'
),
'/tmp/repo/Directory.Packages.props': Fixtures.get(
'two-one-reference-with-central-versions/Directory.Packages.props'
),
});
expect(
await getDependentPackageFiles('Directory.Packages.props', true)
).toEqual(['one/one.csproj', 'two/two.csproj']);
});
it('returns two projects for three projects with two linear references', async () => { it('returns two projects for three projects with two linear references', async () => {
git.getFileList.mockResolvedValue([ git.getFileList.mockResolvedValue([
'one/one.csproj', 'one/one.csproj',

View file

@ -6,28 +6,44 @@ import { logger } from '../../../logger';
import { readLocalFile } from '../../../util/fs'; import { readLocalFile } from '../../../util/fs';
import { getFileList } from '../../../util/git'; import { getFileList } from '../../../util/git';
export const NUGET_CENTRAL_FILE = 'Directory.Packages.props';
export const MSBUILD_CENTRAL_FILE = 'Packages.props';
/** /**
* Get all package files at any level of ancestry that depend on packageFileName * Get all package files at any level of ancestry that depend on packageFileName
*/ */
export async function getDependentPackageFiles( export async function getDependentPackageFiles(
packageFileName: string packageFileName: string,
isCentralManament = false
): Promise<string[]> { ): Promise<string[]> {
const packageFiles = await getAllPackageFiles(); const packageFiles = await getAllPackageFiles();
const graph: ReturnType<typeof Graph> = Graph(); const graph: ReturnType<typeof Graph> = Graph();
if (isCentralManament) {
graph.addNode(packageFileName);
}
const parentDir =
packageFileName === NUGET_CENTRAL_FILE ||
packageFileName === MSBUILD_CENTRAL_FILE
? ''
: upath.dirname(packageFileName);
for (const f of packageFiles) { for (const f of packageFiles) {
graph.addNode(f); graph.addNode(f);
if (isCentralManament && upath.dirname(f).startsWith(parentDir)) {
graph.addEdge(packageFileName, f);
}
} }
for (const f of packageFiles) { for (const f of packageFiles) {
const packageFileContent = (await readLocalFile(f, 'utf8')).toString(); const packageFileContent = await readLocalFile(f, 'utf8');
const doc = new xmldoc.XmlDocument(packageFileContent); const doc = new xmldoc.XmlDocument(packageFileContent);
const projectReferenceAttributes = ( const projectReferenceAttributes = doc
doc .childrenNamed('ItemGroup')
.childrenNamed('ItemGroup') .map((ig) => ig.childrenNamed('ProjectReference'))
.map((ig) => ig.childrenNamed('ProjectReference')) ?? []
)
.flat() .flat()
.map((pf) => pf.attr['Include']); .map((pf) => pf.attr['Include']);
@ -47,7 +63,13 @@ export async function getDependentPackageFiles(
} }
} }
return recursivelyGetDependentPackageFiles(packageFileName, graph); const dependents = recursivelyGetDependentPackageFiles(
packageFileName,
graph
);
// deduplicate
return Array.from(new Set(dependents.reverse())).reverse();
} }
/** /**
@ -57,7 +79,7 @@ function recursivelyGetDependentPackageFiles(
packageFileName: string, packageFileName: string,
graph: ReturnType<typeof Graph> graph: ReturnType<typeof Graph>
): string[] { ): string[] {
const dependents: string[] = graph.adjacent(packageFileName); const dependents = graph.adjacent(packageFileName);
if (dependents.length === 0) { if (dependents.length === 0) {
return []; return [];