diff --git a/.gitignore b/.gitignore index dc2ab02461..ad4f0df6bd 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ *.vspscc .builds *.dotCover +*.orig ## If you have NuGet Package Restore enabled, uncomment this packages/ diff --git a/AcceptanceTests/NoTagsInRepositorySpecification.RunSpecification.approved.txt.orig b/AcceptanceTests/NoTagsInRepositorySpecification.RunSpecification.approved.txt.orig deleted file mode 100644 index 0706fc4ec5..0000000000 --- a/AcceptanceTests/NoTagsInRepositorySpecification.RunSpecification.approved.txt.orig +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Major":0, - "Minor":1, - "Patch":0, - "Suffix":"", - "LongVersion":"0.1.0 Sha:'000000000000000000000000000000000000000'", - "NugetVersion":"0.1.0", - "ShortVersion":"0.1.0", - "BranchName":"master", - "BranchType":"Master", - "Sha":"000000000000000000000000000000000000000", - "MajorMinorPatch":"0.1.0", - "SemVer":"0.1.0", - "Version":"0.1.0", - "PreReleasePartTwo":2, - "Stability":"Final" -} diff --git a/AcceptanceTests/NoTagsInRepositoryWithNextVersionTxtSpecification.RunSpecification.approved.txt.orig b/AcceptanceTests/NoTagsInRepositoryWithNextVersionTxtSpecification.RunSpecification.approved.txt.orig deleted file mode 100644 index cab87ab2db..0000000000 --- a/AcceptanceTests/NoTagsInRepositoryWithNextVersionTxtSpecification.RunSpecification.approved.txt.orig +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Major":0, - "Minor":2, - "Patch":0, - "Suffix":"", - "LongVersion":"0.2.0 Sha:'000000000000000000000000000000000000000'", - "NugetVersion":"0.2.0", - "ShortVersion":"0.2.0", - "BranchName":"master", - "BranchType":"Master", - "Sha":"000000000000000000000000000000000000000", - "MajorMinorPatch":"0.2.0", - "SemVer":"0.2.0", - "Version":"0.2.0", - "PreReleasePartTwo":2, - "Stability":"Final" -} diff --git a/AcceptanceTests/RepositorySpecification.RunSpecification.approved.txt.orig b/AcceptanceTests/RepositorySpecification.RunSpecification.approved.txt.orig deleted file mode 100644 index ca1e70c05f..0000000000 --- a/AcceptanceTests/RepositorySpecification.RunSpecification.approved.txt.orig +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Major":0, - "Minor":1, - "Patch":0, - "Suffix":"", - "LongVersion":"0.1.0 Sha:'5d8a17535e215be2b92f16d0da71d25c505b5543'", - "NugetVersion":"0.1.0", - "ShortVersion":"0.1.0", - "BranchName":"master", - "BranchType":"Master", - "Sha":"5d8a17535e215be2b92f16d0da71d25c505b5543", - "MajorMinorPatch":"0.1.0", - "SemVer":"0.1.0", - "Version":"0.1.0", - "PreReleasePartTwo":2, - "Stability":"Final" -} diff --git a/AcceptanceTests/TagFollowedByCommitsWithApplicableNextVersionTxtSpecification.ForOneCommit.approved.txt.orig b/AcceptanceTests/TagFollowedByCommitsWithApplicableNextVersionTxtSpecification.ForOneCommit.approved.txt.orig deleted file mode 100644 index b2a4eae97c..0000000000 --- a/AcceptanceTests/TagFollowedByCommitsWithApplicableNextVersionTxtSpecification.ForOneCommit.approved.txt.orig +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Major":1, - "Minor":1, - "Patch":0, - "Suffix":"", - "LongVersion":"1.1.0 Sha:'000000000000000000000000000000000000000'", - "NugetVersion":"1.1.0", - "ShortVersion":"1.1.0", - "BranchName":"master", - "BranchType":"Master", - "Sha":"000000000000000000000000000000000000000", - "MajorMinorPatch":"1.1.0", - "SemVer":"1.1.0", - "Version":"1.1.0", - "PreReleasePartTwo":1, - "Stability":"Final" -} diff --git a/AcceptanceTests/TagFollowedByCommitsWithApplicableNextVersionTxtSpecification.ForTenCommitsCommit.approved.txt.orig b/AcceptanceTests/TagFollowedByCommitsWithApplicableNextVersionTxtSpecification.ForTenCommitsCommit.approved.txt.orig deleted file mode 100644 index a0a5b1b051..0000000000 --- a/AcceptanceTests/TagFollowedByCommitsWithApplicableNextVersionTxtSpecification.ForTenCommitsCommit.approved.txt.orig +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Major":1, - "Minor":1, - "Patch":0, - "Suffix":"", - "LongVersion":"1.1.0 Sha:'000000000000000000000000000000000000000'", - "NugetVersion":"1.1.0", - "ShortVersion":"1.1.0", - "BranchName":"master", - "BranchType":"Master", - "Sha":"000000000000000000000000000000000000000", - "MajorMinorPatch":"1.1.0", - "SemVer":"1.1.0", - "Version":"1.1.0", - "PreReleasePartTwo":12, - "Stability":"Final" -} diff --git a/AcceptanceTests/TagFollowedByCommitsWithApplicableNextVersionTxtSpecification.Run.approved.txt.orig b/AcceptanceTests/TagFollowedByCommitsWithApplicableNextVersionTxtSpecification.Run.approved.txt.orig deleted file mode 100644 index 19a76f130c..0000000000 --- a/AcceptanceTests/TagFollowedByCommitsWithApplicableNextVersionTxtSpecification.Run.approved.txt.orig +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/AcceptanceTests/TagFollowedByCommitsWithNoNextVersionTxtSpecification.RunSpecification.approved.txt.orig b/AcceptanceTests/TagFollowedByCommitsWithNoNextVersionTxtSpecification.RunSpecification.approved.txt.orig deleted file mode 100644 index 3f76166cc2..0000000000 --- a/AcceptanceTests/TagFollowedByCommitsWithNoNextVersionTxtSpecification.RunSpecification.approved.txt.orig +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Major":0, - "Minor":1, - "Patch":1, - "Suffix":"", - "LongVersion":"0.1.1 Sha:'000000000000000000000000000000000000000'", - "NugetVersion":"0.1.1", - "ShortVersion":"0.1.1", - "BranchName":"master", - "BranchType":"Master", - "Sha":"000000000000000000000000000000000000000", - "MajorMinorPatch":"0.1.1", - "SemVer":"0.1.1", - "Version":"0.1.1", - "PreReleasePartTwo":1, - "Stability":"Final" -} diff --git a/AcceptanceTests/TagFollowedByCommitsWithRedundantNextVersionTxtSpecification.Run.approved.txt.orig b/AcceptanceTests/TagFollowedByCommitsWithRedundantNextVersionTxtSpecification.Run.approved.txt.orig deleted file mode 100644 index 19a76f130c..0000000000 --- a/AcceptanceTests/TagFollowedByCommitsWithRedundantNextVersionTxtSpecification.Run.approved.txt.orig +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/AcceptanceTests/TagFollowedByCommitsWithRedundantNextVersionTxtSpecification.WithOneCommit.approved.txt.orig b/AcceptanceTests/TagFollowedByCommitsWithRedundantNextVersionTxtSpecification.WithOneCommit.approved.txt.orig deleted file mode 100644 index 1faf95cb7b..0000000000 --- a/AcceptanceTests/TagFollowedByCommitsWithRedundantNextVersionTxtSpecification.WithOneCommit.approved.txt.orig +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Major":1, - "Minor":0, - "Patch":4, - "Suffix":"", - "LongVersion":"1.0.4 Sha:'000000000000000000000000000000000000000'", - "NugetVersion":"1.0.4", - "ShortVersion":"1.0.4", - "BranchName":"master", - "BranchType":"Master", - "Sha":"000000000000000000000000000000000000000", - "MajorMinorPatch":"1.0.4", - "SemVer":"1.0.4", - "Version":"1.0.4", - "PreReleasePartTwo":12, - "Stability":"Final" -} diff --git a/AcceptanceTests/TagFollowedByCommitsWithRedundantNextVersionTxtSpecification.WithTenCommit.approved.txt.orig b/AcceptanceTests/TagFollowedByCommitsWithRedundantNextVersionTxtSpecification.WithTenCommit.approved.txt.orig deleted file mode 100644 index e3faace630..0000000000 --- a/AcceptanceTests/TagFollowedByCommitsWithRedundantNextVersionTxtSpecification.WithTenCommit.approved.txt.orig +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Major":1, - "Minor":0, - "Patch":4, - "Suffix":"", - "LongVersion":"1.0.4 Sha:'000000000000000000000000000000000000000'", - "NugetVersion":"1.0.4", - "ShortVersion":"1.0.4", - "BranchName":"master", - "BranchType":"Master", - "Sha":"000000000000000000000000000000000000000", - "MajorMinorPatch":"1.0.4", - "SemVer":"1.0.4", - "Version":"1.0.4", - "PreReleasePartTwo":10, - "Stability":"Final" -} diff --git a/GitVersion.sln b/GitVersion.sln index 464e2bc7b8..97f4bbc25f 100644 --- a/GitVersion.sln +++ b/GitVersion.sln @@ -10,6 +10,11 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitVersionTask", "GitVersionTask\GitVersionTask.csproj", "{F7AC0E71-3E9A-4F6D-B986-E004825A48E1}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AcceptanceTests", "AcceptanceTests\AcceptanceTests.csproj", "{BF905F84-382C-440D-92F5-C61108626D8D}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3EFFC5D6-88D0-49D9-BB53-E1B7EB49DD45}" + ProjectSection(SolutionItems) = preProject + LICENSE = LICENSE + README.md = README.md + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/GitVersion/ArgumentParser.cs b/GitVersion/ArgumentParser.cs index 03f78b260b..cf7e991194 100644 --- a/GitVersion/ArgumentParser.cs +++ b/GitVersion/ArgumentParser.cs @@ -88,6 +88,30 @@ public static Arguments ParseArguments(List commandLineArguments) continue; } + if (IsSwitch("exec", name)) + { + arguments.Exec = value; + continue; + } + + if (IsSwitch("execargs", name)) + { + arguments.ExecArgs = value; + continue; + } + + if (IsSwitch("proj", name)) + { + arguments.Proj = value; + continue; + } + + if (IsSwitch("projargs", name)) + { + arguments.ProjArgs = value; + continue; + } + if ((IsSwitch("v", name)) && VersionParts.Contains(value.ToLower())) { arguments.VersionPart = value.ToLower(); diff --git a/GitVersion/Arguments.cs b/GitVersion/Arguments.cs index 5546c1cb09..61cf0f8411 100644 --- a/GitVersion/Arguments.cs +++ b/GitVersion/Arguments.cs @@ -31,5 +31,10 @@ public Arguments() public string VersionPart; public OutputType Output; + + public string Proj; + public string ProjArgs; + public string Exec; + public string ExecArgs; } } \ No newline at end of file diff --git a/GitVersion/GitVersion.csproj b/GitVersion/GitVersion.csproj index cd3c85b8db..464df4d9da 100644 --- a/GitVersion/GitVersion.csproj +++ b/GitVersion/GitVersion.csproj @@ -82,6 +82,7 @@ + diff --git a/GitVersion/HelpWriter.cs b/GitVersion/HelpWriter.cs index 8786d56202..b091a9097c 100644 --- a/GitVersion/HelpWriter.cs +++ b/GitVersion/HelpWriter.cs @@ -11,13 +11,17 @@ public static void Write() GitVersion [path] [/l logFilePath] - path The directory containing .git. If not defined current directory is used. - /url Url to remote git repository. - /b Name of the branch to use on the remote repository, must be used in combination with /url. - /u Username in case authentication is required. - /p Password in case authentication is required. - /output Determines the output to the console. Can be either 'json' or 'buildserver', will default to 'json'. - /l Path to logfile. + path The directory containing .git. If not defined current directory is used. + /url Url to remote git repository. + /b Name of the branch to use on the remote repository, must be used in combination with /url. + /u Username in case authentication is required. + /p Password in case authentication is required. + /output Determines the output to the console. Can be either 'json' or 'buildserver', will default to 'json'. + /l Path to logfile. + /exec Executes target executable making GitVersion variables available as environmental variables + /execargs Arguments for the executable specified by /exec + /proj Build a msbuild file, GitVersion variables will be passed as msbuild properties + /projargs Additional arguments to pass to msbuild "; Console.Write(message); } diff --git a/GitVersion/ProcessHelper.cs b/GitVersion/ProcessHelper.cs new file mode 100644 index 0000000000..40a3238b63 --- /dev/null +++ b/GitVersion/ProcessHelper.cs @@ -0,0 +1,112 @@ +namespace GitVersion +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Runtime.InteropServices; + using System.Threading; + + static class ProcessHelper + { + private static volatile object _lockObject = new object(); + + // http://social.msdn.microsoft.com/Forums/en/netfxbcl/thread/f6069441-4ab1-4299-ad6a-b8bb9ed36be3 + public static Process Start(ProcessStartInfo startInfo) + { + Process process; + + lock (_lockObject) + { + using (new ChangeErrorMode(ErrorModes.FailCriticalErrors | ErrorModes.NoGpFaultErrorBox)) + { + process = Process.Start(startInfo); + process.PriorityClass = ProcessPriorityClass.Idle; + } + } + + return process; + } + + // http://csharptest.net/532/using-processstart-to-capture-console-output/ + public static int Run(Action output, Action errorOutput, TextReader input, string exe, string args, string workingDirectory) + { + if (String.IsNullOrEmpty(exe)) + throw new FileNotFoundException(); + if (output == null) + throw new ArgumentNullException("output"); + + var psi = new ProcessStartInfo + { + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardOutput = true, + RedirectStandardInput = true, + WindowStyle = ProcessWindowStyle.Hidden, + CreateNoWindow = true, + ErrorDialog = false, + WorkingDirectory = workingDirectory ?? Environment.CurrentDirectory, + FileName = exe, + Arguments = args + }; + + using (var process = Process.Start(psi)) + using (var mreOut = new ManualResetEvent(false)) + using (var mreErr = new ManualResetEvent(false)) + { + process.EnableRaisingEvents = true; + process.OutputDataReceived += (o, e) => + { + // ReSharper disable once AccessToDisposedClosure + if (e.Data == null) + mreOut.Set(); + else + output(e.Data); + }; + process.BeginOutputReadLine(); + process.ErrorDataReceived += (o, e) => + { + // ReSharper disable once AccessToDisposedClosure + if (e.Data == null) + mreErr.Set(); + else + errorOutput(e.Data); + }; + process.BeginErrorReadLine(); + + string line; + while (input != null && null != (line = input.ReadLine())) + process.StandardInput.WriteLine(line); + + process.StandardInput.Close(); + process.WaitForExit(); + + mreOut.WaitOne(); + mreErr.WaitOne(); + + return process.ExitCode; + } + } + + [Flags] + enum ErrorModes + { + FailCriticalErrors = 0x1, + NoGpFaultErrorBox = 0x2 + } + + struct ChangeErrorMode : IDisposable + { + private readonly int _oldMode; + + public ChangeErrorMode(ErrorModes mode) + { + _oldMode = SetErrorMode((int)mode); + } + + void IDisposable.Dispose() { SetErrorMode(_oldMode); } + + [DllImport("kernel32.dll")] + private static extern int SetErrorMode(int newMode); + } + } +} \ No newline at end of file diff --git a/GitVersion/Program.cs b/GitVersion/Program.cs index 7c5e1831c6..ea01867129 100644 --- a/GitVersion/Program.cs +++ b/GitVersion/Program.cs @@ -8,6 +8,8 @@ namespace GitVersion class Program { + private const string MsBuild = @"c:\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe"; + static void Main() { int? exitCode = null; @@ -67,6 +69,9 @@ static void Main() break; } } + + RunMsBuildIfNeeded(arguments, gitDirectory); + RunExecCommandIfNeeded(arguments, gitDirectory); if (gitPreparer.IsDynamicGitRepository) { @@ -146,5 +151,30 @@ static List GetArgumentsWithoutExeName() .Skip(1) .ToList(); } + + private static void RunMsBuildIfNeeded(Arguments args, string gitDirectory) + { + if (string.IsNullOrEmpty(args.Proj)) return; + + Console.WriteLine("Launching {0} \"{1}\"{2}", MsBuild, args.Proj, args.ProjArgs); + var results = ProcessHelper.Run( + Console.WriteLine, Console.Error.WriteLine, + null, MsBuild, string.Format("\"{0}\"{1}", args.Proj, args.ProjArgs), gitDirectory); + + if (results != 0) + throw new ErrorException("MsBuild execution failed, non-zero return code"); + } + + private static void RunExecCommandIfNeeded(Arguments args, string gitDirectory) + { + if (string.IsNullOrEmpty(args.Exec)) return; + + Console.WriteLine("Launching {0} {1}", args.Exec, args.ExecArgs); + var results = ProcessHelper.Run( + Console.WriteLine, Console.Error.WriteLine, + null, args.Exec, args.ExecArgs, gitDirectory); + if (results != 0) + throw new ErrorException(string.Format("Execution of {0} failed, non-zero return code", args.Exec)); + } } } \ No newline at end of file diff --git a/Tests/ArgumentParserTests.cs b/Tests/ArgumentParserTests.cs index adda3fb789..088db4a0e1 100644 --- a/Tests/ArgumentParserTests.cs +++ b/Tests/ArgumentParserTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using GitVersion; using NUnit.Framework; @@ -42,6 +43,56 @@ public void h_means_IsHelp() Assert.IsTrue(arguments.IsHelp); } + [Test] + public void exec() + { + var arguments = ArgumentParser.ParseArguments("-exec rake"); + Assert.AreEqual("rake", arguments.Exec); + } + + [Test] + public void exec_with_args() + { + var arguments = ArgumentParser.ParseArguments(new List + { + "-exec", + "rake", + "-execargs", + "clean build" + }); + Assert.AreEqual("rake", arguments.Exec); + Assert.AreEqual("clean build", arguments.ExecArgs); + } + + [Test] + public void msbuild() + { + var arguments = ArgumentParser.ParseArguments("-proj msbuild.proj"); + Assert.AreEqual("msbuild.proj", arguments.Proj); + } + + [Test] + public void msbuild_with_args() + { + var arguments = ArgumentParser.ParseArguments(new List + { + "-proj", + "msbuild.proj", + "-projargs", + "/p:Configuration=Debug /p:Platform=AnyCPU" + }); + Assert.AreEqual("msbuild.proj", arguments.Proj); + Assert.AreEqual("/p:Configuration=Debug /p:Platform=AnyCPU", arguments.ProjArgs); + } + + [Test] + public void execwith_targetdirectory() + { + var arguments = ArgumentParser.ParseArguments("targetDirectoryPath -exec rake"); + Assert.AreEqual("targetDirectoryPath", arguments.TargetPath); + Assert.AreEqual("rake", arguments.Exec); + } + [Test] public void TargetDirectory_and_LogFilePath_can_be_parsed() {