Click here to Skip to main content
15,884,628 members
Articles / Programming Languages / C#

Check StyleCop Rules on TortoiseSVN Commit

Rate me:
Please Sign up or sign in to vote.
4.71/5 (3 votes)
24 Mar 2014CPOL4 min read 22.8K   83   11   1
This hook validates code with StyleCop before or after they are checked in to ensure they conform to validation rules.

Introduction

One of the mantras encouraged by Uncle Bob Martin in his classic book Clean Code: a Handbook of Agile Craftsmanship is that you should "leave the code cleaner than how you found it."

In our environment, the code base has been actively maintained for over 20 years. To ensure code conformity and highlight potential coding violations, we use StyleCop. Developers are responsible to check code in their IDEs and team and code metrics are collected in an automated build environment. The code base is stable and in many instances, you want to separate functional changes from re-factoring. If you reformat code alongside major functional changes, it becomes difficult to answer the question: "What changed?".

Our current StyleCop checking is passive in the sense that you rely on the developer to do it or, look at the build statistics. With a large code base, you want to incrementally tackle the problem as you touch the code. Based on this scenario, the ideal solution would show the developer all "code smells" during check-in. If there are quick wins, the developer can tackle it while the functionality of the code in question is fresh in their mind. Building on the TortoiseSVN Hook allows you to use Stylecop configurations to evaluate your code and show all violations during check-in.

This may be viewed as a "big brother" imposition on the developer. The configurability of the hook gives flexibility to the developer and your particular environment. This removes the plausible deniability excuse if standards are not followed.

  • The hook function in TortoiseSVN allows when to trigger the StyleCop analysis. It allows for 7 different places in the check-in life cycle to trigger the functionality.
  • The full flexibility and configurability of StyleCop can be used to customize the experience.
  • You have the source code, what more do you need?

Building the Code

This basis for this hook is a combination of TortoiseSVN pre-commit hook in C# - Save yourself some troubles! and Running StyleCop from Your Code for each file checked in.

To compile, create a console project, and add references to the StyleCop DLLs. Make sure to include the Rules DLL as you will not get no rule checking without any violations. On my system, they were installed at C:\Program Files (x86)\StyleCop 4.7.

StyleCop References

Replace the Program.c with the following code:

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;
        }
    }
}

Registering the Hook

In a directory where SVN is deployed, right click and open the Settings tab. On the Hooks Scripts entry, add a new hook.

StyleCop References

The working copy path is the root directory where this hook is active. It would be nice if this directory is passed in when the hook is invoked. This would allow you to get the StyleCop settings to be loaded from this directory.

Depending on when or how you want to react, you can register it as a pre-commit hook. This will give you the option to stop until it has no violations. If your model is to check in changes and then re-factor, you may elect to register it as a post update hook.

StyleCop References

If you are still actively developing - just point it to your bin\Debug. You can add the StyleCop.Settings to your project but make sure the Copy To Output property is set to Copy Always to add to your deployment.

Invoking the Hook

If configuration is correct and you have StyleCop violations, you should see something like:

StyleCop References

You will see a DEBUG entry on the screenshot. During development, you may want to see some internal variables. Simply write out to Console.Error.

Comments

This was my first project using Visual Studio 2013 (coming from 2010) and a couple of things to consider.

  • When you create the project, you have to set the Target framework (project properties) to .NET Framework 4 - it defaults to .NET Framework 4 Client
  • My initial reaction to linking in StyleCop was to use NuGet to add it to my project. This will create the references to compile the code, but running StyleCop will give you no violations. You need the rules engine linked in => StyleCop.CSharp.Rules which is deployed with the normal StyleCop deployment
  • On Stack Overflow, there is a LINQ way to write GetRootPath() that I could have used. Even that may be more efficient. I find it unreadable and prefer the old fashioned way where the logic is more readable.
  • It seems StyleCop does some sort of enumeration and analysis of the projectPath passed to it. If the working project directory has a large number of files in it, I had to kill the process in task manager. It may have completed but it was eating up memory which is the main reason for the GetRootPath() function.
  • This code was written to meet our corporate needs. It does what I intended it for and put it together in a couple of hours. Right now, it seems to work for my personal use, but needs more industrial application to work out the kinks. Check the GitHub Repository for the most recent version.

History

  • 21st March, 2014: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
I've been developing software since the late 1980's. I started off using Pascal, spent a couple of decades using C / C++ followed by Java and currently C#. Professionally I've been a Programmer, Developer, Software Engineer, Project Manager, SCRUM Master, Agile Coach even a director of Software Engineering.

I enjoy writing code and the creative inputs required to do it well.

Comments and Discussions

 
GeneralMy 5 Pin
Omar Gameel Salem24-Mar-14 23:20
professionalOmar Gameel Salem24-Mar-14 23:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.