diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..52fa8cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Created by https://www.gitignore.io/ + +### NuGet Packages ### +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* + +### VisualStudioCode ### +.vscode + +### Pester Test Result ### +TestResult.xml diff --git a/README.markdown b/README.markdown index 025ae8e..5bf6192 100644 --- a/README.markdown +++ b/README.markdown @@ -1,6 +1,8 @@ Welcome to the psake-contrib project. ===================================== +[![Join the chat at https://gitter.im/psake/psake-contrib](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/psake/psake-contrib?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + psake-contrib is a repository for scripts, modules and functions that are useful for running a build with [psake](http://github.com/JamesKovacs/psake). diff --git a/Run-Tests.ps1 b/Run-Tests.ps1 new file mode 100644 index 0000000..b55e511 --- /dev/null +++ b/Run-Tests.ps1 @@ -0,0 +1,13 @@ +Import-Module Pester + +Invoke-Pester tests\** -OutputFile TestResult.xml -OutputFormat NUnitXml + +$appVeyorJobId = $env:APPVEYOR_JOB_ID +if ($appVeyorJobId) { + $url = "https://ci.appveyor.com/api/testresults/nunit/$appVeyorJobId" + + $wc = New-Object 'System.Net.WebClient' + $wc.UploadFile($url, (Resolve-Path '.\TestResult.xml')); + + "Uploaded test results to AppVeyor." +} diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..3b19e69 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,17 @@ +# psake-contrib + +version: 1.0.0-build-{build} + +branches: + except: + - gh-pages + +os: Visual Studio 2015 + +install: + - cinst pester + +build: false + +test_script: + - ps: . .\Run-Tests.ps1 diff --git a/build.cmd b/build.cmd new file mode 100644 index 0000000..c14153a --- /dev/null +++ b/build.cmd @@ -0,0 +1,8 @@ +@echo off + +NuGet.exe install Pester -OutputDirectory packages -ExcludeVersion -Verbosity quiet + +set pester=.\packages\Pester\tools\Pester.psm1 + +powershell -NoProfile -ExecutionPolicy Bypass -Command "Import-Module '%pester%'; Invoke-Pester tests\**;" +goto :eof diff --git a/nuget/psake-contrib.nuspec b/nuget/psake-contrib.nuspec index 9b55887..758dec2 100644 --- a/nuget/psake-contrib.nuspec +++ b/nuget/psake-contrib.nuspec @@ -2,7 +2,7 @@ psake-contrib - 1.0.0 + 1.1.0 James Kovacs,James Crowley,Rafal Klys,Artur Dorochowicz,Scott Banwart,Ales Roubicek,C-J Berg,Pepa Stefan https://github.com/psake/psake-contrib false @@ -14,4 +14,4 @@ - \ No newline at end of file + diff --git a/teamcity.psm1 b/teamcity.psm1 index b3cc50d..1182b1d 100644 --- a/teamcity.psm1 +++ b/teamcity.psm1 @@ -15,6 +15,30 @@ if ($env:TEAMCITY_VERSION) { } } +function TeamCity-Message([string]$text, [string]$status = 'NORMAL', [string]$errorDetails) { + $messageAttributes = @{ text=$text; status=$status } + + if ($errorDetails) { + $messageAttributes.errorDetails = $errorDetails + } + + TeamCity-WriteServiceMessage 'message' $messageAttributes +} + +function TeamCity-BlockOpened([string]$name, [string]$description) { + $messageAttributes = @{ name=$name } + + if ($description) { + $messageAttributes.description = $description + } + + TeamCity-WriteServiceMessage 'blockOpened' $messageAttributes +} + +function TeamCity-BlockClosed([string]$name) { + TeamCity-WriteServiceMessage 'blockClosed' @{ name=$name } +} + function TeamCity-TestSuiteStarted([string]$name) { TeamCity-WriteServiceMessage 'testSuiteStarted' @{ name=$name } } @@ -112,18 +136,36 @@ function TeamCity-ReportBuildFinish([string]$message) { TeamCity-WriteServiceMessage 'progressFinish' $message } -function TeamCity-ReportBuildStatus([string]$status, [string]$text='') { - TeamCity-WriteServiceMessage 'buildStatus' @{ status=$status; text=$text } +function TeamCity-ReportBuildStatus([string]$status=$null, [string]$text='') { + $messageAttributes = @{ text=$text } + + if (![string]::IsNullOrEmpty($status)) { + $messageAttributes.status=$status + } + + TeamCity-WriteServiceMessage 'buildStatus' $messageAttributes } function TeamCity-SetBuildNumber([string]$buildNumber) { TeamCity-WriteServiceMessage 'buildNumber' $buildNumber } +function TeamCity-SetParameter([string]$name, [string]$value) { + TeamCity-WriteServiceMessage 'setParameter' @{ name=$name; value=$value } +} + function TeamCity-SetBuildStatistic([string]$key, [string]$value) { TeamCity-WriteServiceMessage 'buildStatisticValue' @{ key=$key; value=$value } } +function TeamCity-EnableServiceMessages() { + TeamCity-WriteServiceMessage 'enableServiceMessages' +} + +function TeamCity-DisableServiceMessages() { + TeamCity-WriteServiceMessage 'disableServiceMessages' +} + function TeamCity-CreateInfoDocument([string]$buildNumber='', [boolean]$status=$true, [string[]]$statusText=$null, [System.Collections.IDictionary]$statistics=$null) { $doc=New-Object xml; $buildEl=$doc.CreateElement('build'); @@ -198,9 +240,10 @@ function TeamCity-WriteServiceMessage([string]$messageName, $messageAttributesHa if ($messageAttributesHashOrSingleValue -is [hashtable]) { $messageAttributesString = ($messageAttributesHashOrSingleValue.GetEnumerator() | %{ "{0}='{1}'" -f $_.Key, (escape $_.Value) }) -join ' ' - } else { - $messageAttributesString = ("'{0}'" -f (escape $messageAttributesHashOrSingleValue)) + $messageAttributesString = " $messageAttributesString" + } elseif ($messageAttributesHashOrSingleValue) { + $messageAttributesString = (" '{0}'" -f (escape $messageAttributesHashOrSingleValue)) } - Write-Output "##teamcity[$messageName $messageAttributesString]" + Write-Output "##teamcity[$messageName$messageAttributesString]" } diff --git a/tests/teamcity.tests.ps1 b/tests/teamcity.tests.ps1 new file mode 100644 index 0000000..5c69fbf --- /dev/null +++ b/tests/teamcity.tests.ps1 @@ -0,0 +1,254 @@ +Import-Module ".\teamcity.psm1" -DisableNameChecking -Force + +Describe "TeamCity-Message" { + It "Writes ##teamcity[message text='Build log message.' status='NORMAL']" { + TeamCity-Message "Build log message." | ` + Should BeExactly "##teamcity[message text='Build log message.' status='NORMAL']" + } + + It "Writes ##teamcity[message text='Exception text' status='ERROR']" { + TeamCity-Message "Exception text" "ERROR" | ` + Should BeExactly "##teamcity[message text='Exception text' status='ERROR']" + } + + It "Writes ##teamcity[message text='Exception text' errorDetails='stack trace' status='ERROR']" { + TeamCity-Message "Exception text" "ERROR" "stack trace" | ` + Should BeExactly "##teamcity[message errorDetails='stack trace' status='ERROR' text='Exception text']" + } +} + +Describe "TeamCity-BlockOpened" { + It "Writes ##teamcity[blockOpened name='MyServiceBlock']" { + TeamCity-BlockOpened "MyServiceBlock" | ` + Should BeExactly "##teamcity[blockOpened name='MyServiceBlock']" + } + + It "Writes ##teamcity[blockOpened name='MyServiceBlock' description='Service block description.']" { + TeamCity-BlockOpened "MyServiceBlock" "Service block description." | ` + Should BeExactly "##teamcity[blockOpened name='MyServiceBlock' description='Service block description.']" + } +} + +Describe "TeamCity-BlockClosed" { + It "Writes ##teamcity[blockClosed name='MyServiceBlock']" { + TeamCity-BlockClosed "MyServiceBlock" | ` + Should BeExactly "##teamcity[blockClosed name='MyServiceBlock']" + } +} + +Describe "TeamCity-WriteServiceMessage" { + It "Writes ##teamcity[message 'Single parameter message.']" { + TeamCity-WriteServiceMessage "message" "Single parameter message." | ` + Should BeExactly "##teamcity[message 'Single parameter message.']" + } + + It "Writes ##teamcity[message key='value']" { + TeamCity-WriteServiceMessage "message" @{ key = 'value'} | ` + Should BeExactly "##teamcity[message key='value']" + } +} + +Describe "TeamCity-TestSuiteStarted" { + It "Writes ##teamcity[testSuiteStarted name='suiteName']" { + TeamCity-TestSuiteStarted "suiteName" | ` + Should BeExactly "##teamcity[testSuiteStarted name='suiteName']" + } +} + +Describe "TeamCity-TestSuiteFinished" { + It "Writes ##teamcity[testSuiteFinished name='suiteName']" { + TeamCity-TestSuiteFinished "suiteName" | ` + Should BeExactly "##teamcity[testSuiteFinished name='suiteName']" + } +} + +Describe "TeamCity-TestStarted" { + It "Writes ##teamcity[testStarted name='testName']" { + TeamCity-TestStarted "testName" | ` + Should BeExactly "##teamcity[testStarted name='testName']" + } +} + +Describe "TeamCity-TestFinished" { + It "Writes ##teamcity[testFinished duration='0' name='testName'] when no duration is given" { + TeamCity-TestFinished "testName" | ` + Should BeExactly "##teamcity[testFinished duration='0' name='testName']" + } + + It "Writes ##teamcity[testFinished duration='0' name='testName'] when 0 duration is given" { + TeamCity-TestFinished "testName" 0 | ` + Should BeExactly "##teamcity[testFinished duration='0' name='testName']" + } + + It "Writes ##teamcity[testFinished duration='247' name='testName'] when 247 duration is given" { + TeamCity-TestFinished "testName" 247 | ` + Should BeExactly "##teamcity[testFinished duration='247' name='testName']" + } + + It "Writes ##teamcity[testFinished duration='-1' name='testName'] when duration is negative number" { + TeamCity-TestFinished "testName" -1 | ` + Should BeExactly "##teamcity[testFinished duration='-1' name='testName']" + } +} + +Describe "TeamCity-TestIgnored" { + It "Writes ##teamcity[testIgnored message='' name='testName']" { + TeamCity-TestIgnored "testName" | ` + Should BeExactly "##teamcity[testIgnored message='' name='testName']" + } + + It "Writes ##teamcity[testIgnored message='ignore comment' name='testName']" { + TeamCity-TestIgnored "testName" "ignore comment" | ` + Should BeExactly "##teamcity[testIgnored message='ignore comment' name='testName']" + } +} + +Describe "TeamCity-TestOutput" { + It "Writes ##teamcity[testStdOut name='className.testName' out='text']" { + TeamCity-TestOutput "className.testName" "text" | ` + Should BeExactly "##teamcity[testStdOut name='className.testName' out='text']" + } +} + +Describe "TeamCity-TestError" { + It "Writes ##teamcity[testStdErr name='className.testName' out='error text']" { + TeamCity-TestError "className.testName" "error text" | ` + Should BeExactly "##teamcity[testStdErr name='className.testName' out='error text']" + } +} + +Describe "TeamCity-TestFailed" { + It "Writes ##teamcity[testFailed message='failure message' type='comparisonFailure' actual='actual value' expected='expected value' details='message and stack trace' name='MyTest.test2']" { + TeamCity-TestFailed "MyTest.test2" "failure message" "message and stack trace" "comparisonFailure" "expected value" "actual value" | ` + Should BeExactly "##teamcity[testFailed message='failure message' type='comparisonFailure' actual='actual value' expected='expected value' details='message and stack trace' name='MyTest.test2']" + } +} + +Describe "TeamCity-ConfigureDotNetCoverage" { + It "Writes ##teamcity[dotNetCoverage ncover3_home='C:\tools\ncover3']" { + TeamCity-ConfigureDotNetCoverage "ncover3_home" "C:\tools\ncover3" | ` + Should BeExactly "##teamcity[dotNetCoverage ncover3_home='C:\tools\ncover3']" + } + + It "Writes ##teamcity[dotNetCoverage partcover_report_xslts='file.xslt=>generatedFileName.html']" { + TeamCity-ConfigureDotNetCoverage "partcover_report_xslts" "file.xslt=>generatedFileName.html" | ` + Should BeExactly "##teamcity[dotNetCoverage partcover_report_xslts='file.xslt=>generatedFileName.html']" + } +} + +Describe "TeamCity-ImportDotNetCoverageResult" { + It "Writes ##teamcity[importData type='dotNetCoverage' tool='ncover3' path='C:\BuildAgent\work\build1\results.xml']" { + TeamCity-ImportDotNetCoverageResult "ncover3" "C:\BuildAgent\work\build1\results.xml" | ` + Should BeExactly "##teamcity[importData path='C:\BuildAgent\work\build1\results.xml' tool='ncover3' type='dotNetCoverage']" + } +} + +Describe "TeamCity-ImportFxCopResult" { + It "Writes ##teamcity[importData type='FxCop' path='C:\BuildAgent\work\results.xml']" { + TeamCity-ImportFxCopResult "C:\BuildAgent\work\results.xml" | ` + Should BeExactly "##teamcity[importData type='FxCop' path='C:\BuildAgent\work\results.xml']" + } +} + +Describe "TeamCity-ImportDuplicatesResult" { + It "Writes ##teamcity[importData type='DotNetDupFinder' path='C:\BuildAgent\work\results.xml']" { + TeamCity-ImportDuplicatesResult "C:\BuildAgent\work\results.xml" | ` + Should BeExactly "##teamcity[importData type='DotNetDupFinder' path='C:\BuildAgent\work\results.xml']" + } +} + +Describe "TeamCity-ImportInspectionCodeResult" { + It "Writes ##teamcity[importData type='ReSharperInspectCode' path='C:\BuildAgent\work\results.xml']" { + TeamCity-ImportInspectionCodeResult "C:\BuildAgent\work\results.xml" | ` + Should BeExactly "##teamcity[importData type='ReSharperInspectCode' path='C:\BuildAgent\work\results.xml']" + } +} + +Describe "TeamCity-ImportNUnitReport" { + It "Writes ##teamcity[importData type='nunit' path='C:\BuildAgent\work\results.xml']" { + TeamCity-ImportNUnitReport "C:\BuildAgent\work\results.xml" | ` + Should BeExactly "##teamcity[importData type='nunit' path='C:\BuildAgent\work\results.xml']" + } +} + +Describe "TeamCity-ImportJSLintReport" { + It "Writes ##teamcity[importData type='jslint' path='C:\BuildAgent\work\results.xml']" { + TeamCity-ImportJSLintReport "C:\BuildAgent\work\results.xml" | ` + Should BeExactly "##teamcity[importData type='jslint' path='C:\BuildAgent\work\results.xml']" + } +} + +Describe "TeamCity-PublishArtifact" { + It "Writes ##teamcity[publishArtifacts 'artifacts\*.exe -> App.zip']" { + TeamCity-PublishArtifact "artifacts\*.exe -> App.zip" | ` + Should BeExactly "##teamcity[publishArtifacts 'artifacts\*.exe -> App.zip']" + } +} + +Describe "TeamCity-ReportBuildStart" { + It "Writes ##teamcity[progressStart 'Compilation started']" { + TeamCity-ReportBuildStart "Compilation started" | ` + Should BeExactly "##teamcity[progressStart 'Compilation started']" + } +} + +Describe "TeamCity-ReportBuildProgress" { + It "Writes ##teamcity[progressMessage 'Build progress message']" { + TeamCity-ReportBuildProgress "Build progress message" | ` + Should BeExactly "##teamcity[progressMessage 'Build progress message']" + } +} + +Describe "TeamCity-ReportBuildFinish" { + It "Writes ##teamcity[progressFinish 'Build finished.']" { + TeamCity-ReportBuildFinish "Build finished." | ` + Should BeExactly "##teamcity[progressFinish 'Build finished.']" + } +} + +Describe "TeamCity-ReportBuildStatus" { + It "Writes ##teamcity[buildStatus text='{build.status.text}, 10/10 tests passed' status='SUCCESS']" { + TeamCity-ReportBuildStatus "SUCCESS" "{build.status.text}, 10/10 tests passed" | ` + Should BeExactly "##teamcity[buildStatus text='{build.status.text}, 10/10 tests passed' status='SUCCESS']" + } + + It "Writes ##teamcity[buildStatus text='{build.status.text}, 10/10 tests passed'] without optional status attribute." { + TeamCity-ReportBuildStatus -text "{build.status.text}, 10/10 tests passed" | ` + Should BeExactly "##teamcity[buildStatus text='{build.status.text}, 10/10 tests passed']" + } +} + +Describe "TeamCity-SetBuildNumber" { + It "Writes ##teamcity[buildNumber '1.2.3_{build.number}-ent']" { + TeamCity-SetBuildNumber "1.2.3_{build.number}-ent" | ` + Should BeExactly "##teamcity[buildNumber '1.2.3_{build.number}-ent']" + } +} + +Describe "TeamCity-SetParameter" { + It "Writes ##teamcity[setParameter value='value1' name='system.p1']" { + TeamCity-SetParameter "system.p1" "value1" | ` + Should BeExactly "##teamcity[setParameter value='value1' name='system.p1']" + } +} + +Describe "TeamCity-SetBuildStatistic" { + It "Writes ##teamcity[buildStatisticValue key='unittests.count' value='19']" { + TeamCity-SetBuildStatistic "unittests.count" "19" | ` + Should BeExactly "##teamcity[buildStatisticValue key='unittests.count' value='19']" + } +} + +Describe "TeamCity-EnableServiceMessages" { + It "Writes ##teamcity[enableServiceMessages]" { + TeamCity-EnableServiceMessages| ` + Should BeExactly "##teamcity[enableServiceMessages]" + } +} + +Describe "TeamCity-DisableServiceMessages" { + It "Writes ##teamcity[disableServiceMessages]" { + TeamCity-DisableServiceMessages | ` + Should BeExactly "##teamcity[disableServiceMessages]" + } +}