From 35d931c536203ca65938903ddeb131802e71765f Mon Sep 17 00:00:00 2001 From: Chris Blyth Date: Wed, 10 Oct 2018 08:32:47 +0100 Subject: [PATCH 1/4] Correct TeamCity publish named artifact --- src/app/Fake.BuildServer.TeamCity/TeamCity.fs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/Fake.BuildServer.TeamCity/TeamCity.fs b/src/app/Fake.BuildServer.TeamCity/TeamCity.fs index b912bf8c46d..96645b78bc6 100644 --- a/src/app/Fake.BuildServer.TeamCity/TeamCity.fs +++ b/src/app/Fake.BuildServer.TeamCity/TeamCity.fs @@ -199,6 +199,9 @@ module TeamCity = /// Publishes an artifact on the TeamcCity build server. let internal publishArtifact path = TeamCityWriter.encapsulateSpecialChars path |> TeamCityWriter.sendToTeamCity "##teamcity[publishArtifacts '%s']" + /// Publishes an artifact on the TeamcCity build server with a name. + let internal publishNamedArtifact name path = TeamCityWriter.sendToTeamCity "##teamcity[publishArtifacts '%s' => '%s']" (TeamCityWriter.encapsulateSpecialChars path) (TeamCityWriter.encapsulateSpecialChars name) + /// Sets the TeamCity build number. let internal setBuildNumber buildNumber = TeamCityWriter.encapsulateSpecialChars buildNumber |> TeamCityWriter.sendToTeamCity "##teamcity[buildNumber '%s']" @@ -423,7 +426,8 @@ module TeamCity = error text | TraceData.LogMessage(text, newLine) | TraceData.TraceMessage(text, newLine) -> ConsoleWriter.write false color newLine text - | TraceData.ImportData (ImportData.BuildArtifactWithName _, path) + | TraceData.ImportData (ImportData.BuildArtifactWithName name, path) + publishNamedArtifact name path | TraceData.ImportData (ImportData.BuildArtifact, path) -> publishArtifact path | TraceData.ImportData (ImportData.DotNetCoverage tool, path) -> From 89fb2c1a558ee57d474e7b78cc7aa7e1da85f694 Mon Sep 17 00:00:00 2001 From: Chris Blyth Date: Wed, 10 Oct 2018 08:33:11 +0100 Subject: [PATCH 2/4] Add report build problem to TeamCity --- src/app/Fake.BuildServer.TeamCity/TeamCity.fs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/Fake.BuildServer.TeamCity/TeamCity.fs b/src/app/Fake.BuildServer.TeamCity/TeamCity.fs index 96645b78bc6..50655902f2a 100644 --- a/src/app/Fake.BuildServer.TeamCity/TeamCity.fs +++ b/src/app/Fake.BuildServer.TeamCity/TeamCity.fs @@ -57,6 +57,9 @@ module TeamCity = /// Sends an error to TeamCity let sendTeamCityError error = TeamCityWriter.sendToTeamCity "##teamcity[buildStatus status='FAILURE' text='%s']" error + /// Reports build problem + let reportBuildProblem message = (sprintf "##teamcity[buildProblem description='%s']" (TeamCityWriter.encapsulateSpecialChars message)) |> TeamCityWriter.sendStrToTeamCity + let internal sendTeamCityImportData typ file = TeamCityWriter.sendToTeamCity2 "##teamcity[importData type='%s' file='%s']" typ file module internal Import = From f9242b9c483d31ce087cd9548b532256e7b3809a Mon Sep 17 00:00:00 2001 From: Chris Blyth Date: Wed, 10 Oct 2018 08:33:42 +0100 Subject: [PATCH 3/4] Tidy TeamCity listener match statement --- src/app/Fake.BuildServer.TeamCity/TeamCity.fs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/app/Fake.BuildServer.TeamCity/TeamCity.fs b/src/app/Fake.BuildServer.TeamCity/TeamCity.fs index 50655902f2a..0a2319999ba 100644 --- a/src/app/Fake.BuildServer.TeamCity/TeamCity.fs +++ b/src/app/Fake.BuildServer.TeamCity/TeamCity.fs @@ -405,9 +405,8 @@ module TeamCity = reportBuildStatus "SUCCESS" "{build.status.text}" | TraceData.BuildState TagStatus.Warning -> warning "Setting build state to warning." - //reportBuildStatus "SUCCESS" "{build.status.text}" | TraceData.BuildState TagStatus.Failed -> - reportBuildStatus "FAILURE" (sprintf "%s - {build.status.text}" ("Failed")) + reportBuildStatus "FAILURE" "Failure - {build.status.text}" | TraceData.CloseTag (KnownTags.Test name, time, _) -> finishTestCase name time | TraceData.OpenTag (KnownTags.TestSuite name, _) -> @@ -418,9 +417,6 @@ module TeamCity = match description with | Some d -> TeamCityWriter.sendOpenBlock tag.Name (sprintf "%s: %s" tag.Type d) | _ -> TeamCityWriter.sendOpenBlock tag.Name tag.Type - | TraceData.CloseTag (tag, _, TagStatus.Failed) -> - TeamCityWriter.sendCloseBlock tag.Name - //reportBuildStatus "FAILURE" (sprintf "Failure in %s" tag.Name) | TraceData.CloseTag (tag, _, _) -> TeamCityWriter.sendCloseBlock tag.Name | TraceData.ImportantMessage text -> From 26e13b50affda1fb134b777227a1fbb5add74450 Mon Sep 17 00:00:00 2001 From: Chris Blyth Date: Wed, 10 Oct 2018 10:25:16 +0100 Subject: [PATCH 4/4] Refactor all messages sent to TeamCity into TeamCityInternal This is easier to see and re-use message formats --- src/app/Fake.BuildServer.TeamCity/TeamCity.fs | 150 ++++++---------- .../TeamCityInternal.fs | 163 ++++++++++-------- 2 files changed, 150 insertions(+), 163 deletions(-) diff --git a/src/app/Fake.BuildServer.TeamCity/TeamCity.fs b/src/app/Fake.BuildServer.TeamCity/TeamCity.fs index 0a2319999ba..97555c1e7df 100644 --- a/src/app/Fake.BuildServer.TeamCity/TeamCity.fs +++ b/src/app/Fake.BuildServer.TeamCity/TeamCity.fs @@ -55,188 +55,148 @@ module TeamCity = with member __.Dispose() = TeamCityWriter.sendCloseBlock name } /// Sends an error to TeamCity - let sendTeamCityError error = TeamCityWriter.sendToTeamCity "##teamcity[buildStatus status='FAILURE' text='%s']" error + let sendTeamCityError error = TeamCityWriter.sendBuildStatus "FAILURE" error /// Reports build problem - let reportBuildProblem message = (sprintf "##teamcity[buildProblem description='%s']" (TeamCityWriter.encapsulateSpecialChars message)) |> TeamCityWriter.sendStrToTeamCity - - let internal sendTeamCityImportData typ file = TeamCityWriter.sendToTeamCity2 "##teamcity[importData type='%s' file='%s']" typ file + let reportBuildProblem message = TeamCityWriter.sendBuildProblem message module internal Import = /// Sends an NUnit results filename to TeamCity - let sendNUnit path = sendTeamCityImportData "nunit" path + let sendNUnit path = TeamCityWriter.sendImportData "nunit" path /// Sends an FXCop results filename to TeamCity - let sendFXCop path = sendTeamCityImportData "FxCop" path + let sendFXCop path = TeamCityWriter.sendImportData "FxCop" path /// Sends an JUnit Ant task results filename to TeamCity - let sendJUnit path = sendTeamCityImportData "junit" path + let sendJUnit path = TeamCityWriter.sendImportData "junit" path /// Sends an Maven Surefire results filename to TeamCity - let sendSurefire path = sendTeamCityImportData "surefire" path + let sendSurefire path = TeamCityWriter.sendImportData "surefire" path /// Sends an MSTest results filename to TeamCity - let sendMSTest path = sendTeamCityImportData "mstest" path + let sendMSTest path = TeamCityWriter.sendImportData "mstest" path /// Sends an Google Test results filename to TeamCity - let sendGTest path = sendTeamCityImportData "gtest" path + let sendGTest path = TeamCityWriter.sendImportData "gtest" path /// Sends an Checkstyle results filename to TeamCity - let sendCheckstyle path = sendTeamCityImportData "checkstyle" path + let sendCheckstyle path = TeamCityWriter.sendImportData "checkstyle" path /// Sends an FindBugs results filename to TeamCity - let sendFindBugs path = sendTeamCityImportData "findBugs" path + let sendFindBugs path = TeamCityWriter.sendImportData "findBugs" path /// Sends an JSLint results filename to TeamCity - let sendJSLint path = sendTeamCityImportData "jslint" path + let sendJSLint path = TeamCityWriter.sendImportData "jslint" path /// Sends an ReSharper inspectCode.exe results filename to TeamCity - let sendReSharperInspectCode path = sendTeamCityImportData "ReSharperInspectCode" path + let sendReSharperInspectCode path = TeamCityWriter.sendImportData "ReSharperInspectCode" path /// Sends an PMD inspections results filename to TeamCity - let sendPmd path = sendTeamCityImportData "pmd" path + let sendPmd path = TeamCityWriter.sendImportData "pmd" path /// Sends an PMD Copy/Paste Detector results filename to TeamCity - let sendPmdCpd path = sendTeamCityImportData "pmdCpd" path + let sendPmdCpd path = TeamCityWriter.sendImportData "pmdCpd" path /// Sends an ReSharper dupfinder.exe results filename to TeamCity - let sendDotNetDupFinder path = sendTeamCityImportData "DotNetDupFinder" path + let sendDotNetDupFinder path = TeamCityWriter.sendImportData "DotNetDupFinder" path /// Sends an dotcover, partcover, ncover or ncover3 results filename to TeamCity - let sendDotNetCoverageForTool path (tool : DotNetCoverageTool) = - sprintf "##teamcity[importData type='dotNetCoverage' tool='%s' path='%s']" (tool.TeamCityName |> TeamCityWriter.scrub) (path |> TeamCityWriter.scrub) - |> TeamCityWriter.sendStrToTeamCity + let sendDotNetCoverageForTool path (tool : DotNetCoverageTool) = TeamCityWriter.sendImportDataWithTool "dotNetCoverage" tool.TeamCityName path /// Sends the full path to the dotCover home folder to override the bundled dotCover to TeamCity - let internal sendTeamCityDotCoverHome = TeamCityWriter.sendToTeamCity "##teamcity[dotNetCoverage dotcover_home='%s']" + let internal sendTeamCityDotCoverHome = TeamCityWriter.sendDotNetCoverage "dotcover_home" /// Sends the full path to NCover installation folder to TeamCity - let internal sendTeamCityNCover3Home = TeamCityWriter.sendToTeamCity "##teamcity[dotNetCoverage ncover3_home='%s']" + let internal sendTeamCityNCover3Home = TeamCityWriter.sendDotNetCoverage "ncover3_home" /// Sends arguments for the NCover report generator to TeamCity - let internal sendTeamCityNCover3ReporterArgs = TeamCityWriter.sendToTeamCity "##teamcity[dotNetCoverage ncover3_reporter_args='%s']" + let internal sendTeamCityNCover3ReporterArgs = TeamCityWriter.sendDotNetCoverage "ncover3_reporter_args" /// Sends the path to NCoverExplorer to TeamCity - let internal sendTeamCityNCoverExplorerTool = TeamCityWriter.sendToTeamCity "##teamcity[dotNetCoverage ncover_explorer_tool='%s']" + let internal sendTeamCityNCoverExplorerTool = TeamCityWriter.sendDotNetCoverage "ncover_explorer_tool" /// Sends additional arguments for NCover 1.x to TeamCity - let internal sendTeamCityNCoverExplorerToolArgs = TeamCityWriter.sendToTeamCity "##teamcity[dotNetCoverage ncover_explorer_tool_args='%s']" + let internal sendTeamCityNCoverExplorerToolArgs = TeamCityWriter.sendDotNetCoverage "ncover_explorer_tool_args" /// Sends the value for NCover /report: argument to TeamCity - let internal sendTeamCityNCoverReportType : int -> unit = string >> TeamCityWriter.sendToTeamCity "##teamcity[dotNetCoverage ncover_explorer_report_type='%s']" + let internal sendTeamCityNCoverReportType : int -> unit = string >> TeamCityWriter.sendDotNetCoverage "ncover_explorer_report_type" /// Sends the value for NCover /sort: argument to TeamCity - let internal sendTeamCityNCoverReportOrder : int -> unit = string >> TeamCityWriter.sendToTeamCity "##teamcity[dotNetCoverage ncover_explorer_report_order='%s']" + let internal sendTeamCityNCoverReportOrder : int -> unit = string >> TeamCityWriter.sendDotNetCoverage "ncover_explorer_report_order" /// Send the PartCover xslt transformation rules (Input xlst and output files) to TeamCity let internal sendTeamCityPartCoverReportXslts : seq -> unit = Seq.map (fun (xslt, output) -> sprintf "%s=>%s" xslt output) - >> Seq.map TeamCityWriter.encapsulateSpecialChars - >> String.concat "|n" - >> sprintf "##teamcity[dotNetCoverage partcover_report_xslts='%s']" - >> TeamCityWriter.sendStrToTeamCity + >> String.concat "\n" + >> TeamCityWriter.sendDotNetCoverage "partcover_report_xslts" /// Starts the test case. - let internal startTestCase testCaseName = - TeamCityWriter.sendToTeamCity "##teamcity[testStarted name='%s' captureStandardOutput='true']" testCaseName + let internal startTestCase testCaseName = TeamCityWriter.sendTestStarted testCaseName /// Finishes the test case. - let internal finishTestCase testCaseName (duration : System.TimeSpan) = - let duration = - duration.TotalMilliseconds - |> round - |> string - sprintf "##teamcity[testFinished name='%s' duration='%s']" (TeamCityWriter.encapsulateSpecialChars testCaseName) duration - |> TeamCityWriter.sendStrToTeamCity + let internal finishTestCase testCaseName (duration : System.TimeSpan) = + TeamCityWriter.sendTestFinished testCaseName (duration.TotalMilliseconds |> round |> string) /// Ignores the test case. let internal ignoreTestCase name message = - startTestCase name - sprintf "##teamcity[testIgnored name='%s' message='%s']" (TeamCityWriter.encapsulateSpecialChars name) - (TeamCityWriter.encapsulateSpecialChars message) |> TeamCityWriter.sendStrToTeamCity - + TeamCityWriter.sendTestStarted name + TeamCityWriter.sendTestIgnored name message /// Report Standard-Output for a given test-case - let internal reportTestOutput name output = - sprintf "##teamcity[testStdOut name='%s' out='%s']" - (TeamCityWriter.encapsulateSpecialChars name) - (TeamCityWriter.encapsulateSpecialChars output) - |> TeamCityWriter.sendStrToTeamCity + let internal reportTestOutput name output = TeamCityWriter.sendTestStdOut name output /// Report Standard-Error for a given test-case - let internal reportTestError name output = - sprintf "##teamcity[testStdErr name='%s' out='%s']" - (TeamCityWriter.encapsulateSpecialChars name) - (TeamCityWriter.encapsulateSpecialChars output) - |> TeamCityWriter.sendStrToTeamCity + let internal reportTestError name output = TeamCityWriter.sendTestStdError name output /// Ignores the test case. let internal ignoreTestCaseWithDetails name message details = - ignoreTestCase name (message + " " + details) + TeamCityWriter.sendTestStarted name + TeamCityWriter.sendTestIgnored name (message + " " + details) /// Finishes the test suite. - let internal finishTestSuite testSuiteName = - TeamCityWriter.encapsulateSpecialChars testSuiteName |> TeamCityWriter.sendToTeamCity "##teamcity[testSuiteFinished name='%s']" + let internal finishTestSuite testSuiteName = TeamCityWriter.sendTestSuiteFinished testSuiteName /// Starts the test suite. - let internal startTestSuite testSuiteName = - TeamCityWriter.encapsulateSpecialChars testSuiteName |> TeamCityWriter.sendToTeamCity "##teamcity[testSuiteStarted name='%s']" + let internal startTestSuite testSuiteName = TeamCityWriter.sendTestSuiteStarted testSuiteName /// Reports the progress. - let reportProgress message = TeamCityWriter.encapsulateSpecialChars message |> TeamCityWriter.sendToTeamCity "##teamcity[progressMessage '%s']" + let reportProgress message = TeamCityWriter.sendProgressMessage message /// Reports the progress start. - let reportProgressStart message = TeamCityWriter.encapsulateSpecialChars message |> TeamCityWriter.sendToTeamCity "##teamcity[progressStart '%s']" + let reportProgressStart message = TeamCityWriter.sendProgressStart message /// Reports the progress end. - let reportProgressFinish message = TeamCityWriter.encapsulateSpecialChars message |> TeamCityWriter.sendToTeamCity "##teamcity[progressFinish '%s']" - - /// Create the build status. - /// [omit] - let buildStatus status message = - sprintf "##teamcity[buildStatus status='%s' text='%s']" (TeamCityWriter.encapsulateSpecialChars status) (TeamCityWriter.encapsulateSpecialChars message) + let reportProgressFinish message = TeamCityWriter.sendProgressFinish message /// Reports the build status. - let reportBuildStatus status message = buildStatus status message |> TeamCityWriter.sendStrToTeamCity + let reportBuildStatus status message = TeamCityWriter.sendBuildStatus status message /// Publishes an artifact on the TeamcCity build server. - let internal publishArtifact path = TeamCityWriter.encapsulateSpecialChars path |> TeamCityWriter.sendToTeamCity "##teamcity[publishArtifacts '%s']" + let internal publishArtifact path = TeamCityWriter.sendPublishArtifact path /// Publishes an artifact on the TeamcCity build server with a name. - let internal publishNamedArtifact name path = TeamCityWriter.sendToTeamCity "##teamcity[publishArtifacts '%s' => '%s']" (TeamCityWriter.encapsulateSpecialChars path) (TeamCityWriter.encapsulateSpecialChars name) + let internal publishNamedArtifact name path = TeamCityWriter.sendPublishNamedArtifact name path /// Sets the TeamCity build number. - let internal setBuildNumber buildNumber = TeamCityWriter.encapsulateSpecialChars buildNumber |> TeamCityWriter.sendToTeamCity "##teamcity[buildNumber '%s']" + let internal setBuildNumber buildNumber = TeamCityWriter.sendBuildNumber buildNumber /// Reports a build statistic. - let setBuildStatistic key value = - sprintf "##teamcity[buildStatisticValue key='%s' value='%s']" (TeamCityWriter.encapsulateSpecialChars key) - (TeamCityWriter.encapsulateSpecialChars value) |> TeamCityWriter.sendStrToTeamCity + let setBuildStatistic key value = TeamCityWriter.sendBuildStatistic key value /// Reports a parameter value - let setParameter name value = - sprintf "##teamcity[setParameter name='%s' value='%s']" (TeamCityWriter.encapsulateSpecialChars name) - (TeamCityWriter.encapsulateSpecialChars value) |> TeamCityWriter.sendStrToTeamCity - - /// Reports a failed test. - let internal testFailed name message details = - sprintf "##teamcity[testFailed name='%s' message='%s' details='%s']" (TeamCityWriter.encapsulateSpecialChars name) - (TeamCityWriter.encapsulateSpecialChars message) (TeamCityWriter.encapsulateSpecialChars details) |> TeamCityWriter.sendStrToTeamCity - - /// Reports a failed comparison. - let internal comparisonFailure name message details expected actual = - sprintf - "##teamcity[testFailed type='comparisonFailure' name='%s' message='%s' details='%s' expected='%s' actual='%s']" - (TeamCityWriter.encapsulateSpecialChars name) (TeamCityWriter.encapsulateSpecialChars message) (TeamCityWriter.encapsulateSpecialChars details) - (TeamCityWriter.encapsulateSpecialChars expected) (TeamCityWriter.encapsulateSpecialChars actual) |> TeamCityWriter.sendStrToTeamCity + let setParameter name value = TeamCityWriter.sendSetParameter name value + + /// Reports a failed test + let internal testFailed name message details = TeamCityWriter.sendTestFailed name message details + + /// Reports a failed comparison + let internal comparisonFailure name message details expected actual = TeamCityWriter.sendComparisonFailed name message details expected actual /// Sends a warning message. - let internal warning message = - TeamCityWriter.sendToTeamCity "##teamcity[message text='%s' status='WARNING']" message + let internal warning message = TeamCityWriter.sendMessage "WARNING" message /// Sends an error message. - let internal error message = - TeamCityWriter.sendToTeamCity "##teamcity[message text='%s' status='ERROR']" message + let internal error message = TeamCityWriter.sendMessage "ERROR" message /// TeamCity build parameters /// @@ -425,14 +385,14 @@ module TeamCity = error text | TraceData.LogMessage(text, newLine) | TraceData.TraceMessage(text, newLine) -> ConsoleWriter.write false color newLine text - | TraceData.ImportData (ImportData.BuildArtifactWithName name, path) + | TraceData.ImportData (ImportData.BuildArtifactWithName name, path) -> publishNamedArtifact name path | TraceData.ImportData (ImportData.BuildArtifact, path) -> publishArtifact path | TraceData.ImportData (ImportData.DotNetCoverage tool, path) -> Import.sendDotNetCoverageForTool path tool | TraceData.ImportData (typ, path) -> - sendTeamCityImportData typ.TeamCityName path + TeamCityWriter.sendImportData typ.TeamCityName path | TraceData.BuildNumber number -> setBuildNumber number let defaultTraceListener = diff --git a/src/app/Fake.BuildServer.TeamCity/TeamCityInternal.fs b/src/app/Fake.BuildServer.TeamCity/TeamCityInternal.fs index 8487cce9da7..eb5d9d2318a 100644 --- a/src/app/Fake.BuildServer.TeamCity/TeamCityInternal.fs +++ b/src/app/Fake.BuildServer.TeamCity/TeamCityInternal.fs @@ -4,61 +4,11 @@ namespace Fake.BuildServer open System open System.IO open Fake.Core -open Microsoft.FSharp.Reflection -open System.Text.RegularExpressions module internal TeamCityWriter = - // Probably too slow... -(* - // From https://gist.github.com/mausch/465668 - // I think we need some cache here... - let PrintfFormatProc (worker: string * obj list -> 'd) (query: PrintfFormat<'a, _, _, 'd>) : 'a = - if not (FSharpType.IsFunction typeof<'a>) then - unbox (worker (query.Value, [])) - else - let rec getFlattenedFunctionElements (functionType: Type) = - let domain, range = FSharpType.GetFunctionElements functionType - if not (FSharpType.IsFunction range) - then domain::[range] - else domain::getFlattenedFunctionElements(range) - let types = getFlattenedFunctionElements typeof<'a> - let rec proc (types: Type list) (values: obj list) (a: obj) : obj = - let values = a::values - match types with - | [x;_] -> - let result = worker (query.Value, List.rev values) - box result - | x::y::z::xs -> - let cont = proc (y::z::xs) values - let ft = FSharpType.MakeFunctionType(y,z) - let cont = FSharpValue.MakeFunction(ft, cont) - box cont - | _ -> failwith "shouldn't happen" - let handler = proc types [] - unbox (FSharpValue.MakeFunction(typeof<'a>, handler)) - let processor (format: string, values: obj list) = - let stripFormatting s = - let i = ref -1 - let eval (rxMatch: Match) = - incr i - sprintf "{%d}" !i - Regex.Replace(s, "%.", eval) - let newFormat = stripFormatting format - let args = - values - |> List.map (sprintf "%O" >> scrub) - |> List.toArray - String.Format(newFormat, args) - |> printfn "%s" - - /// Send message to TeamCity - let sendToTeamCity format = - PrintfFormatProc processor format -*) - /// Encapsulates special chars - let inline encapsulateSpecialChars text = + let private encapsulateSpecialChars text = text |> String.replace "|" "||" |> String.replace "'" "|'" @@ -67,31 +17,108 @@ module internal TeamCityWriter = |> String.replace "[" "|[" |> String.replace "]" "|]" - let scrub = String.removeLineBreaks >> encapsulateSpecialChars + let private singleLine = String.removeLineBreaks >> encapsulateSpecialChars + + type private TeamCityMessage = + | OneParamMultiLine of (Printf.StringFormat string>)*string + | OneParamSingleLine of (Printf.StringFormat string>)*string + | TwoParamMultiLine of (Printf.StringFormat string ->string>)*string*string + | TwoParamSingleLineBoth of (Printf.StringFormat string -> string>)*string*string + | TwoParamSingleLineParam1 of (Printf.StringFormat string -> string>)*string*string + | ThreeParamSingleLineAll of (Printf.StringFormat string -> string -> string>)*string*string*string + | ThreeParamSingleLineParam1 of (Printf.StringFormat string -> string -> string>)*string*string*string + | FiveParamSingleLineParam1 of (Printf.StringFormat string -> string -> string -> string -> string>)*string*string*string*string*string + + /// Send message to TeamCity with single param + let private sendToTeamCity (message:TeamCityMessage) = + let content = match message with + | OneParamMultiLine (fmt, param1) -> sprintf fmt (encapsulateSpecialChars param1) + | OneParamSingleLine (fmt, param1) -> sprintf fmt (singleLine param1) + | TwoParamMultiLine (fmt, param1, param2) -> sprintf fmt (encapsulateSpecialChars param1) (encapsulateSpecialChars param2) + | TwoParamSingleLineBoth (fmt, param1, param2) -> sprintf fmt (singleLine param1) (singleLine param2) + | TwoParamSingleLineParam1 (fmt, param1, param2) -> sprintf fmt (singleLine param1) (encapsulateSpecialChars param2) + | ThreeParamSingleLineAll (fmt, param1, param2, param3) -> sprintf fmt (singleLine param1) (singleLine param2) (singleLine param3) + | ThreeParamSingleLineParam1 (fmt, param1, param2, param3) -> sprintf fmt (singleLine param1) (encapsulateSpecialChars param2) (encapsulateSpecialChars param3) + | FiveParamSingleLineParam1 (fmt, param1, param2, param3, param4, param5) -> sprintf fmt (singleLine param1) (encapsulateSpecialChars param2) (encapsulateSpecialChars param3) (encapsulateSpecialChars param4) (encapsulateSpecialChars param5) - /// Send message to TeamCity - let sendToTeamCity (format:Printf.StringFormat string>) message = - sprintf format (scrub message) // printf is racing with others in parallel mode - |> fun s -> System.Console.WriteLine("\n{0}", s) + System.Console.WriteLine("{0}", content) + + /// Open Named Block + let internal sendOpenBlock name description = sendToTeamCity (TeamCityMessage.TwoParamSingleLineBoth("##teamcity[blockOpened name='%s' description='%s']", name, description)) - let sendToTeamCity2 (format:Printf.StringFormat string -> string>) param1 param2 = - sprintf format (scrub param1) (scrub param2) - // printf is racing with others in parallel mode - |> fun s -> System.Console.WriteLine("\n{0}", s) + /// Close Named Block + let internal sendCloseBlock name = sendToTeamCity (TeamCityMessage.OneParamSingleLine("##teamcity[blockClosed name='%s']", name)) - let sendStrToTeamCity str = - sprintf "%s" str - // printf is racing with others in parallel mode - |> fun s -> System.Console.WriteLine("\n{0}", s) + /// Build status + let internal sendBuildStatus status text = sendToTeamCity (TeamCityMessage.TwoParamSingleLineParam1("##teamcity[buildStatus status='%s' text='%s']", status, text)) - /// Open Named Block - let sendOpenBlock name description = sendToTeamCity2 "##teamcity[blockOpened name='%s' description='%s']" name description + /// Build Problem + let internal sendBuildProblem description = sendToTeamCity (TeamCityMessage.OneParamMultiLine("##teamcity[buildProblem description='%s']", description)) - /// Close Named Block - let sendCloseBlock = sendToTeamCity "##teamcity[blockClosed name='%s']" + // Import Data + let internal sendImportData typ file = sendToTeamCity(TeamCityMessage.TwoParamSingleLineBoth("##teamcity[importData type='%s' file='%s']", typ, file)) + + // Import Data With Tool + let internal sendImportDataWithTool typ tool path = sendToTeamCity(TeamCityMessage.ThreeParamSingleLineAll("##teamcity[importData type='%s' tool='%s' path='%s']", typ, tool, path)) + + // Import Data With Tool + let internal sendDotNetCoverage typ value = sendToTeamCity(TeamCityMessage.TwoParamSingleLineBoth("##teamcity[dotNetCoverage %s='%s']", typ, value)) + + /// Test Started + let internal sendTestStarted name = sendToTeamCity(TeamCityMessage.OneParamMultiLine("##teamcity[testStarted name='%s' captureStandardOutput='true']", name)) + + /// Test Finshed + let internal sendTestFinished name duration = sendToTeamCity(TeamCityMessage.TwoParamMultiLine("##teamcity[testFinished name='%s' duration='%s']", name, duration)) + + /// Test Ignored + let internal sendTestIgnored name message = sendToTeamCity(TeamCityMessage.TwoParamMultiLine("##teamcity[testIgnored name='%s' message='%s']", name, message)) + + /// Test Std Out + let internal sendTestStdOut name out = sendToTeamCity(TeamCityMessage.TwoParamMultiLine("##teamcity[testStdOut name='%s' out='%s']", name, out)) + /// Test Std Error + let internal sendTestStdError name out = sendToTeamCity(TeamCityMessage.TwoParamMultiLine("##teamcity[testStdErr name='%s' out='%s']", name, out)) + /// Test Suite Finished + let internal sendTestSuiteFinished name = sendToTeamCity(TeamCityMessage.OneParamSingleLine("##teamcity[testSuiteFinished name='%s']", name)) + + /// Test Suite Started + let internal sendTestSuiteStarted name = sendToTeamCity(TeamCityMessage.OneParamSingleLine("##teamcity[testSuiteStarted name='%s']", name)) + + /// Progress Message + let internal sendProgressMessage message = sendToTeamCity(TeamCityMessage.OneParamSingleLine("##teamcity[progressMessage '%s']", message)) + + /// Progress Start + let internal sendProgressStart message = sendToTeamCity(TeamCityMessage.OneParamSingleLine("##teamcity[progressStart '%s']", message)) + + /// Progress Finish + let internal sendProgressFinish message = sendToTeamCity(TeamCityMessage.OneParamSingleLine("##teamcity[progressFinish '%s']", message)) + + /// Publish Artifact + let internal sendPublishArtifact path = sendToTeamCity(TeamCityMessage.OneParamSingleLine("##teamcity[publishArtifacts '%s']", path)) + + /// Publish Named Artifact + let internal sendPublishNamedArtifact name path = sendToTeamCity(TeamCityMessage.TwoParamSingleLineBoth("##teamcity[publishArtifacts '%s' => '%s']", name, path)) + + /// Build Number + let internal sendBuildNumber buildNumber = sendToTeamCity(TeamCityMessage.OneParamSingleLine("##teamcity[buildNumber '%s']", buildNumber)) + + /// Build Statistic + let internal sendBuildStatistic key value = sendToTeamCity(TeamCityMessage.TwoParamSingleLineBoth("##teamcity[buildStatisticValue key='%s' value='%s']", key, value)) + + /// Set Parameter + let internal sendSetParameter name value = sendToTeamCity(TeamCityMessage.TwoParamSingleLineBoth("##teamcity[setParameter name='%s' value='%s']", name, value)) + + /// Test Failure + let internal sendTestFailed name message details = sendToTeamCity(TeamCityMessage.ThreeParamSingleLineParam1("##teamcity[testFailed name='%s' message='%s' details='%s']", name, message, details)) + + /// Comparison Failed + let internal sendComparisonFailed name message details expected actual = sendToTeamCity(TeamCityMessage.FiveParamSingleLineParam1("##teamcity[testFailed type='comparisonFailure' name='%s' message='%s' details='%s' expected='%s' actual='%s']", name, message, details, expected, actual)) + + /// Message + let internal sendMessage status text = sendToTeamCity(TeamCityMessage.TwoParamSingleLineParam1("##teamcity[message status='%s' text='%s']", text, status)) + module private JavaPropertiesFile = open System.Text open System.IO