我们使用TeamCity进行持续集成,我们的源代码管理是Git,我们有一个主要的存储库,其中包含多个.sln文件(大约10个)。
总而言之,这个存储库有大约100-200个C#项目。
在推送到主存储库后,TeamCity会触发一个生成,该生成将编译存储库中的所有项目。
我希望能够告诉哪些项目实际上受到了特定提交的影响,从而仅将这些项目的输出发布为当前构建的工件。
为此,我设计了一个解决方案,将NDepend集成到我们的构建过程中,并生成当前和最新构建输出之间的差异报告。更改/添加的输出将作为生成输出发布。
我对NDepend没有什么经验;据我所见,它的所有真正功能都来自于内置的查询语言
我想知道如何(如果可能的话)实现以下目标:
- 包含以前生成的输出的文件夹和生成输出的当前文件夹之间的差异
- 让NDepend生成一个消耗品格式的报告,这样我就可以确定需要复制的文件
这种情况可能发生吗?这有多容易/难?
因此,简单的答案是按照本文档中解释的报告代码差异方法。这个基本答案的问题是,它预先假设两个NDepend项目总是引用两个相同的程序集。
当然,程序集的数量和名称在您的上下文中是不同的,因此我们需要动态构建两个项目(旧的/新的),并通过NDepend.API.对它们进行分析
这是NDepend.API的源代码。对于It Just Works体验,在PowerTools源代码(在$NDependInstallDir$NDepend.PowerTools.SourceCodeNDepend.PowerTools.sln
中)中,只需在Program.cs
中的AssemblyResolve
注册调用之后调用FoldersDiff.Main();
方法。
...
AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolverHelper.AssemblyResolveHandler;
FoldersDiff.Main();
...
这是利用NDdepend.API.的源代码
请注意,通过两个codeBase
对象和compareContext
对象,可以做更多的工作。您可以显示API特征更改、添加的新方法和类型、修改的类和方法、代码质量回归。。。为此,只需查看有关diff的默认代码规则,它们基于相同的NDepend.CodeModel API。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NDepend;
using NDepend.Analysis;
using NDepend.CodeModel;
using NDepend.Path;
using NDepend.Project;
class FoldersDiff {
private static readonly NDependServicesProvider s_NDependServicesProvider = new NDependServicesProvider();
internal static void Main() {
var dirOld = @"C:MyProductOldAssembliesDir".ToAbsoluteDirectoryPath();
var dirNew = @"C:MyProductNewAssembliesDir".ToAbsoluteDirectoryPath();
Console.WriteLine("Analyzing assemblies in " + dirOld.ToString());
var codeBaseOld = GetCodeBaseFromAsmInDir(dirOld, TemporaryProjectMode.TemporaryOlder);
Console.WriteLine("Analyzing assemblies in " + dirNew.ToString());
var codeBaseNew = GetCodeBaseFromAsmInDir(dirNew, TemporaryProjectMode.TemporaryNewer);
var compareContext = codeBaseNew.CreateCompareContextWithOlder(codeBaseOld);
// So much more can be done by exploring fine-grained diff in codeBases and compareContext
Dump("Added assemblies", codeBaseNew.Assemblies.Where(compareContext.WasAdded));
Dump("Removed assemblies", codeBaseOld.Assemblies.Where(compareContext.WasRemoved));
Dump("Assemblies with modified code", codeBaseNew.Assemblies.Where(compareContext.CodeWasChanged));
Console.Read();
}
internal static ICodeBase GetCodeBaseFromAsmInDir(IAbsoluteDirectoryPath dir, TemporaryProjectMode temporaryProjectMode) {
Debug.Assert(dir.Exists);
var dotNetManager = s_NDependServicesProvider.DotNetManager;
var assembliesPath = dir.ChildrenFilesPath.Where(dotNetManager.IsAssembly).ToArray();
Debug.Assert(assembliesPath.Length > 0); // Make sure we found assemblies
var projectManager = s_NDependServicesProvider.ProjectManager;
IProject project = projectManager.CreateTemporaryProject(assembliesPath, temporaryProjectMode);
// In PowerTool context, better call:
// var analysisResult = ProjectAnalysisUtils.RunAnalysisShowProgressOnConsole(project);
var analysisResult = project.RunAnalysis();
return analysisResult.CodeBase;
}
internal static void Dump(string title, IEnumerable<IAssembly> assemblies) {
Debug.Assert(!string.IsNullOrEmpty(title));
Debug.Assert(assemblies != null);
Console.WriteLine(title);
foreach (var @assembly in assemblies) {
Console.WriteLine(" " + @assembly.Name);
}
}
}