diff --git a/project/UnitTests/Core/Label/CustomLabellerTest.cs b/project/UnitTests/Core/Label/CustomLabellerTest.cs new file mode 100644 index 000000000..b63096aa2 --- /dev/null +++ b/project/UnitTests/Core/Label/CustomLabellerTest.cs @@ -0,0 +1,92 @@ +using Exortech.NetReflector; +using NUnit.Framework; +using ThoughtWorks.CruiseControl.Core.Label; + +namespace ThoughtWorks.CruiseControl.UnitTests.Core.Label +{ + [TestFixture] + public class CustomLabellerTest : IntegrationFixture + { + private CustomLabeller labeller; + + private string exampleCode = @" + + if ( + integrationResult != null + && integrationResult.LastIntegration != null + && !integrationResult.LastIntegration.IsInitial() + ) + { + if (integrationResult.LastIntegration.Status == ThoughtWorks.CruiseControl.Remote.IntegrationStatus.Success) + { + System.Version lastVersion = System.Version.Parse(integrationResult.LastIntegration.Label); + System.Version nextVersion = new System.Version(lastVersion.Major, lastVersion.Minor, lastVersion.Build, lastVersion.Revision + 1); + ret = nextVersion.ToString(); + } + else + { + ret = integrationResult.LastIntegration.Label; + } + } + else + { + ret = new System.Version(1, 1, 1, 1).ToString(); + } + + +"; + + [SetUp] + public void SetUp() + { + labeller = new CustomLabeller(); + } + + [Test] + public void GenerateInitialLabelWoCode() + { + Assert.AreEqual("0.0.0.0", labeller.Generate(InitialIntegrationResult())); + } + + [Test] + public void GenerateLabelWoCode() + { + Assert.AreEqual("0.0.0.0", labeller.Generate(SuccessfulResult("35"))); + } + + [Test] + public void GenerateNextLabel() + { + labeller.CsCode = @"ret = ""somelabel"";"; + Assert.AreEqual("somelabel", labeller.Generate(SuccessfulResult("previouslabel"))); + } + + [Test] + public void GenerateInitialLabel() + { + labeller.CsCode = @"ret = ""somelabel"";"; + Assert.AreEqual("somelabel", labeller.Generate(SuccessfulResult("previouslabel"))); + } + + [Test] + public void GenerateInitialLabelWithExampleCode() + { + labeller.CsCode = exampleCode; + Assert.AreEqual("1.1.1.1", labeller.Generate(InitialIntegrationResult())); + } + + [Test] + public void GenerateNextLabelWithExampleCode() + { + labeller.CsCode = exampleCode; + Assert.AreEqual("1.2.3.5", labeller.Generate(SuccessfulResult("1.2.3.4"))); + } + + [Test] + public void GenerateSameLabelWithExampleCode() + { + labeller.CsCode = exampleCode; + Assert.AreEqual("1.2.3.4", labeller.Generate(FailedResult("1.2.3.4"))); + } + } +} diff --git a/project/UnitTests/UnitTests.csproj b/project/UnitTests/UnitTests.csproj index 06a1f2db0..ccdbdfd4b 100644 --- a/project/UnitTests/UnitTests.csproj +++ b/project/UnitTests/UnitTests.csproj @@ -376,6 +376,7 @@ + diff --git a/project/core/core.csproj b/project/core/core.csproj index 6431d5c55..77cf18bb8 100644 --- a/project/core/core.csproj +++ b/project/core/core.csproj @@ -372,6 +372,7 @@ Code + Code diff --git a/project/core/label/CustomLabeller.cs b/project/core/label/CustomLabeller.cs new file mode 100644 index 000000000..de7f14658 --- /dev/null +++ b/project/core/label/CustomLabeller.cs @@ -0,0 +1,135 @@ +using System; +using System.Linq; +using System.Text.RegularExpressions; +using Exortech.NetReflector; +using ThoughtWorks.CruiseControl.Remote; +using System.Globalization; +using Microsoft.CSharp; +using System.CodeDom.Compiler; +using System.Reflection; + +namespace ThoughtWorks.CruiseControl.Core.Label +{ + /// + /// + /// Allows CCNet create custom code-generated labels + /// + /// + /// You can do this by specifying your own configuration of the default labeller in your project. + /// + /// + /// Custom Labeller + /// 1.0 + /// + /// + /// <labeller type="customlabeller"> + /// <cscode>1</cscode> + /// </labeller> + /// + /// + [ReflectorType("customlabeller")] + public class CustomLabeller + : LabellerBase + { + /// + /// Generates the specified integration result. + /// + /// The integration result. + /// + /// + public override string Generate(IIntegrationResult integrationResult) + { + MethodInfo method = this.CreateFunction(this.CsCode); + string ret = (string)method.Invoke(null, new object[1] { integrationResult }); + return ret; + } + + [ReflectorProperty("usings", Required = false)] + public string Usings { get; set; } + + [ReflectorProperty("referencedassemblies", Required = false)] + public string ReferencedAssemblies { get; set; } + + [ReflectorProperty("cscode", Required = true)] + public string CsCode { get; set; } + + private string CSCodeWrapper + { + get + { + return @" + +using System; +using ThoughtWorks.CruiseControl.Core; +using ThoughtWorks.CruiseControl.Remote; + +namespace CustomLabelerGeneratorUserFunctions +{ + public class CustomLabelerGenerator + { + public static string Generate(ThoughtWorks.CruiseControl.Core.IIntegrationResult integrationResult) + { + string ret = ""0.0.0.0""; + + return ret; + } + } +} +"; + } + } + + public MethodInfo CreateFunction(string function) + { + System.Text.StringBuilder usings = new System.Text.StringBuilder(); + if (!string.IsNullOrWhiteSpace(this.Usings)) + { + foreach (var each in this.Usings.Split(';')) + { + if (!string.IsNullOrWhiteSpace(each)) + { + usings.AppendFormat("using {0};", each); + usings.AppendLine(); + } + } + } + + string finalCode = usings.ToString() + CSCodeWrapper.Replace("", function); + + CSharpCodeProvider provider = new CSharpCodeProvider(); + var parameters = new CompilerParameters(); + parameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location); + parameters.ReferencedAssemblies.Add(typeof(ThoughtWorks.CruiseControl.Remote.IntegrationStatus).Assembly.Location); + if (!string.IsNullOrWhiteSpace(this.ReferencedAssemblies)) + { + foreach (var each in this.ReferencedAssemblies.Split(';')) + { + if (!string.IsNullOrWhiteSpace(each)) + { + parameters.ReferencedAssemblies.Add(each); + } + } + } + + parameters.GenerateInMemory = true; + parameters.GenerateExecutable = false; + parameters.IncludeDebugInformation = false; + CompilerResults results = provider.CompileAssemblyFromSource(parameters, finalCode); + if (results.Errors != null && results.Errors.Count > 0) + { + var path = System.IO.Path.GetTempFileName(); + System.IO.File.WriteAllText(path, finalCode); + foreach (var each in results.Errors) + { + Console.WriteLine("ERROR in {0}: {1}", path, each); + } + + throw new ApplicationException("There are compilation errors. Please see " + path); + } + + + Type binaryFunction = results.CompiledAssembly.GetType("CustomLabelerGeneratorUserFunctions.CustomLabelerGenerator"); + return binaryFunction.GetMethod("Generate"); + } + } +} diff --git a/project/core/sourcecontrol/Git.cs b/project/core/sourcecontrol/Git.cs index c07f0ead0..b3827e9ea 100644 --- a/project/core/sourcecontrol/Git.cs +++ b/project/core/sourcecontrol/Git.cs @@ -231,6 +231,15 @@ public class Git : ProcessSourceControl [ReflectorProperty("commitUntrackedFiles", Required = false)] public bool CommitUntrackedFiles { get; set; } + /// + /// Indicates that files committed during the build process should be pushed to remmote repository after tagging. This requires 'commitBuildModifications' + /// and 'pushCommitedOnSuccess' to be set to 'true'. + /// + /// 1.6 + /// false + [ReflectorProperty("pushCommitedOnSuccess", Required = false)] + public bool PushCommitedOnSuccess { get; set; } + /// /// Used to set the "user.name" configuration setting in the local repository. Required for the 'tagOnSuccess ' feature. /// @@ -366,6 +375,11 @@ public override void LabelSourceControl(IIntegrationResult result) // create a tag and push it. GitCreateTag(tagName, commitMessage, result); GitPushTag(tagName, result); + + if (CommitBuildModifications && PushCommitedOnSuccess) + { + GitPushAll(result); + } } #region private methods @@ -754,6 +768,30 @@ private void GitPushTag(string tagName, IIntegrationResult result) ProcessExecutor.ProcessOutput -= ProcessExecutor_ProcessOutput; } + /// + /// Push commited content with "git push origin HEAD:'branch name'". + /// + /// IIntegrationResult of the current build. + private void GitPushAll(IIntegrationResult result) + { + ProcessArgumentBuilder buffer = new ProcessArgumentBuilder(); + buffer.AddArgument("push"); + buffer.AddArgument("origin"); + buffer.AddArgument("HEAD:"+ Branch); + + // initialize progress information + var bpi = GetBuildProgressInformation(result); + bpi.SignalStartRunTask(string.Concat("git ", buffer.ToString())); + + // enable Stdout monitoring + ProcessExecutor.ProcessOutput += ProcessExecutor_ProcessOutput; + + Execute(NewProcessInfo(buffer.ToString(), result)); + + // remove Stdout monitoring + ProcessExecutor.ProcessOutput -= ProcessExecutor_ProcessOutput; + } + /// /// Initialize the git submodules. ///