SolutionCop
Привет хабр! Основная речь пойдет про разработку на .Net, то есть с использованием Microsoft Visual Studio, ReSharper, Nuget и пр.
Я думаю, многие из вас разрабатывали большие решения (в msdn — solution), со множеством подпроектов. И в этом случае нередко становилась проблема синхронизации Nuget пакетов, настроек сборки и т.д. Причем, ReSharper здесь поможет слабо, разве что он тоже начнет путаться во множестве используемых библиотек.
Чтобы проверять исходный код, было сделано Open Source решение — SolutionCop, которое бесплатно для использования.
Для начала приведу парочку примеров, когда не помешали бы проверки наших решений.
Пример 1: разные версии Nuget библиотек.
Например, есть три проекта: exe, dll1 и dll2. exe ссылается на обе библиотеки, каждая из них ссылается, например, на RX. Но dll1 использует RX 2.2.0, а dll2 — RX 2.2.5. На деле, далеко не сразу можно получить ошибку, так как сигнатуры функций более-менее совпадают, более того, MsBuild чаще всего собирает проекты в одном и то же порядке. Однако подобная конфигурация может привести к проблемам, которые появятся после deployment’а, когда все модульные тесты пройдут (т.к. они ссылаются только на свою библиотеку), и когда будет готовиться результирующий набор файлов.
Пример 2: проект ссылается на библиотеку напрямую, а не через Nuget.
Опять возьмем три наших проекта: exe, dll1 и dll2. Допустим, мы также используем еще Jetbrains.Annotations, чтобы размечать код NotNull/CanBeNull аттрибутами и получать симпатичный статический анализ. Но вот незадача: для dll1 мы честно скачали пакет версии 9.2.0, а в dll2 мы просто попросили ReSharper добавить ссылку, что он и сделал. В итоге, в packages.config файле dll2 нет пакета с аттрибутами, а значит, если проект будет собираться в порядке dll2 --> dll1 --> exe, то мы получим ошибку, ведь Nuget пакет скачается только при сборе dll1!
Можно привести еще ряд примеров, когда разные настройки в проектах могут привести к веселым проблемам. Например, проекты для .Net 4.0 и .Net 4.5 могут ссылаться на одинаковые пакеты, но на разные библиотеки в них (см. Nuget help), а значит мы опять будем получать спецэффекты при сборке проектов. Однако лучше перейдем к SolutionCop.
Установка
SolutionCop доступен на Nuget, устанавливается он на уровень всего решения. После установки пакета необходимо создать xml файл с правилами, настроить их и встроить проверяльщика в процедуру сборки на CI.
Пошагово:
- Создаем xml файл с правилами (будем проверять сам SolutionCop):
SolutionCop.exe -s "C:\git\SolutionCop\src\SolutionCop.sln"
После этой команды возле sln файла появится файл SolutionCop.xml. В нем перечислены все правила, но все они выключены. Рассмотрим их позже.
- Настраиваем CI сервер так, чтобы SolutionCop запускался при каждом билде (решение со 100 проектами проверяется 3–4 секунды). Для TeamCity можно даже публиковать статус с более удобном формате. Для всех остальных — придется читать Error Output. Приложение вернет 0, если все правила выполнились успешно. Итак, командная строка для TeamCity:
SolutionCop.exe -s "C:\git\SolutionCop\src\SolutionCop.sln" -b TeamCity --suppress-success-status-message.
Последний аргумент необходим в случае, если TeamCity не пишет статуса модульных тестов (несмотря на то, что они выполнялись позднее). Он отключит вывод SolutionCop для случая, если все правила выполнились.
Правила
Итак, SolutionCop настроен, он теперь заглядывает в решение, но ничего не проверяет. А потому перечислю правила, которые могут пригодиться. Детальное описание их использование есть на GitHub, я просто перечислю интересности. Для каждого правила, конечно же, можно задавать исключения и пр.
- WarningLevel. Проверяет, что все проекты имеют Warning Level не ниже заданного.
- TreatWarningsAsErrors. Смежное с предыдущим. Тоже синхронизует настройку компиляции.
- TargetFrameworkVersion. Сихнронизует версию .Net для всех проектов
- SuppressWarnings. Сихнронизует список предупреждений, на которые компилятор будет закрывать глаза.
- FilesIncludedIntoProject. Проверяет, что все файлы, находящиеся в папке с проектом, включены в этот проект (дополнительно указыватся расширение). Невероятно полезное правило. Например, один раз при неправильном git merge у нас из проекта с модульными тестами выпало около трети файлов. Заметили это, конечно, не сразу, к тому времени мы упустили уже несколько багов. Также помогает с очисткой репозитороя, чтобы избежать кучи висящих файлов, которые никто не использует.
- ReferenceNuGetPackagesOnly. Запрещает динамически линковаться на библиотеки, которые не добавлены как NuGet пакеты. По факту это исправление примера 2 выше.
- NuGetPackagesUsage. Определяет Nuget пакеты, которые можно использовать. Эта настройка нужна для задач, когда в продукте есть несколько команд, у каждой свой репозиторий, они ссылаются друг на друга, а также на некоторые общие компоненты. И чтобы избежать проблем при объединении кода, можно определить общие правила для всех: какие версии пакетов кому можно использовать. В этом случае все правила хранятся отдельно от кода, в отдельном репозитории.
- SameNuGetPackageVersions. Запрещает использование разных Nuget пакетов в решении. Исправление ошибки из примера 1.
- NuGetAutomaticPackagesRestore. Проверяет, что все проекты восстанавливают Nuget пакеты в процессе сборки. Иначе разный порядок компиляции на CI сервере может привести к тому, что nuget пакеты не подгрузятся вовремя, так как какой-то проект пользовался тем, что его зависимости загружают другие проекты.
На деле, в SolutionCop есть и ряд других правил, которые помогут вычищать код. Я постарался перечислить те, которые могут понадобиться почти всем, кто разрабатывает на .Net.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.