在 TortoiseSVN 提交时检查 StyleCop 规则
此钩子在代码提交到存储库之前或之后使用 StyleCop 进行验证,以确保其符合验证规则。
引言
Bob Martin 叔叔 在他的经典著作《代码整洁之道:敏捷软件开发修炼之道》中提倡的理念之一是,你应该“让代码比你接手时更整洁。”
在我们的环境中,代码库已经维护了 20 多年。为了确保代码的一致性并突出潜在的编码违规,我们使用 StyleCop。开发人员负责在他们的 IDE 中检查代码,并且在自动化构建环境中收集团队和代码指标。代码库是稳定的,在很多情况下,您希望将功能性更改与重构分开。如果您在进行重大的功能更改的同时重新格式化代码,那么就很难回答这个问题:“有什么变化?”
我们当前的 StyleCop 检查是处于被动状态,这意味着您依赖开发人员来执行它,或者查看构建统计信息。对于大型代码库,您希望在修改代码时逐步解决问题。基于这种情况,理想的解决方案是在提交时向开发人员显示所有“代码异味”。如果有快速改进的机会,开发人员可以在代码的功能在他/她的脑海中仍然清晰时进行处理。利用 TortoiseSVN Hook,您可以配置 Stylecop 配置来评估您的代码,并在提交时显示所有违规行为。
这可能会被视为对开发人员的“老大哥”式强制。钩子的可配置性为开发人员和您的特定环境提供了灵活性。这消除了在不遵守标准时“推卸责任”的借口。
- TortoiseSVN 中的钩子函数允许触发 StyleCop 分析的时间。它允许在提交生命周期的 7 个不同位置触发该功能。
- StyleCop 的全部灵活性和可配置性可用于自定义体验。
- 你有源代码,还需要什么?
构建代码
此钩子的基础结合了 C# 中的 TortoiseSVN 预提交钩子 - 拯救自己的一些麻烦! 和 从代码运行 StyleCop 来处理每个提交的文件。 要编译,请创建一个控制台项目,并添加对 StyleCop DLL 的引用。务必包含 Rules DLL,因为没有违规行为将无法进行任何规则检查。在我的系统上,它们安装在C:\Program Files (x86)\StyleCop 4.7。 |
![]() |
用以下代码替换Program.c
using StyleCop;
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace TortoiseSVNStyleCop
{
internal class Program
{
public static void Main(string[] args)
{
int foundViolatons = 0;
string[] filePaths = File.ReadAllLines(args[0]);
string projectPath = GetRootPath(filePaths);
string settingsPath = Path.Combine
(System.Reflection.Assembly.GetExecutingAssembly().Location, @"Settings.StyleCop");
if (File.Exists(settingsPath))
{
settingsPath = null;
}
Console.Error.WriteLine("DEBUG: {0}", settingsPath);
StyleCopConsole styleCopConsole = new StyleCopConsole(settingsPath, false, null, null, true);
Configuration configuration = new Configuration(null);
CodeProject project = new CodeProject(0, projectPath, configuration);
foreach (string file in filePaths)
{
var loaded = styleCopConsole.Core.Environment.AddSourceCode(project, file, null);
}
List<violation> violations = new List<violation>();
styleCopConsole.ViolationEncountered += ((sender, arguments) => violations.Add(arguments.Violation));
List<string> output = new List<string>();
styleCopConsole.OutputGenerated += ((sender, arguments) => output.Add(arguments.Output));
styleCopConsole.Start(new[] { project }, true);
foreach (string file in filePaths)
{
List<violation> fileViolations = violations.FindAll(viol => viol.SourceCode.Path == file);
if (fileViolations.Count > 0)
{
foundViolatons = 1;
Console.Error.WriteLine("{0} - {1} violations.", fileViolations[0].SourceCode.Name, fileViolations.Count);
foreach (Violation violation in fileViolations)
{
Console.Error.WriteLine(" {0}: Line {1}-{2}", violation.Rule.CheckId, violation.Line, violation.Message);
}
}
}
Environment.Exit(foundViolatons);
}
private static string GetRootPath(string[] filePaths)
{
if (filePaths.Length > 0)
{
string[] testAgainst = filePaths[0].Split('/');
int noOfLevels = testAgainst.Length;
foreach (string filePath in filePaths)
{
string[] current = filePath.Split('/');
int level;
for (level = 0; level <= Math.Min(noOfLevels, current.Length) - 1; level++)
{
if (testAgainst[level] != current[level])
{
break;
}
}
noOfLevels = Math.Min(noOfLevels, level);
}
return (testAgainst.Take(noOfLevels).Aggregate((m, n) => m + "/" + n));
}
return string.Empty;
}
}
}
注册钩子
在部署了 SVN 的目录中,右键单击并打开“设置”选项卡。在“钩子脚本”条目下,添加一个新的钩子。
工作副本路径是此钩子生效的根目录。如果钩子被调用时传入此目录,那将是很好的。这将允许您从该目录加载 StyleCop 设置。
根据您想何时或如何响应,您可以将其注册为预提交钩子。这将使您有机会停止提交,直到没有违规为止。如果您的模式是提交更改然后重构,您可能选择将其注册为更新后钩子。
如果您仍在积极开发中,只需将其指向您的bin\Debug 目录。您可以将 StyleCop.Settings
添加到您的项目中,但请确保“复制到输出目录”属性设置为“始终复制”,以便添加到您的部署中。
调用钩子
如果配置正确且存在 StyleCop 违规,您应该会看到类似以下内容:

您将在截图上看到一个 DEBUG 条目。在开发过程中,您可能想查看一些内部变量。只需写入 Console.Error
即可。
注释
这是我第一次使用 Visual Studio 2013(从 2010 迁移过来)并需要考虑的一些事项。
- 创建项目时,您必须将目标框架(项目属性)设置为 .NET Framework 4 - 它默认设置为 .NET Framework 4 Client
- 我最初链接 StyleCop 的想法是使用 NuGet 将其添加到我的项目中。这将创建编译代码的引用,但运行 StyleCop 将不会产生任何违规。您需要链接规则引擎 =>
StyleCop.CSharp.Rules
,它随正常的 StyleCop 部署一起提供。 - 在 Stack Overflow 上,有一个 LINQ 方法 来编写
GetRootPath()
,我本可以使用它。即使那样也可能更有效。我觉得它难以阅读,更喜欢逻辑更易读的老式方法。 - 似乎 StyleCop 会对传递给它的
projectPath
进行某种枚举和分析。如果当前项目目录中有大量文件,我不得不使用任务管理器终止进程。它可能已经完成,但它占用了内存,这就是GetRootPath()
函数的主要原因。 - 此代码是为了满足我们的公司需求而编写的。它实现了我的预期功能,并在几个小时内完成。目前,它似乎适用于我的个人用途,但需要更多的工业应用来解决一些问题。请查看 GitHub 仓库 以获取最新版本。
历史
- 2014 年 3 月 21 日:初始版本