Создание отчета о различиях с помощью NDepend во время сборки

Мы используем TeamCity для непрерывной интеграции, нашим исходным кодом является Git, и у нас есть 1 крупный репозиторий, содержащий несколько файлов .sln (около 10).

Всего в этом репозитории около ~ 100–200 проектов C #.

После отправки в главный репозиторий TeamCity запускает сборку, которая скомпилирует все проекты в репозитории.

Я хотел бы иметь возможность сообщать, какие проекты действительно были затронуты конкретным коммитом, и, таким образом, публиковать только результаты этих проектов как артефакты текущей сборки.

Для этого я разработал решение для интеграции NDepend в наш процесс сборки и создания отчета о различиях между текущими и последними выходными данными сборки. Результаты, которые были изменены / добавлены, будут опубликованы как результаты сборки.

У меня мало опыта работы с NDepend; Судя по тому, что я видел, вся его истинная сила исходит от встроенного в него языка запросов.

Мне интересно, как (если возможно) я могу добиться следующего:

  1. Разница между папкой, содержащей выходные данные предыдущей сборки, и текущей папкой выходных данных сборки.
  2. Попросите NDepend создать отчет в формате расходных материалов, чтобы я мог определить файлы, которые нужно скопировать.

Возможен ли такой сценарий? Насколько это было бы легко / сложно?


person lysergic-acid    schedule 10.02.2013    source источник


Ответы (1)


Итак, простой ответ - использовать метод Reporting Code Diff, как описано в этой документации. Проблема с этим базовым ответом заключается в том, что он предполагает два проекта NDepend, которые всегда относятся к одному и тому же набору сборок.


Конечно, количество и имена сборок различаются в зависимости от вашего контекста, поэтому нам нужно создать два проекта (старый / новый) на лету и проанализировать их с помощью NDepend.API.

Вот исходный код NDepend.API для этого. Для It-Just-Works в исходном коде PowerTools (в $NDependInstallDir$\NDepend.PowerTools.SourceCode\NDepend.PowerTools.sln) просто вызовите метод FoldersDiff.Main(); после AssemblyResolve вызова регистрации в Program.cs.

 ...
 AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolverHelper.AssemblyResolveHandler;
 FoldersDiff.Main();
 ...

Вот исходный код, использующий NDepend.API.

Обратите внимание, что с помощью двух объектов codeBase и объекта compareContext можно сделать гораздо больше. Вместо того, чтобы просто показывать 3 списка добавленных / удаленных / codeWasChanges сборок, вы можете показать изменения в API, добавленные новые методы и типы, измененные классы и методы, снижение качества кода ... Для этого просто посмотрите на правила кода по умолчанию, касающиеся различий, которые основаны на том же 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:\MyProduct\OldAssembliesDir".ToAbsoluteDirectoryPath();
      var dirNew = @"C:\MyProduct\NewAssembliesDir".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);
      }
   }
}
person Patrick from NDepend team    schedule 11.02.2013
comment
Классные штуки, Патрик! Выглядит достаточно долго, чтобы действительно работать. Как только я это протестирую, я отмечу как ответ. - person lysergic-acid; 11.02.2013