diff --git a/src/GitVersionCore.Tests/ExecuteCoreTests.cs b/src/GitVersionCore.Tests/ExecuteCoreTests.cs new file mode 100644 index 0000000000..a74967523e --- /dev/null +++ b/src/GitVersionCore.Tests/ExecuteCoreTests.cs @@ -0,0 +1,95 @@ +using System; +using System.Text; + +using GitVersion; +using GitVersion.Helpers; + +using NUnit.Framework; + +using Shouldly; + +[TestFixture] +public class ExecuteCoreTests +{ + IFileSystem fileSystem; + + [SetUp] + public void SetUp() + { + fileSystem = new FileSystem(); + } + + [Test] + public void CacheFileExistsOnDisk() + { + const string versionCacheFileContent = @" +Major: 4 +Minor: 10 +Patch: 3 +PreReleaseTag: test.19 +PreReleaseTagWithDash: -test.19 +BuildMetaData: +BuildMetaDataPadded: +FullBuildMetaData: Branch.feature/test.Sha.dd2a29aff0c948e1bdf3dabbe13e1576e70d5f9f +MajorMinorPatch: 4.10.3 +SemVer: 4.10.3-test.19 +LegacySemVer: 4.10.3-test19 +LegacySemVerPadded: 4.10.3-test0019 +AssemblySemVer: 4.10.3.0 +FullSemVer: 4.10.3-test.19 +InformationalVersion: 4.10.3-test.19+Branch.feature/test.Sha.dd2a29aff0c948e1bdf3dabbe13e1576e70d5f9f +BranchName: feature/test +Sha: dd2a29aff0c948e1bdf3dabbe13e1576e70d5f9f +NuGetVersionV2: 4.10.3-test0019 +NuGetVersion: 4.10.3-test0019 +CommitsSinceVersionSource: 19 +CommitsSinceVersionSourcePadded: 0019 +CommitDate: 2015-11-10 +"; + + var versionAndBranchFinder = new ExecuteCore(fileSystem); + + var info = RepositoryScope(versionAndBranchFinder, (fixture, vv) => + { + fileSystem.WriteAllText(vv.FileName, versionCacheFileContent); + vv = versionAndBranchFinder.ExecuteGitVersion(null, null, null, null, false, fixture.RepositoryPath, null); + vv.AssemblySemVer.ShouldBe("4.10.3.0"); + }); + + info.ShouldContain("Deserializing version variables from cache file", () => info); + } + + [Test] + public void CacheFileIsMissing() + { + var info = RepositoryScope(); + info.ShouldContain("yml not found", () => info); + } + + string RepositoryScope(ExecuteCore executeCore = null, Action fixtureAction = null) + { + // Make sure GitVersion doesn't trigger build server mode when we are running the tests + Environment.SetEnvironmentVariable("APPVEYOR", null); + var infoBuilder = new StringBuilder(); + Action infoLogger = s => { infoBuilder.AppendLine(s); }; + executeCore = executeCore ?? new ExecuteCore(fileSystem); + + Logger.SetLoggers(infoLogger, s => { }, s => { }); + + using (var fixture = new EmptyRepositoryFixture(new Config())) + { + fixture.Repository.MakeACommit(); + var vv = executeCore.ExecuteGitVersion(null, null, null, null, false, fixture.RepositoryPath, null); + + vv.AssemblySemVer.ShouldBe("0.1.0.0"); + vv.FileName.ShouldNotBeNullOrEmpty(); + + if (fixtureAction != null) + { + fixtureAction(fixture, vv); + } + } + + return infoBuilder.ToString(); + } +} \ No newline at end of file diff --git a/src/GitVersionCore.Tests/GitDirFinderTests.cs b/src/GitVersionCore.Tests/GitDirFinderTests.cs deleted file mode 100644 index acae8f587d..0000000000 --- a/src/GitVersionCore.Tests/GitDirFinderTests.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.IO; -using GitVersion; -using LibGit2Sharp; -using NUnit.Framework; - -[TestFixture] -public class GitDirFinderTests -{ - string workDirectory; - string gitDirectory; - - [SetUp] - public void CreateTemporaryRepository() - { - workDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - - gitDirectory = Repository.Init(workDirectory) - .TrimEnd(new[] { Path.DirectorySeparatorChar }); - - Assert.NotNull(gitDirectory); - } - - [TearDown] - public void Cleanup() - { - Directory.Delete(workDirectory, true); - } - - [Test] - public void From_WorkingDirectory() - { - Assert.AreEqual(gitDirectory, GitDirFinder.TreeWalkForDotGitDir(workDirectory)); - } - - [Test] - public void From_WorkingDirectory_Parent() - { - var parentDirectory = Directory.GetParent(workDirectory).FullName; - Assert.Null(GitDirFinder.TreeWalkForDotGitDir(parentDirectory)); - } - - [Test] - public void From_GitDirectory() - { - Assert.AreEqual(gitDirectory, GitDirFinder.TreeWalkForDotGitDir(gitDirectory)); - } - - [Test] - public void From_RefsDirectory() - { - var refsDirectory = Path.Combine(gitDirectory, "refs"); - Assert.AreEqual(gitDirectory, GitDirFinder.TreeWalkForDotGitDir(refsDirectory)); - } -} diff --git a/src/GitVersionCore.Tests/GitVersionCore.Tests.csproj b/src/GitVersionCore.Tests/GitVersionCore.Tests.csproj index e63c1b5a99..94abc9db78 100644 --- a/src/GitVersionCore.Tests/GitVersionCore.Tests.csproj +++ b/src/GitVersionCore.Tests/GitVersionCore.Tests.csproj @@ -72,6 +72,10 @@ ..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Rocks.dll True + + ..\packages\NSubstitute.1.9.2.0\lib\net45\NSubstitute.dll + True + ..\packages\NUnitTestAdapter.2.0.0\lib\nunit.core.dll True @@ -125,7 +129,6 @@ - @@ -175,6 +178,7 @@ + diff --git a/src/GitVersionCore.Tests/TestFileSystem.cs b/src/GitVersionCore.Tests/TestFileSystem.cs index dba500611c..2e2a83e24a 100644 --- a/src/GitVersionCore.Tests/TestFileSystem.cs +++ b/src/GitVersionCore.Tests/TestFileSystem.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; + using GitVersion.Helpers; public class TestFileSystem : IFileSystem @@ -35,9 +37,13 @@ public string ReadAllText(string path) public void WriteAllText(string file, string fileContents) { if (fileSystem.ContainsKey(file)) + { fileSystem[file] = fileContents; + } else + { fileSystem.Add(file, fileContents); + } } public IEnumerable DirectoryGetFiles(string directory, string searchPattern, SearchOption searchOption) @@ -49,4 +55,24 @@ public Stream OpenWrite(string path) { return new TestStream(path, this); } + + public Stream OpenRead(string path) + { + if (fileSystem.ContainsKey(path)) + { + var content = fileSystem[path]; + return new MemoryStream(Encoding.UTF8.GetBytes(content)); + } + + throw new FileNotFoundException("File not found.", path); + } + + public void CreateDirectory(string path) + { + } + + public long GetLastDirectoryWrite(string path) + { + return 1; + } } \ No newline at end of file diff --git a/src/GitVersionCore.Tests/packages.config b/src/GitVersionCore.Tests/packages.config index 94bf961fe7..2b351bce70 100644 --- a/src/GitVersionCore.Tests/packages.config +++ b/src/GitVersionCore.Tests/packages.config @@ -7,6 +7,7 @@ + diff --git a/src/GitVersionCore/ExecuteCore.cs b/src/GitVersionCore/ExecuteCore.cs index 16389b46af..6b41d4ecf6 100644 --- a/src/GitVersionCore/ExecuteCore.cs +++ b/src/GitVersionCore/ExecuteCore.cs @@ -4,17 +4,25 @@ namespace GitVersion using System.Linq; using GitVersion.Helpers; - public static class ExecuteCore + using LibGit2Sharp; + + public class ExecuteCore { - public static VersionVariables ExecuteGitVersion(IFileSystem fileSystem, string targetUrl, string dynamicRepositoryLocation, Authentication authentication, string targetBranch, bool noFetch, string workingDirectory, string commitId) + readonly IFileSystem fileSystem; + readonly GitVersionCache gitVersionCache; + + public ExecuteCore(IFileSystem fileSystem) + { + if (fileSystem == null) throw new ArgumentNullException("fileSystem"); + + this.fileSystem = fileSystem; + gitVersionCache = new GitVersionCache(fileSystem); + } + + public VersionVariables ExecuteGitVersion(string targetUrl, string dynamicRepositoryLocation, Authentication authentication, string targetBranch, bool noFetch, string workingDirectory, string commitId) { // Normalise if we are running on build server var gitPreparer = new GitPreparer(targetUrl, dynamicRepositoryLocation, authentication, noFetch, workingDirectory); - var applicableBuildServers = BuildServerList.GetApplicableBuildServers(); - var buildServer = applicableBuildServers.FirstOrDefault(); - - gitPreparer.Initialise(buildServer != null, ResolveCurrentBranch(buildServer, targetBranch)); - var dotGitDirectory = gitPreparer.GetDotGitDirectory(); var projectRoot = gitPreparer.GetProjectRootDirectory(); Logger.WriteInfo(string.Format("Project root is: " + projectRoot)); @@ -23,28 +31,85 @@ public static VersionVariables ExecuteGitVersion(IFileSystem fileSystem, string // TODO Link to wiki article throw new Exception(string.Format("Failed to prepare or find the .git directory in path '{0}'.", workingDirectory)); } - VersionVariables variables; - var versionFinder = new GitVersionFinder(); - var configuration = ConfigurationProvider.Provide(projectRoot, fileSystem); - - using (var repo = RepositoryLoader.GetRepo(dotGitDirectory)) + + using (var repo = GetRepository(dotGitDirectory)) { - var gitVersionContext = new GitVersionContext(repo, configuration, commitId: commitId); - var semanticVersion = versionFinder.FindVersion(gitVersionContext); - variables = VariableProvider.GetVariablesFor(semanticVersion, gitVersionContext.Configuration, gitVersionContext.IsCurrentCommitTagged); + var versionVariables = gitVersionCache.LoadVersionVariablesFromDiskCache(repo, dotGitDirectory); + if (versionVariables == null) + { + versionVariables = ExecuteInternal(targetBranch, commitId, repo, gitPreparer, projectRoot); + gitVersionCache.WriteVariablesToDiskCache(repo, dotGitDirectory, versionVariables); + } + + return versionVariables; } + } - return variables; + public bool TryGetVersion(string directory, out VersionVariables versionVariables, bool noFetch, Authentication authentication) + { + try + { + versionVariables = ExecuteGitVersion(null, null, authentication, null, noFetch, directory, null); + return true; + } + catch (Exception ex) + { + Logger.WriteWarning("Could not determine assembly version: " + ex); + versionVariables = null; + return false; + } } - private static string ResolveCurrentBranch(IBuildServer buildServer, string targetBranch) + static string ResolveCurrentBranch(IBuildServer buildServer, string targetBranch) { - if (buildServer == null) return targetBranch; + if (buildServer == null) + { + return targetBranch; + } var currentBranch = buildServer.GetCurrentBranch() ?? targetBranch; Logger.WriteInfo("Branch from build environment: " + currentBranch); return currentBranch; } + + VersionVariables ExecuteInternal(string targetBranch, string commitId, IRepository repo, GitPreparer gitPreparer, string projectRoot) + { + var applicableBuildServers = BuildServerList.GetApplicableBuildServers(); + var buildServer = applicableBuildServers.FirstOrDefault(); + + gitPreparer.Initialise(buildServer != null, ResolveCurrentBranch(buildServer, targetBranch)); + + var versionFinder = new GitVersionFinder(); + var configuration = ConfigurationProvider.Provide(projectRoot, fileSystem); + + var gitVersionContext = new GitVersionContext(repo, configuration, commitId : commitId); + var semanticVersion = versionFinder.FindVersion(gitVersionContext); + + return VariableProvider.GetVariablesFor(semanticVersion, gitVersionContext.Configuration, gitVersionContext.IsCurrentCommitTagged); + } + + IRepository GetRepository(string gitDirectory) + { + try + { + var repository = new Repository(gitDirectory); + + var branch = repository.Head; + if (branch.Tip == null) + { + throw new WarningException("No Tip found. Has repo been initialized?"); + } + return repository; + } + catch (Exception exception) + { + if (exception.Message.Contains("LibGit2Sharp.Core.NativeMethods") || exception.Message.Contains("FilePathMarshaler")) + { + throw new WarningException("Restart of the process may be required to load an updated version of LibGit2Sharp."); + } + throw; + } + } } } \ No newline at end of file diff --git a/src/GitVersionCore/GitDirFinder.cs b/src/GitVersionCore/GitDirFinder.cs deleted file mode 100644 index 9478cc8da8..0000000000 --- a/src/GitVersionCore/GitDirFinder.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace GitVersion -{ - using System.IO; - using LibGit2Sharp; - - public class GitDirFinder - { - public static string TreeWalkForDotGitDir(string currentDirectory) - { - var gitDirectory = Repository.Discover(currentDirectory); - - if (gitDirectory != null) - { - return gitDirectory.TrimEnd(Path.DirectorySeparatorChar); - } - - return null; - } - } -} \ No newline at end of file diff --git a/src/GitVersionCore/GitPreparer.cs b/src/GitVersionCore/GitPreparer.cs index 2c40f97fc7..11a8e13a4b 100644 --- a/src/GitVersionCore/GitPreparer.cs +++ b/src/GitVersionCore/GitPreparer.cs @@ -3,6 +3,7 @@ namespace GitVersion using System; using System.IO; using System.Linq; + using LibGit2Sharp; public class GitPreparer @@ -83,25 +84,26 @@ static bool GitRepoHasMatchingRemote(string possiblePath, string targetUrl) { return false; } - } public string GetDotGitDirectory() { if (IsDynamicGitRepository) + { return DynamicGitRepositoryPath; + } - return GitDirFinder.TreeWalkForDotGitDir(targetPath); + return Repository.Discover(targetPath); } public string GetProjectRootDirectory() { if (IsDynamicGitRepository) - return this.targetPath; + return targetPath; - var gitDir = GitDirFinder.TreeWalkForDotGitDir(this.targetPath); + var gitDir = Repository.Discover(targetPath); - if (String.IsNullOrEmpty(gitDir)) + if (string.IsNullOrEmpty(gitDir)) throw new DirectoryNotFoundException("Can't find the .git directory in " + targetPath); return Directory.GetParent(gitDir).FullName; @@ -132,7 +134,7 @@ static string CreateDynamicRepository(string targetPath, Authentication authenti return gitDirectory; } - private static void CloneRepository(string repositoryUrl, string gitDirectory, Authentication authentication) + static void CloneRepository(string repositoryUrl, string gitDirectory, Authentication authentication) { Credentials credentials = null; if (!string.IsNullOrWhiteSpace(authentication.Username) && !string.IsNullOrWhiteSpace(authentication.Password)) @@ -150,12 +152,12 @@ private static void CloneRepository(string repositoryUrl, string gitDirectory, A try { - Repository.Clone(repositoryUrl, gitDirectory, - new CloneOptions - { - Checkout = false, - CredentialsProvider = (url, usernameFromUrl, types) => credentials - }); + var cloneOptions = new CloneOptions + { + Checkout = false, + CredentialsProvider = (url, usernameFromUrl, types) => credentials + }; + Repository.Clone(repositoryUrl, gitDirectory, cloneOptions); } catch (LibGit2SharpException ex) { @@ -172,7 +174,7 @@ private static void CloneRepository(string repositoryUrl, string gitDirectory, A { throw new Exception("Not found: The repository was not found"); } - + throw new Exception("There was an unknown problem with the Git repository you provided"); } } diff --git a/src/GitVersionCore/GitVersionCache.cs b/src/GitVersionCore/GitVersionCache.cs new file mode 100644 index 0000000000..c3ea184a10 --- /dev/null +++ b/src/GitVersionCore/GitVersionCache.cs @@ -0,0 +1,112 @@ +namespace GitVersion +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Security.Cryptography; + using System.Text; + using GitVersion.Helpers; + using LibGit2Sharp; + using YamlDotNet.Serialization; + + public class GitVersionCache + { + readonly IFileSystem fileSystem; + + public GitVersionCache(IFileSystem fileSystem) + { + this.fileSystem = fileSystem; + } + + public void WriteVariablesToDiskCache(IRepository repo, string gitDir, VersionVariables variablesFromCache) + { + var cacheFileName = GetCacheFileName(GetKey(repo, gitDir), GetCacheDir(gitDir)); + variablesFromCache.FileName = cacheFileName; + + using (var stream = fileSystem.OpenWrite(cacheFileName)) + { + using (var sw = new StreamWriter(stream)) + { + Dictionary dictionary; + using (Logger.IndentLog("Creating dictionary")) + { + dictionary = variablesFromCache.ToDictionary(x => x.Key, x => x.Value); + } + + using (Logger.IndentLog("Storing version variables to cache file " + cacheFileName)) + { + var serializer = new Serializer(); + serializer.Serialize(sw, dictionary); + } + } + } + } + + public VersionVariables LoadVersionVariablesFromDiskCache(IRepository repo, string gitDir) + { + using (Logger.IndentLog("Loading version variables from disk cache")) + { + // If the cacheDir already exists, CreateDirectory just won't do anything (it won't fail). @asbjornu + + var cacheDir = GetCacheDir(gitDir); + fileSystem.CreateDirectory(cacheDir); + var cacheFileName = GetCacheFileName(GetKey(repo, gitDir), cacheDir); + VersionVariables vv = null; + if (fileSystem.Exists(cacheFileName)) + { + using (Logger.IndentLog("Deserializing version variables from cache file " + cacheFileName)) + { + try + { + vv = VersionVariables.FromFile(cacheFileName, fileSystem); + } + catch (Exception ex) + { + Logger.WriteWarning("Unable to read cache file " + cacheFileName + ", deleting it."); + Logger.WriteInfo(ex.ToString()); + try + { + fileSystem.Delete(cacheFileName); + } + catch (Exception deleteEx) + { + Logger.WriteWarning(string.Format("Unable to delete corrupted version cache file {0}. Got {1} exception.", cacheFileName, deleteEx.GetType().FullName)); + } + } + } + } + else + { + Logger.WriteInfo("Cache file " + cacheFileName + " not found."); + } + + return vv; + } + } + + string GetKey(IRepository repo, string gitDir) + { + // Maybe using timestamp in .git/refs directory is enough? + var ticks = fileSystem.GetLastDirectoryWrite(Path.Combine(gitDir, "refs")); + return string.Format("{0}:{1}:{2}:{3}", gitDir, repo.Head.CanonicalName, repo.Head.Tip.Sha, ticks); + } + + static string GetCacheFileName(string key, string cacheDir) + { + string cacheKey; + using (var sha1 = SHA1.Create()) + { + // Make a shorter key by hashing, to avoid having to long cache filename. + cacheKey = BitConverter.ToString(sha1.ComputeHash(Encoding.UTF8.GetBytes(key))).Replace("-", ""); + } + var cacheFileName = string.Concat(Path.Combine(cacheDir, cacheKey), ".yml"); + return cacheFileName; + } + + static string GetCacheDir(string gitDir) + { + return Path.Combine(gitDir, "gitversion_cache"); + } + } +} \ No newline at end of file diff --git a/src/GitVersionCore/GitVersionCore.csproj b/src/GitVersionCore/GitVersionCore.csproj index 7974790b97..e554cc5eab 100644 --- a/src/GitVersionCore/GitVersionCore.csproj +++ b/src/GitVersionCore/GitVersionCore.csproj @@ -110,6 +110,7 @@ + @@ -140,7 +141,6 @@ - @@ -152,7 +152,6 @@ - diff --git a/src/GitVersionCore/GitVersionFinder.cs b/src/GitVersionCore/GitVersionFinder.cs index 44ccf70365..cef4f6297b 100644 --- a/src/GitVersionCore/GitVersionFinder.cs +++ b/src/GitVersionCore/GitVersionFinder.cs @@ -1,9 +1,7 @@ namespace GitVersion { using System.IO; - using System.Linq; using GitVersion.VersionCalculation; - using LibGit2Sharp; public class GitVersionFinder { diff --git a/src/GitVersionCore/Helpers/FileSystem.cs b/src/GitVersionCore/Helpers/FileSystem.cs index a92f5ea5bc..5348177b7b 100644 --- a/src/GitVersionCore/Helpers/FileSystem.cs +++ b/src/GitVersionCore/Helpers/FileSystem.cs @@ -2,6 +2,7 @@ namespace GitVersion.Helpers { using System.Collections.Generic; using System.IO; + using System.Linq; public class FileSystem : IFileSystem { @@ -44,5 +45,25 @@ public Stream OpenWrite(string path) { return File.OpenWrite(path); } + + public Stream OpenRead(string path) + { + return File.OpenRead(path); + } + + public void CreateDirectory(string path) + { + Directory.CreateDirectory(path); + } + + public long GetLastDirectoryWrite(string path) + { + return new DirectoryInfo(path) + .GetDirectories("*.*", SearchOption.AllDirectories) + .Select(d => d.LastWriteTimeUtc) + .DefaultIfEmpty() + .Max() + .Ticks; + } } } \ No newline at end of file diff --git a/src/GitVersionCore/Helpers/IFileSystem.cs b/src/GitVersionCore/Helpers/IFileSystem.cs index e7c3a0b769..dcc8c66ccb 100644 --- a/src/GitVersionCore/Helpers/IFileSystem.cs +++ b/src/GitVersionCore/Helpers/IFileSystem.cs @@ -13,5 +13,8 @@ public interface IFileSystem void WriteAllText(string file, string fileContents); IEnumerable DirectoryGetFiles(string directory, string searchPattern, SearchOption searchOption); Stream OpenWrite(string path); + Stream OpenRead(string path); + void CreateDirectory(string path); + long GetLastDirectoryWrite(string path); } } \ No newline at end of file diff --git a/src/GitVersionCore/Logger.cs b/src/GitVersionCore/Logger.cs index d880b0f9cc..573fedfb17 100644 --- a/src/GitVersionCore/Logger.cs +++ b/src/GitVersionCore/Logger.cs @@ -9,16 +9,16 @@ public static class Logger static readonly Regex ObscurePasswordRegex = new Regex("(https?://)(.+)(:.+@)", RegexOptions.Compiled); static string indent = string.Empty; - public static Action WriteInfo { get; private set; } - public static Action WriteWarning { get; private set; } - public static Action WriteError { get; private set; } - static Logger() { Reset(); } + public static Action WriteInfo { get; private set; } + public static Action WriteWarning { get; private set; } + public static Action WriteError { get; private set; } + public static IDisposable IndentLog(string operationDescription) { var start = DateTime.Now; @@ -43,6 +43,10 @@ static Action ObscurePassword(Action info) public static void SetLoggers(Action info, Action warn, Action error) { + if (info == null) throw new ArgumentNullException("info"); + if (warn == null) throw new ArgumentNullException("warn"); + if (error == null) throw new ArgumentNullException("error"); + WriteInfo = LogMessage(ObscurePassword(info), "INFO"); WriteWarning = LogMessage(ObscurePassword(warn), "WARN"); WriteError = LogMessage(ObscurePassword(error), "ERROR"); diff --git a/src/GitVersionCore/OutputVariables/VersionVariables.cs b/src/GitVersionCore/OutputVariables/VersionVariables.cs index 2870481acb..aac1b4c98c 100644 --- a/src/GitVersionCore/OutputVariables/VersionVariables.cs +++ b/src/GitVersionCore/OutputVariables/VersionVariables.cs @@ -1,9 +1,13 @@ namespace GitVersion { + using System; using System.Collections; using System.Collections.Generic; + using System.IO; using System.Linq; - using System.Reflection; + using GitVersion.Helpers; + + using YamlDotNet.Serialization; public class VersionVariables : IEnumerable> { @@ -54,7 +58,6 @@ public VersionVariables(string major, CommitsSinceVersionSourcePadded = commitsSinceVersionSourcePadded; } - public string Major { get; private set; } public string Minor { get; private set; } public string Patch { get; private set; } @@ -77,42 +80,69 @@ public VersionVariables(string major, public string CommitsSinceVersionSource { get; private set; } public string CommitsSinceVersionSourcePadded { get; private set; } + [ReflectionIgnore] public static IEnumerable AvailableVariables { get { return typeof(VersionVariables) - .GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance) + .GetProperties() + .Where(p => !p.GetCustomAttributes(typeof(ReflectionIgnoreAttribute), false).Any()) .Select(p => p.Name) - .Where(p => p != "AvailableVariables" && p != "Item") .OrderBy(a => a); } } public string CommitDate { get; set; } + [ReflectionIgnore] + public string FileName { get; set; } + + [ReflectionIgnore] public string this[string variable] { get { return (string)typeof(VersionVariables).GetProperty(variable).GetValue(this, null); } } - public IEnumerator> GetEnumerator() { var type = typeof(string); return typeof(VersionVariables) .GetProperties() - .Where(p => p.PropertyType == type && !p.GetIndexParameters().Any()) + .Where(p => p.PropertyType == type && !p.GetIndexParameters().Any() && !p.GetCustomAttributes(typeof(ReflectionIgnoreAttribute), false).Any()) .Select(p => new KeyValuePair(p.Name, (string)p.GetValue(this, null))) .GetEnumerator(); } - IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + public static VersionVariables FromDictionary(IEnumerable> properties) + { + var type = typeof(VersionVariables); + var ctor = type.GetConstructors().Single(); + var ctorArgs = ctor.GetParameters() + .Select(p => properties.Single(v => string.Equals(v.Key, p.Name, StringComparison.CurrentCultureIgnoreCase)).Value) + .Cast() + .ToArray(); + return (VersionVariables)Activator.CreateInstance(type, ctorArgs); + } + + public static VersionVariables FromFile(string filePath, IFileSystem fileSystem) + { + using (var stream = fileSystem.OpenRead(filePath)) + { + using (var reader = new StreamReader(stream)) + { + var dictionary = new Deserializer().Deserialize>(reader); + var versionVariables = FromDictionary(dictionary); + versionVariables.FileName = filePath; + return versionVariables; + } + } + } public bool TryGetValue(string variable, out string variableValue) { @@ -126,10 +156,13 @@ public bool TryGetValue(string variable, out string variableValue) return false; } - public bool ContainsKey(string variable) { return typeof(VersionVariables).GetProperty(variable) != null; } + + sealed class ReflectionIgnoreAttribute : Attribute + { + } } } \ No newline at end of file diff --git a/src/GitVersionCore/RepositoryLoader.cs b/src/GitVersionCore/RepositoryLoader.cs deleted file mode 100644 index 41e0dfece2..0000000000 --- a/src/GitVersionCore/RepositoryLoader.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace GitVersion -{ - using System; - using LibGit2Sharp; - - public class RepositoryLoader - { - public static Repository GetRepo(string gitDirectory) - { - try - { - var repository = new Repository(gitDirectory); - - var branch = repository.Head; - if (branch.Tip == null) - { - throw new WarningException("No Tip found. Has repo been initialized?"); - } - return repository; - } - catch (Exception exception) - { - if (exception.Message.Contains("LibGit2Sharp.Core.NativeMethods") || exception.Message.Contains("FilePathMarshaler")) - { - throw new WarningException("Restart of the process may be required to load an updated version of LibGit2Sharp."); - } - throw; - } - } - } -} \ No newline at end of file diff --git a/src/GitVersionExe.Tests/ExecCmdLineArgumentTest.cs b/src/GitVersionExe.Tests/ExecCmdLineArgumentTest.cs index 3499a470a4..ed1bdcd105 100644 --- a/src/GitVersionExe.Tests/ExecCmdLineArgumentTest.cs +++ b/src/GitVersionExe.Tests/ExecCmdLineArgumentTest.cs @@ -1,7 +1,6 @@ using System.IO; using GitVersion; - using NUnit.Framework; using Shouldly; @@ -61,35 +60,6 @@ public void InvalidArgumentsExitCodeShouldNotBeZero() } } - - [Test] - public void UsesGitVersionConfigWhenCreatingDynamicRepository() - { - var localRepoPath = PathHelper.GetTempPath(); - var repoBasePath = Path.GetDirectoryName(PathHelper.GetTempPath()); - Directory.CreateDirectory(localRepoPath); - - try - { - using (var remote = new EmptyRepositoryFixture(new Config())) - { - remote.Repository.MakeACommit(); - var configFile = Path.Combine(localRepoPath, "GitVersionConfig.yaml"); - File.WriteAllText(configFile, "next-version: 1.0.0"); - - var arguments = string.Format(" /url {0} /dynamicRepoLocation {1} /b master", remote.RepositoryPath, repoBasePath); - var results = GitVersionHelper.ExecuteIn(localRepoPath, arguments, false); - results.OutputVariables.SemVer.ShouldBe("1.0.0"); - } - } - finally - { - DeleteHelper.DeleteGitRepository(localRepoPath); - DeleteHelper.DeleteGitRepository(repoBasePath); - } - } - - [Test] public void InvalidWorkingDirectoryCrashesWithInformativeMessage() { diff --git a/src/GitVersionExe.Tests/ExecutionResults.cs b/src/GitVersionExe.Tests/ExecutionResults.cs index 450011df24..7158939b67 100644 --- a/src/GitVersionExe.Tests/ExecutionResults.cs +++ b/src/GitVersionExe.Tests/ExecutionResults.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Web.Script.Serialization; using GitVersion; @@ -22,13 +20,7 @@ public virtual VersionVariables OutputVariables get { var outputVariables = new JavaScriptSerializer().Deserialize>(Output); - var type = typeof(VersionVariables); - var ctor = type.GetConstructors().Single(); - var ctorArgs = ctor.GetParameters() - .Select(p => outputVariables.Single(v => v.Key.ToLower() == p.Name.ToLower()).Value) - .Cast() - .ToArray(); - return (VersionVariables) Activator.CreateInstance(type, ctorArgs); + return VersionVariables.FromDictionary(outputVariables); } } } \ No newline at end of file diff --git a/src/GitVersionExe/SpecifiedArgumentRunner.cs b/src/GitVersionExe/SpecifiedArgumentRunner.cs index 30324e4aae..33d7dda88b 100644 --- a/src/GitVersionExe/SpecifiedArgumentRunner.cs +++ b/src/GitVersionExe/SpecifiedArgumentRunner.cs @@ -3,6 +3,7 @@ namespace GitVersion using System; using System.Collections.Generic; using System.Linq; + using GitVersion.Helpers; class SpecifiedArgumentRunner @@ -19,7 +20,8 @@ public static void Run(Arguments arguments, IFileSystem fileSystem) var targetBranch = arguments.TargetBranch; var commitId = arguments.CommitId; - var variables = ExecuteCore.ExecuteGitVersion(fileSystem, targetUrl, dynamicRepositoryLocation, authentication, targetBranch, noFetch, targetPath, commitId); + var executeCore = new ExecuteCore(fileSystem); + var variables = executeCore.ExecuteGitVersion(targetUrl, dynamicRepositoryLocation, authentication, targetBranch, noFetch, targetPath, commitId); if (arguments.Output == OutputType.BuildServer) { @@ -91,6 +93,7 @@ static bool RunExecCommandIfNeeded(Arguments args, string workingDirectory, Vers Logger.WriteInfo, Logger.WriteError, null, args.Exec, args.ExecArgs, workingDirectory, GetEnvironmentalVariables(variables)); + if (results != 0) throw new WarningException(string.Format("Execution of {0} failed, non-zero return code", args.Exec)); diff --git a/src/GitVersionTask.Tests/GetVersionTaskTests.cs b/src/GitVersionTask.Tests/GetVersionTaskTests.cs index 707f7b4eda..0030d56da0 100644 --- a/src/GitVersionTask.Tests/GetVersionTaskTests.cs +++ b/src/GitVersionTask.Tests/GetVersionTaskTests.cs @@ -16,10 +16,7 @@ public void OutputsShouldMatchVariableProvider() .Where(p => p.GetCustomAttributes(typeof(OutputAttribute), false).Any()) .Select(p => p.Name); - var variablesProperties = typeof(VersionVariables) - .GetProperties() - .Select(p => p.Name) - .Except(new[] { "AvailableVariables", "Item" }); + var variablesProperties = VersionVariables.AvailableVariables; taskProperties.ShouldBe(variablesProperties, ignoreOrder: true); } diff --git a/src/GitVersionTask.Tests/GitVersionTaskDirectoryTests.cs b/src/GitVersionTask.Tests/GitVersionTaskDirectoryTests.cs index 0c80458a56..497b59526d 100644 --- a/src/GitVersionTask.Tests/GitVersionTaskDirectoryTests.cs +++ b/src/GitVersionTask.Tests/GitVersionTaskDirectoryTests.cs @@ -1,37 +1,44 @@ using System; using System.IO; + +using GitVersion; + using LibGit2Sharp; + using NUnit.Framework; [TestFixture] public class GitVersionTaskDirectoryTests { - string workDirectory; + ExecuteCore executeCore; string gitDirectory; + string workDirectory; + [SetUp] public void CreateTemporaryRepository() { workDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - gitDirectory = Repository.Init(workDirectory) - .TrimEnd(new[] { Path.DirectorySeparatorChar }); - + .TrimEnd(Path.DirectorySeparatorChar); + executeCore = new ExecuteCore(new TestFileSystem()); Assert.NotNull(gitDirectory); } + [TearDown] public void Cleanup() { Directory.Delete(workDirectory, true); } + [Test] public void Finds_GitDirectory() { try { - VersionAndBranchFinder.GetVersion(workDirectory, null, true, null); + executeCore.ExecuteGitVersion(null, null, null, null, true, workDirectory, null); } catch (Exception ex) { @@ -41,6 +48,7 @@ public void Finds_GitDirectory() } } + [Test] public void Finds_GitDirectory_In_Parent() { @@ -49,7 +57,7 @@ public void Finds_GitDirectory_In_Parent() try { - VersionAndBranchFinder.GetVersion(childDir, null, true, null); + executeCore.ExecuteGitVersion(null, null, null, null, true, childDir, null); } catch (Exception ex) { @@ -58,4 +66,4 @@ public void Finds_GitDirectory_In_Parent() Assert.IsNotAssignableFrom(ex); } } -} +} \ No newline at end of file diff --git a/src/GitVersionTask/AssemblyInfo.cs b/src/GitVersionTask/AssemblyInfo.cs index 782d9b63f7..949c083a34 100644 --- a/src/GitVersionTask/AssemblyInfo.cs +++ b/src/GitVersionTask/AssemblyInfo.cs @@ -3,4 +3,4 @@ [assembly: AssemblyTitle("GitVersionTask")] [assembly: AssemblyProduct("GitVersionTask")] [assembly: AssemblyVersion("1.0.0")] -[assembly: AssemblyFileVersion("1.0.0")] +[assembly: AssemblyFileVersion("1.0.0")] \ No newline at end of file diff --git a/src/GitVersionTask/AssemblyInfoBuilder/UpdateAssemblyInfo.cs b/src/GitVersionTask/AssemblyInfoBuilder/UpdateAssemblyInfo.cs index 046f81c704..f2bef23152 100644 --- a/src/GitVersionTask/AssemblyInfoBuilder/UpdateAssemblyInfo.cs +++ b/src/GitVersionTask/AssemblyInfoBuilder/UpdateAssemblyInfo.cs @@ -3,14 +3,24 @@ using System; using System.IO; using System.Text; + using GitVersion; - using GitVersion.Helpers; + using Microsoft.Build.Framework; - using Microsoft.Build.Utilities; - using Logger = GitVersion.Logger; - public class UpdateAssemblyInfo : Task + public class UpdateAssemblyInfo : GitVersionTaskBase { + TaskLogger logger; + + public UpdateAssemblyInfo() + { + CompileFiles = new ITaskItem[] + { + }; + logger = new TaskLogger(this); + Logger.SetLoggers(this.LogInfo, this.LogWarning, s => this.LogError(s)); + } + [Required] public string SolutionDirectory { get; set; } @@ -31,20 +41,6 @@ public class UpdateAssemblyInfo : Task public bool NoFetch { get; set; } - TaskLogger logger; - IFileSystem fileSystem; - - public UpdateAssemblyInfo() - { - CompileFiles = new ITaskItem[] { }; - logger = new TaskLogger(this); - fileSystem = new FileSystem(); - Logger.SetLoggers( - this.LogInfo, - this.LogWarning, - s => this.LogError(s)); - } - public override bool Execute() { try @@ -68,14 +64,14 @@ public override bool Execute() } } - public void InnerExecute() + void InnerExecute() { TempFileTracker.DeleteTempFiles(); InvalidFileChecker.CheckForInvalidFiles(CompileFiles, ProjectFile); VersionVariables versionVariables; - if (!VersionAndBranchFinder.TryGetVersion(SolutionDirectory, out versionVariables, NoFetch, new Authentication(), fileSystem)) + if (!ExecuteCore.TryGetVersion(SolutionDirectory, out versionVariables, NoFetch, new Authentication())) { return; } @@ -105,7 +101,9 @@ void CreateTempAssemblyInfo(VersionVariables versionVariables) { var content = File.ReadAllText(AssemblyInfoTempFilePath, Encoding.UTF8).Trim(); if (string.Equals(assemblyInfo, content, StringComparison.Ordinal)) + { return; // nothign to do as the file matches what we'd create + } } } catch (Exception) diff --git a/src/GitVersionTask/CachedVersion.cs b/src/GitVersionTask/CachedVersion.cs deleted file mode 100644 index 75a35d22c8..0000000000 --- a/src/GitVersionTask/CachedVersion.cs +++ /dev/null @@ -1,7 +0,0 @@ -using GitVersion; - -public class CachedVersion -{ - public VersionVariables VersionVariables; - public long Timestamp; -} \ No newline at end of file diff --git a/src/GitVersionTask/DirectoryDateFinder.cs b/src/GitVersionTask/DirectoryDateFinder.cs deleted file mode 100644 index 0596269ba3..0000000000 --- a/src/GitVersionTask/DirectoryDateFinder.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.IO; -using System.Linq; - -public static class DirectoryDateFinder -{ - public static long GetLastDirectoryWrite(string path) - { - return new DirectoryInfo(path) - .GetDirectories("*.*", SearchOption.AllDirectories) - .Select(d => d.LastWriteTimeUtc) - .DefaultIfEmpty() - .Max() - .Ticks; - } -} \ No newline at end of file diff --git a/src/GitVersionTask/GetVersion.cs b/src/GitVersionTask/GetVersion.cs index 93c46d2044..f1b8afb4d7 100644 --- a/src/GitVersionTask/GetVersion.cs +++ b/src/GitVersionTask/GetVersion.cs @@ -1,14 +1,21 @@ namespace GitVersionTask { using System; + using GitVersion; - using GitVersion.Helpers; + using Microsoft.Build.Framework; - using Microsoft.Build.Utilities; - using Logger = GitVersion.Logger; - public class GetVersion : Task + public class GetVersion : GitVersionTaskBase { + TaskLogger logger; + + public GetVersion() + { + logger = new TaskLogger(this); + Logger.SetLoggers(this.LogInfo, this.LogWarning, s => this.LogError(s)); + } + [Required] public string SolutionDirectory { get; set; } @@ -80,26 +87,13 @@ public class GetVersion : Task [Output] public string CommitsSinceVersionSourcePadded { get; set; } - TaskLogger logger; - IFileSystem fileSystem; - - public GetVersion() - { - logger = new TaskLogger(this); - fileSystem = new FileSystem(); - Logger.SetLoggers( - this.LogInfo, - this.LogWarning, - s => this.LogError(s)); - } - public override bool Execute() { try { VersionVariables variables; - if (VersionAndBranchFinder.TryGetVersion(SolutionDirectory, out variables, NoFetch, new Authentication(), fileSystem)) + if (ExecuteCore.TryGetVersion(SolutionDirectory, out variables, NoFetch, new Authentication())) { var thisType = typeof(GetVersion); foreach (var variable in variables) diff --git a/src/GitVersionTask/GitVersionTask.csproj b/src/GitVersionTask/GitVersionTask.csproj index 48c7a5a178..d80f2fb583 100644 --- a/src/GitVersionTask/GitVersionTask.csproj +++ b/src/GitVersionTask/GitVersionTask.csproj @@ -60,15 +60,13 @@ - - + - diff --git a/src/GitVersionTask/GitVersionTaskBase.cs b/src/GitVersionTask/GitVersionTaskBase.cs new file mode 100644 index 0000000000..3337ad8fb6 --- /dev/null +++ b/src/GitVersionTask/GitVersionTaskBase.cs @@ -0,0 +1,23 @@ +namespace GitVersionTask +{ + using GitVersion; + using GitVersion.Helpers; + + using Microsoft.Build.Utilities; + + public abstract class GitVersionTaskBase : Task + { + readonly ExecuteCore executeCore; + + protected GitVersionTaskBase() + { + var fileSystem = new FileSystem(); + executeCore = new ExecuteCore(fileSystem); + } + + protected ExecuteCore ExecuteCore + { + get { return executeCore; } + } + } +} \ No newline at end of file diff --git a/src/GitVersionTask/VersionAndBranchFinder.cs b/src/GitVersionTask/VersionAndBranchFinder.cs deleted file mode 100644 index c9b7dc260f..0000000000 --- a/src/GitVersionTask/VersionAndBranchFinder.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using GitVersion; -using GitVersion.Helpers; - -public static class VersionAndBranchFinder -{ - static Dictionary versionCacheVersions = new Dictionary(); - - public static bool TryGetVersion(string directory, out VersionVariables versionVariables, bool noFetch, Authentication authentication, IFileSystem fileSystem) - { - try - { - versionVariables = GetVersion(directory, authentication, noFetch, fileSystem); - return true; - } - catch (Exception ex) - { - Logger.WriteWarning("Could not determine assembly version: " + ex.Message); - versionVariables = null; - return false; - } - } - - public static VersionVariables GetVersion(string directory, Authentication authentication, bool noFetch, IFileSystem fileSystem) - { - var gitDir = GitDirFinder.TreeWalkForDotGitDir(directory); - using (var repo = RepositoryLoader.GetRepo(gitDir)) - { - var ticks = DirectoryDateFinder.GetLastDirectoryWrite(gitDir); - var key = string.Format("{0}:{1}:{2}",gitDir, repo.Head.CanonicalName, repo.Head.Tip.Sha); - - Logger.WriteInfo("CacheKey: " + key ); - CachedVersion result; - if (versionCacheVersions.TryGetValue(key, out result)) - { - if (result.Timestamp != ticks) - { - Logger.WriteInfo(string.Format("Change detected. Flushing cache. OldTimeStamp: {0}. NewTimeStamp: {1}", result.Timestamp, ticks)); - result.VersionVariables = ExecuteCore.ExecuteGitVersion(fileSystem, null, null, authentication, null, noFetch, directory, null); - result.Timestamp = ticks; - } - Logger.WriteInfo("Returning version from cache"); - return result.VersionVariables; - } - Logger.WriteInfo("Version not in cache. Calculating version."); - - return (versionCacheVersions[key] = new CachedVersion - { - VersionVariables = ExecuteCore.ExecuteGitVersion(fileSystem, null, null, authentication, null, noFetch, directory, null), - Timestamp = ticks - }).VersionVariables; - } - } -} diff --git a/src/GitVersionTask/WriteVersionInfoToBuildLog.cs b/src/GitVersionTask/WriteVersionInfoToBuildLog.cs index 262191a301..b616bf6181 100644 --- a/src/GitVersionTask/WriteVersionInfoToBuildLog.cs +++ b/src/GitVersionTask/WriteVersionInfoToBuildLog.cs @@ -1,33 +1,27 @@ namespace GitVersionTask { - using GitVersion; - using GitVersion.Helpers; - using Microsoft.Build.Framework; - using Microsoft.Build.Utilities; using System; using System.Collections.Generic; - using Logger = GitVersion.Logger; - public class WriteVersionInfoToBuildLog : Task - { - [Required] - public string SolutionDirectory { get; set; } + using GitVersion; - public bool NoFetch { get; set; } + using Microsoft.Build.Framework; - TaskLogger logger; - IFileSystem fileSystem; + public class WriteVersionInfoToBuildLog : GitVersionTaskBase + { + readonly TaskLogger logger; public WriteVersionInfoToBuildLog() { logger = new TaskLogger(this); - fileSystem = new FileSystem(); - Logger.SetLoggers( - this.LogInfo, - this.LogWarning, - s => this.LogError(s)); + Logger.SetLoggers(this.LogInfo, this.LogWarning, s => this.LogError(s)); } + [Required] + public string SolutionDirectory { get; set; } + + public bool NoFetch { get; set; } + public override bool Execute() { try @@ -51,18 +45,18 @@ public override bool Execute() } } - public void InnerExecute() + void InnerExecute() { VersionVariables result; - if (!VersionAndBranchFinder.TryGetVersion(SolutionDirectory, out result, NoFetch, new Authentication(), fileSystem)) + if (!ExecuteCore.TryGetVersion(SolutionDirectory, out result, NoFetch, new Authentication())) { return; } - + WriteIntegrationParameters(BuildServerList.GetApplicableBuildServers(), result); } - public void WriteIntegrationParameters(IEnumerable applicableBuildServers, VersionVariables variables) + void WriteIntegrationParameters(IEnumerable applicableBuildServers, VersionVariables variables) { foreach (var buildServer in applicableBuildServers) {