Check for missing files in Visual Studio solution

I've a visual studio 2013 solution with several c# projects.
There are some class library projects as well as an asp.net mvc project.

The asp.net mvc project contains in addition to c# files also lots of html/javascript/css/images.
If i now commit this project, and forget to add a c# file in source control, the build will fail, and teamcity will show this to me.
But if i forget to add an image to source control, the build on teamcity will succeed even if this file is missing.


Is there a way to force teamcity to fail the build in this case?

4 comments
Comment actions Permalink

TeamCity uses MSBuild to build VisualStudio solutions. If MSBuild fails - build on TeamCity fails. So, for the files that are consumed by compiler it comes oout-of-the box.
For resource files there is no way of providing such detection, as they are neither consumed, nor inspected by compiler and MSBuild

0
Comment actions Permalink

This is what i was afraid of.
I now did create a unit test for this. It scans all project files, and check that all files are where they must be.
It's a a hack, but it's ok for me.
If one of our devs forget to add and commit a file in sourcontrol which was previously added in visual studio, this unittest will fail.

[TestClass]     public class SolutionCheckTest     {         //static namespace used for xml resolution         readonly XNamespace csprojNamespace = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003");         [TestMethod]         public void TestMethod1()         {             //load the solution file with full path             string solutionFile = this.GetSolutionFile("MySolution.sln");             //load a list of all projects             List<string> projects = this.GetAllProjects(solutionFile);             //list of all missing files. Will be used only if there any             List<string> missingFiles = new List<string>();             foreach (var project in projects)             {                 FileInfo fi = new FileInfo(project);                 XDocument xdoc = XDocument.Load(project);                 foreach (var element in xdoc.Descendants(this.csprojNamespace + "ItemGroup").Descendants()                     .Where(x => x.Name == this.csprojNamespace + "Content" | x.Name == this.csprojNamespace + "Compile"))                 {                     string fileMustExist = element.Attribute("Include").Value;                     //the ' character which can occur in filenames is encoded in xml, and needs to be unencoded for comparison                     fileMustExist = fileMustExist.Replace("%27", "'");                     if (!File.Exists(Path.Combine(fi.DirectoryName, fileMustExist)))                     {                         missingFiles.Add(string.Format("Project: {0}, Filename: {1}", fi.Name, fileMustExist));                     }                 }             }             //If missingFiles.Count is bigger than 0, this means there are missing files             //All missing files are then put into one fail output message             if (missingFiles.Count > 0)             {                 string output = "The following files are missing:" + Environment.NewLine;                 foreach (var missingFile in missingFiles)                 {                     output += missingFile + Environment.NewLine;                 }                 Assert.Fail(output);             }         }         private string GetSolutionFile(string SolutionFileName)         {             var currentDirectory = new DirectoryInfo(System.IO.Path.GetDirectoryName(new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath));             while (!currentDirectory.GetFiles("*", SearchOption.TopDirectoryOnly).Any(x => x.Name == SolutionFileName))             {                 currentDirectory = currentDirectory.Parent;             }             return currentDirectory.FullName + "\\" + SolutionFileName;         }         public List<string> GetAllProjects(string solutionFile)         {             var content = File.ReadAllText(solutionFile);             Regex projReg = new Regex("Project\\(\"\\{[\\w-]*\\}\"\\) = \"([\\w _]*.*)\", \"(.*\\.(cs|vcx|vb)proj)\"", RegexOptions.Compiled);             var matches = projReg.Matches(content).Cast<Match>();             List<string> projects = matches.Select(x => x.Groups[2].Value).ToList();             for (int i = 0; i < projects.Count; ++i)             {                 if (!Path.IsPathRooted(projects[i]))                 {                     projects[i] = Path.Combine(Path.GetDirectoryName(solutionFile), projects[i]);                 }                 projects[i] = Path.GetFullPath(projects[i]);             }             return projects;         }     }

0
Comment actions Permalink

That's a nice solution!
What you can do more is to try to report custom build status from the test itself, so that you can see that some files are missing directly from overview page

0
Comment actions Permalink

Hi Manuel,

I'm not familiar with asp.net mvc, but for one of our .csproj files I made a modification described here. This makes MSBuild fail if it cannot find one or more of the Content entries, so, consequently, TC will fail the build. In our case, the content was .sql files. Perhaps you can use a similar approach in your projects for other file types.

Regards,
Oleg.

0

Please sign in to leave a comment.