├── .editorconfig ├── .gitignore ├── README.markdown ├── Run-Tests.ps1 ├── appveyor.yml ├── biztalk.psm1 ├── build.cmd ├── database.psm1 ├── myget.psm1 ├── nuget └── psake-contrib.nuspec ├── svn.psm1 ├── teamcity.psm1 └── tests └── teamcity.tests.ps1 /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.nuspec] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | 11 | [*.ps1] 12 | charset = utf-8-bom 13 | end_of_line = crlf 14 | insert_final_newline = true 15 | indent_style = space 16 | indent_size = 2 17 | trim_trailing_whitespace = true 18 | 19 | [*.psm1] 20 | charset = utf-8-bom 21 | end_of_line = crlf 22 | insert_final_newline = true 23 | indent_style = space 24 | indent_size = 2 25 | trim_trailing_whitespace = true 26 | 27 | # Note: This configuration is not DRY, but currently editorconfig-sublime has 28 | # problems with multiple strings matching -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/ 2 | 3 | ### NuGet Packages ### 4 | *.nupkg 5 | # The packages folder can be ignored because of Package Restore 6 | **/packages/* 7 | 8 | ### VisualStudioCode ### 9 | .vscode 10 | 11 | ### Pester Test Result ### 12 | TestResult.xml 13 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Welcome to the psake-contrib project. 2 | ===================================== 3 | 4 | psake-contrib is a repository for scripts, modules and functions that are useful for running a build with [psake](http://github.com/psake/psake). 5 | 6 | 7 | ## How to get started: 8 | 9 | **Step 1:** Download the project zip file 10 | 11 | **Step 2:** CD into the directory where you downloaded the zip 12 | 13 | You will need to "unblock" the zip file before extracting - PowerShell by default does not run files downloaded from the internet. 14 | Just right-click the zip and click on "properties" and click on the "unblock" button. 15 | 16 | **Step 3:** Extract the files in the zip 17 | 18 | **Step 4:** Use the scripts and modules in your build 19 | 20 | The following is an excellent technet article on how to ["dot source"](http://technet.microsoft.com/en-us/library/ee176949.aspx) a script 21 | 22 | Type "help about_modules" in PowerShell for help on how to import, load and use modules 23 | 24 | ## How To Contribute, Collaborate, Communicate 25 | 26 | If you'd like to get involved with psake, we have [GitHub Discussions](https://github.com/orgs/psake/discussions), the #psake channel on the [PowerShell Discord](https://aka.ms/psdiscord), and the #psake channel on [PowerShell Slack](https://aka.ms/psslack). 27 | 28 | Anyone can fork the main repository and submit patches, as well. And lastly, the [psake docs](http://github.com/psake/docs) and [issues list](http://github.com/psake/psake-contrib/issues) are also open for additions, edits, and discussion. 29 | -------------------------------------------------------------------------------- /Run-Tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module Pester 2 | 3 | Invoke-Pester tests\** -OutputFile TestResult.xml -OutputFormat NUnitXml 4 | 5 | $appVeyorJobId = $env:APPVEYOR_JOB_ID 6 | if ($appVeyorJobId) { 7 | $url = "https://ci.appveyor.com/api/testresults/nunit/$appVeyorJobId" 8 | 9 | $wc = New-Object 'System.Net.WebClient' 10 | $wc.UploadFile($url, (Resolve-Path '.\TestResult.xml')); 11 | 12 | "Uploaded test results to AppVeyor." 13 | } 14 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # psake-contrib 2 | 3 | version: 1.0.0-build-{build} 4 | 5 | branches: 6 | except: 7 | - gh-pages 8 | 9 | os: Visual Studio 2015 10 | 11 | install: 12 | - cinst pester 13 | 14 | build: false 15 | 16 | test_script: 17 | - ps: . .\Run-Tests.ps1 18 | -------------------------------------------------------------------------------- /biztalk.psm1: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Scott Banwart 2 | 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | if ($env:BTSINSTALLPATH) { 16 | Add-Type -Path "$env:BTSINSTALLPATH\Developer Tools\Microsoft.BizTalk.ExplorerOM.dll" 17 | $btsCatalog = New-Object Microsoft.BizTalk.ExplorerOM.BTSCatalogExplorer 18 | $btsCatalog.ConnectionString = "Data Source=localhost;Initial Catalog=BizTalkMgmtDb;Integrated Security=SSPI" 19 | } 20 | 21 | function Add-Application([string]$appName, [string]$appDescription = "") { 22 | $app = $btsCatalog.AddNewApplication() 23 | $app.Name = $appName 24 | $app.Description = $appDescription 25 | $btsCatalog.SaveChanges() 26 | } 27 | 28 | function Remove-Application([string]$appName) { 29 | $app = $btsCatalog.Applications["$appName"] 30 | $btsCatalog.RemoveApplication($app) 31 | $btsCatalog.SaveChanges() 32 | } 33 | 34 | function Add-ApplicationReference([string]$sourceAppName, [string]$destinationAppName) { 35 | $sourceApp = $btsCatalog.Applications["$sourceAppName"] 36 | $destinationApp = $btsCatalog.Applications[$destinationAppName] 37 | 38 | $destinationApp.AddReference($sourceApp) 39 | $btsCatalog.SaveChanges() 40 | } 41 | 42 | function Remove-ApplicationReference([string]$sourceAppName, [string]$destinationAppName) { 43 | $sourceApp = $btsCatalog.Applications[$sourceAppName] 44 | $destinationApp = $btsCatalog.Applications[$destinationAppName] 45 | 46 | $destinationApp.RemoveReference($sourceApp) 47 | $btsCatalog.SaveChanges() 48 | } 49 | 50 | function Restart-HostInstances([string[]]$hostInstances) { 51 | foreach ($hostInstance in $hostInstances) { 52 | Stop-HostInstance "$hostInstance" 53 | Start-HostInstance "$hostInstance" 54 | } 55 | } 56 | 57 | function Stop-HostInstance([string]$hostInstanceName) { 58 | Stop-Service "$hostInstanceName" 59 | } 60 | 61 | function Start-HostInstance([string]$hostInstanceName) { 62 | Start-Service "$hostInstanceName" 63 | } 64 | 65 | function ExportMsi([string]$appName, [string]$msiPath) { 66 | btstask.exe ExportApp -ApplicationName:"$appName" -Package:(Join-Path $msiPath "$appName.msi") 67 | } 68 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | NuGet.exe install Pester -OutputDirectory packages -ExcludeVersion -Verbosity quiet 4 | 5 | set pester=.\packages\Pester\tools\Pester.psm1 6 | 7 | powershell -NoProfile -ExecutionPolicy Bypass -Command "Import-Module '%pester%'; Invoke-Pester tests\**;" 8 | goto :eof 9 | -------------------------------------------------------------------------------- /database.psm1: -------------------------------------------------------------------------------- 1 | function Invoke-SqlCommand { 2 | [cmdletbinding(DefaultParameterSetName='customauth')] 3 | param( 4 | [Parameter(Mandatory=$false)] 5 | [string] 6 | $sqlServer = ".\SQLEXPRESS", 7 | 8 | [Parameter(Mandatory=$false)] 9 | [string] 10 | $database = "Northwind", 11 | 12 | [Parameter(Mandatory=$true)] 13 | [string] 14 | $sqlCommand, 15 | 16 | [Parameter(Mandatory=$false, ParameterSetName='credauth')] 17 | [System.Management.Automation.PsCredential] 18 | $credential, 19 | 20 | [Parameter(Mandatory=$false, ParameterSetName='customauth')] 21 | [string] 22 | $authentication ="Integrated Security=SSPI;", 23 | 24 | [Parameter(ParameterSetName='devauth')] 25 | [switch] 26 | $developmentAuthentication 27 | ) 28 | 29 | switch ($PsCmdlet.ParameterSetName) { 30 | 'devauth' { 31 | $authentication = 'User ID=sa;Password=pass' 32 | Write-Debug "Using development authentication: $authentication" 33 | } 34 | 'credauth' { 35 | $plainCred = $credential.GetNetworkCredential() 36 | $authentication = "uid={0};pwd={1};" -f $plainCred.Username,$plainCred.Password 37 | Write-Debug "Using passed credentials, user: $($plainCred.Username)" 38 | } 39 | 'customauth' { 40 | Write-Debug "Using custom authentication: $authentication" 41 | } 42 | default { throw "Parameter set name unknown: $($PsCmdlet.ParameterSetName)" } 43 | } 44 | 45 | $connectionString = "Server=$sqlServer;Database=$database;$authentication;" 46 | 47 | write-debug "Connection string: $connectionString" 48 | ## Connect to the data source and open it 49 | $SqlConnection = New-Object System.Data.SqlClient.SqlConnection 50 | $SqlConnection.ConnectionString = $connectionString 51 | 52 | $SqlCmd = New-Object System.Data.SqlClient.SqlCommand 53 | $SqlCmd.CommandText = $sqlCommand 54 | $SqlCmd.Connection = $SqlConnection 55 | $SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter 56 | $SqlAdapter.SelectCommand = $SqlCmd 57 | $dataSet = New-Object System.Data.DataSet 58 | $SqlAdapter.Fill($dataSet) 59 | $SqlConnection.Close() 60 | $SqlCmd.Dispose() 61 | 62 | ## Return all of the rows from their query 63 | $dataSet.Tables | Select-Object -Expand Rows 64 | } -------------------------------------------------------------------------------- /myget.psm1: -------------------------------------------------------------------------------- 1 | function MyGet-SetBuildNumber([string]$buildNumber) { 2 | MyGet-WriteServiceMessage 'buildNumber' $buildNumber 3 | } 4 | 5 | function MyGet-WriteBuildLogMessage([string]$text, [string]$errorDetails, [string]$status) { 6 | MyGet-WriteServiceMessage 'message ' @{ text=$text; errorDetails=$errorDetails; status=$status} 7 | } 8 | 9 | function MyGet-SetEnvironmentVariable([string]$name, [string]$value) { 10 | MyGet-WriteServiceMessage 'setParameter' @{ name=$name; value=$value} 11 | } 12 | 13 | function MyGet-ReportBuildProblem([string]$buildProblem) { 14 | MyGet-WriteServiceMessage 'buildProblem' @{ description=$buildProblem; } 15 | } 16 | 17 | function MyGet-WriteServiceMessage([string]$messageName, $messageAttributesHashOrSingleValue) { 18 | function escape([string]$value) { 19 | ([char[]] $value | 20 | %{ switch ($_) 21 | { 22 | "|" { "||" } 23 | "'" { "|'" } 24 | "`n" { "|n" } 25 | "`r" { "|r" } 26 | "[" { "|[" } 27 | "]" { "|]" } 28 | ([char] 0x0085) { "|x" } 29 | ([char] 0x2028) { "|l" } 30 | ([char] 0x2029) { "|p" } 31 | default { $_ } 32 | } 33 | } ) -join '' 34 | } 35 | 36 | if ($messageAttributesHashOrSingleValue -is [hashtable]) { 37 | $messageAttributesString = ($messageAttributesHashOrSingleValue.GetEnumerator() | 38 | %{ "{0}='{1}'" -f $_.Key, (escape $_.Value) }) -join ' ' 39 | } else { 40 | $messageAttributesString = ("'{0}'" -f (escape $messageAttributesHashOrSingleValue)) 41 | } 42 | 43 | Write-Output "##myget[$messageName $messageAttributesString]" 44 | } -------------------------------------------------------------------------------- /nuget/psake-contrib.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | psake-contrib 5 | 1.2.0 6 | James Kovacs,James Crowley,Rafal Klys,Artur Dorochowicz,Scott Banwart,Ales Roubicek,C-J Berg,Pepa Stefan,Gary Ewan Park 7 | https://github.com/psake/psake-contrib 8 | false 9 | psake-contrib is a repository for scripts, modules and functions that are useful for running a build with psake. 10 | Contrib project for psake extensions 11 | en-US 12 | psake build powershell 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /svn.psm1: -------------------------------------------------------------------------------- 1 | # takes one parameter and somehow notifies user; It could be email, growl message, output to file... 2 | [scriptblock]$script:Notifier = {} 3 | # todo: read output from xml 4 | 5 | function Set-SvnNotifier { 6 | param( 7 | [Parameter(Mandatory=$true)][scriptblock]$notifier 8 | ) 9 | $script:Notifier = $notifier 10 | } 11 | 12 | function Get-SvnInfo 13 | { 14 | param( 15 | [Parameter(Mandatory=$true)][string]$dir 16 | ) 17 | $info = svn info $dir 18 | $ret = new-object PSObject 19 | $info | % { 20 | if ($_ -match '^Revision') { $ret | Add-Member NoteProperty Revision ($_ -replace 'Revision[\s:]*','') } 21 | if ($_ -match '^Last Changed Author') { $ret | Add-Member NoteProperty Author ($_ -replace 'Last Changed Author[\s:]*','') } 22 | if ($_ -match '^Last Changed Date') { $ret | Add-Member NoteProperty Date ($_ -replace 'Last Changed Date[\s:]*','') } 23 | } 24 | $ret 25 | } 26 | 27 | function Update-Svn 28 | { 29 | param( 30 | [Parameter(Mandatory=$true)][string]$dir, 31 | [switch]$gui, 32 | [switch]$Wait 33 | ) 34 | $info = Get-SvnInfo $dir 35 | if ($gui) { 36 | Start-Process TortoiseProc.exe -Argument "/command:update", "/path:`"$dir`"" -wait:$wait 37 | } else { 38 | svn update $dir | 39 | % { 40 | $m = $_ 41 | switch -regex ($m) { 42 | '^(Updated to|At revision)' { write-host $m } 43 | '^\s*U\s' { write-host $m -fore Yellow } 44 | '^\s*A\s' { write-host $m -fore Green } 45 | '^\s*D\s' { write-host $m -fore Red } 46 | '^\s*G\s' { write-host $m -fore DarkCyan } 47 | default { write-host $m -fore Magenta } 48 | } 49 | } 50 | & $Notifier 'updated from SVN' 51 | } 52 | $info2 = Get-SvnInfo $dir 53 | if ($info.Revision -ne $info2.Revision) { 54 | #Write-Host ("State before: {0} - {1} - {2}" -f $info.Revision, $info.Author, $info.Date) -fore Green 55 | #Write-Host ("State after: {0} - {1} - {2}" -f $info2.Revision, $info2.Author, $info2.Date) -fore Green 56 | Get-SvnLogInfos -dir $dir -maxCount ($info2.Revision - $info.Revision) | % { 57 | write-host $_.Header-fore Green 58 | $_.Info | % { write-host " $_" } 59 | } 60 | } 61 | # more colors: 62 | #"Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White" -split ', '|% { write-host $_ -fore $_ } 63 | } 64 | 65 | function Commit-Svn 66 | { 67 | param( 68 | [Parameter(Mandatory=$true)][string]$dir, 69 | [switch]$Wait 70 | ) 71 | Start-Process TortoiseProc.exe -Argument "/command:commit", "/path:`"$dir`"" -wait:$wait 72 | } 73 | 74 | function Get-SvnLogInfos 75 | { 76 | param( 77 | [Parameter(Mandatory=$true)][string]$dir, 78 | [Parameter(Mandatory=$false)][int]$maxCount=1 79 | ) 80 | $ret = @() 81 | svn log $dir -l $maxCount | % { 82 | $line = $_ 83 | switch -regex ($_) { 84 | '^(-*|\s*)$' { return } 85 | '^r\d+\s*\|\s*[\w\.]+\s\|' { 86 | if ($header -ne $null) { $ret += $header } 87 | $header = new-object PSObject -property @{Header = $line; Info = @() } 88 | } 89 | default { 90 | $header.Info += $line 91 | } 92 | } 93 | } 94 | if ($header -ne $null) { $ret += $header } 95 | $ret 96 | } -------------------------------------------------------------------------------- /teamcity.psm1: -------------------------------------------------------------------------------- 1 | if ($env:TEAMCITY_VERSION) { 2 | # When PowerShell is started through TeamCity's Command Runner, the standard 3 | # output will be wrapped at column 80 (a default). This has a negative impact 4 | # on service messages, as TeamCity quite naturally fails parsing a wrapped 5 | # message. The solution is to set a new, much wider output width. It will 6 | # only be set if TEAMCITY_VERSION exists, i.e., if started by TeamCity. 7 | $host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.Size(8192,50) 8 | } 9 | 10 | function TeamCity-Message([string]$text, [string]$status = 'NORMAL', [string]$errorDetails) { 11 | $messageAttributes = @{ text=$text; status=$status } 12 | 13 | if ($errorDetails) { 14 | $messageAttributes.errorDetails = $errorDetails 15 | } 16 | 17 | TeamCity-WriteServiceMessage 'message' $messageAttributes 18 | } 19 | 20 | function TeamCity-BlockOpened([string]$name, [string]$description) { 21 | $messageAttributes = @{ name=$name } 22 | 23 | if ($description) { 24 | $messageAttributes.description = $description 25 | } 26 | 27 | TeamCity-WriteServiceMessage 'blockOpened' $messageAttributes 28 | } 29 | 30 | function TeamCity-BlockClosed([string]$name) { 31 | TeamCity-WriteServiceMessage 'blockClosed' @{ name=$name } 32 | } 33 | 34 | function TeamCity-TestSuiteStarted([string]$name) { 35 | TeamCity-WriteServiceMessage 'testSuiteStarted' @{ name=$name } 36 | } 37 | 38 | function TeamCity-TestSuiteFinished([string]$name) { 39 | TeamCity-WriteServiceMessage 'testSuiteFinished' @{ name=$name } 40 | } 41 | 42 | function TeamCity-TestStarted([string]$name) { 43 | TeamCity-WriteServiceMessage 'testStarted' @{ name=$name } 44 | } 45 | 46 | function TeamCity-TestFinished([string]$name, [int]$duration) { 47 | $messageAttributes = @{name=$name; duration=$duration} 48 | 49 | if ($duration -gt 0) { 50 | $messageAttributes.duration=$duration 51 | } 52 | 53 | TeamCity-WriteServiceMessage 'testFinished' $messageAttributes 54 | } 55 | 56 | function TeamCity-TestIgnored([string]$name, [string]$message='') { 57 | TeamCity-WriteServiceMessage 'testIgnored' @{ name=$name; message=$message } 58 | } 59 | 60 | function TeamCity-TestOutput([string]$name, [string]$output) { 61 | TeamCity-WriteServiceMessage 'testStdOut' @{ name=$name; out=$output } 62 | } 63 | 64 | function TeamCity-TestError([string]$name, [string]$output) { 65 | TeamCity-WriteServiceMessage 'testStdErr' @{ name=$name; out=$output } 66 | } 67 | 68 | function TeamCity-TestFailed([string]$name, [string]$message, [string]$details='', [string]$type='', [string]$expected='', [string]$actual='') { 69 | $messageAttributes = @{ name=$name; message=$message; details=$details } 70 | 71 | if (![string]::IsNullOrEmpty($type)) { 72 | $messageAttributes.type = $type 73 | } 74 | 75 | if (![string]::IsNullOrEmpty($expected)) { 76 | $messageAttributes.expected=$expected 77 | } 78 | if (![string]::IsNullOrEmpty($actual)) { 79 | $messageAttributes.actual=$actual 80 | } 81 | 82 | TeamCity-WriteServiceMessage 'testFailed' $messageAttributes 83 | } 84 | 85 | # See http://confluence.jetbrains.net/display/TCD5/Manually+Configuring+Reporting+Coverage 86 | function TeamCity-ConfigureDotNetCoverage([string]$key, [string]$value) { 87 | TeamCity-WriteServiceMessage 'dotNetCoverage' @{ $key=$value } 88 | } 89 | 90 | function TeamCity-ImportDotNetCoverageResult([string]$tool, [string]$path) { 91 | TeamCity-WriteServiceMessage 'importData' @{ type='dotNetCoverage'; tool=$tool; path=$path } 92 | } 93 | 94 | # See http://confluence.jetbrains.net/display/TCD5/FxCop_#FxCop_-UsingServiceMessages 95 | function TeamCity-ImportFxCopResult([string]$path) { 96 | TeamCity-WriteServiceMessage 'importData' @{ type='FxCop'; path=$path } 97 | } 98 | 99 | function TeamCity-ImportDuplicatesResult([string]$path) { 100 | TeamCity-WriteServiceMessage 'importData' @{ type='DotNetDupFinder'; path=$path } 101 | } 102 | 103 | function TeamCity-ImportInspectionCodeResult([string]$path) { 104 | TeamCity-WriteServiceMessage 'importData' @{ type='ReSharperInspectCode'; path=$path } 105 | } 106 | 107 | function TeamCity-ImportNUnitReport([string]$path) { 108 | TeamCity-WriteServiceMessage 'importData' @{ type='nunit'; path=$path } 109 | } 110 | 111 | function TeamCity-ImportJSLintReport([string]$path) { 112 | TeamCity-WriteServiceMessage 'importData' @{ type='jslint'; path=$path } 113 | } 114 | 115 | function TeamCity-PublishArtifact([string]$path) { 116 | TeamCity-WriteServiceMessage 'publishArtifacts' $path 117 | } 118 | 119 | function TeamCity-ReportBuildStart([string]$message) { 120 | TeamCity-WriteServiceMessage 'progressStart' $message 121 | } 122 | 123 | function TeamCity-ReportBuildProgress([string]$message) { 124 | TeamCity-WriteServiceMessage 'progressMessage' $message 125 | } 126 | 127 | function TeamCity-ReportBuildFinish([string]$message) { 128 | TeamCity-WriteServiceMessage 'progressFinish' $message 129 | } 130 | 131 | function TeamCity-ReportBuildStatus([string]$status=$null, [string]$text='') { 132 | $messageAttributes = @{ text=$text } 133 | 134 | if (![string]::IsNullOrEmpty($status)) { 135 | $messageAttributes.status=$status 136 | } 137 | 138 | TeamCity-WriteServiceMessage 'buildStatus' $messageAttributes 139 | } 140 | 141 | function TeamCity-ReportBuildProblem([string]$description, [string]$identity=$null) { 142 | $messageAttributes = @{ description=$description } 143 | 144 | if (![string]::IsNullOrEmpty($identity)) { 145 | $messageAttributes.identity=$identity 146 | } 147 | 148 | TeamCity-WriteServiceMessage 'buildProblem' $messageAttributes 149 | } 150 | 151 | function TeamCity-SetBuildNumber([string]$buildNumber) { 152 | TeamCity-WriteServiceMessage 'buildNumber' $buildNumber 153 | } 154 | 155 | function TeamCity-SetParameter([string]$name, [string]$value) { 156 | TeamCity-WriteServiceMessage 'setParameter' @{ name=$name; value=$value } 157 | } 158 | 159 | function TeamCity-SetBuildStatistic([string]$key, [string]$value) { 160 | TeamCity-WriteServiceMessage 'buildStatisticValue' @{ key=$key; value=$value } 161 | } 162 | 163 | function TeamCity-EnableServiceMessages() { 164 | TeamCity-WriteServiceMessage 'enableServiceMessages' 165 | } 166 | 167 | function TeamCity-DisableServiceMessages() { 168 | TeamCity-WriteServiceMessage 'disableServiceMessages' 169 | } 170 | 171 | function TeamCity-CreateInfoDocument([string]$buildNumber='', [boolean]$status=$true, [string[]]$statusText=$null, [System.Collections.IDictionary]$statistics=$null) { 172 | $doc=New-Object xml; 173 | $buildEl=$doc.CreateElement('build'); 174 | 175 | if (![string]::IsNullOrEmpty($buildNumber)) { 176 | $buildEl.SetAttribute('number', $buildNumber); 177 | } 178 | 179 | $buildEl=$doc.AppendChild($buildEl); 180 | 181 | $statusEl=$doc.CreateElement('statusInfo'); 182 | if ($status) { 183 | $statusEl.SetAttribute('status', 'SUCCESS'); 184 | } else { 185 | $statusEl.SetAttribute('status', 'FAILURE'); 186 | } 187 | 188 | if ($statusText -ne $null) { 189 | foreach ($text in $statusText) { 190 | $textEl=$doc.CreateElement('text'); 191 | $textEl.SetAttribute('action', 'append'); 192 | $textEl.set_InnerText($text); 193 | $textEl=$statusEl.AppendChild($textEl); 194 | } 195 | } 196 | 197 | $statusEl=$buildEl.AppendChild($statusEl); 198 | 199 | if ($statistics -ne $null) { 200 | foreach ($key in $statistics.Keys) { 201 | $val=$statistics.$key 202 | if ($val -eq $null) { 203 | $val='' 204 | } 205 | 206 | $statEl=$doc.CreateElement('statisticsValue'); 207 | $statEl.SetAttribute('key', $key); 208 | $statEl.SetAttribute('value', $val.ToString()); 209 | $statEl=$buildEl.AppendChild($statEl); 210 | } 211 | } 212 | 213 | return $doc; 214 | } 215 | 216 | function TeamCity-WriteInfoDocument([xml]$doc) { 217 | $dir=(Split-Path $buildFile) 218 | $path=(Join-Path $dir 'teamcity-info.xml') 219 | 220 | $doc.Save($path); 221 | } 222 | 223 | function TeamCity-WriteServiceMessage([string]$messageName, $messageAttributesHashOrSingleValue) { 224 | function escape([string]$value) { 225 | ([char[]] $value | 226 | %{ switch ($_) 227 | { 228 | "|" { "||" } 229 | "'" { "|'" } 230 | "`n" { "|n" } 231 | "`r" { "|r" } 232 | "[" { "|[" } 233 | "]" { "|]" } 234 | ([char] 0x0085) { "|x" } 235 | ([char] 0x2028) { "|l" } 236 | ([char] 0x2029) { "|p" } 237 | default { $_ } 238 | } 239 | } ) -join '' 240 | } 241 | 242 | if ($messageAttributesHashOrSingleValue -is [hashtable]) { 243 | $messageAttributesString = ($messageAttributesHashOrSingleValue.GetEnumerator() | 244 | %{ "{0}='{1}'" -f $_.Key, (escape $_.Value) }) -join ' ' 245 | $messageAttributesString = " $messageAttributesString" 246 | } elseif ($messageAttributesHashOrSingleValue) { 247 | $messageAttributesString = (" '{0}'" -f (escape $messageAttributesHashOrSingleValue)) 248 | } 249 | 250 | Write-Output "##teamcity[$messageName$messageAttributesString]" 251 | } 252 | -------------------------------------------------------------------------------- /tests/teamcity.tests.ps1: -------------------------------------------------------------------------------- 1 | Import-Module ".\teamcity.psm1" -DisableNameChecking -Force 2 | 3 | Describe "TeamCity-Message" { 4 | It "Writes ##teamcity[message text='Build log message.' status='NORMAL']" { 5 | TeamCity-Message "Build log message." | ` 6 | Should BeExactly "##teamcity[message text='Build log message.' status='NORMAL']" 7 | } 8 | 9 | It "Writes ##teamcity[message text='Exception text' status='ERROR']" { 10 | TeamCity-Message "Exception text" "ERROR" | ` 11 | Should BeExactly "##teamcity[message text='Exception text' status='ERROR']" 12 | } 13 | 14 | It "Writes ##teamcity[message text='Exception text' errorDetails='stack trace' status='ERROR']" { 15 | TeamCity-Message "Exception text" "ERROR" "stack trace" | ` 16 | Should BeExactly "##teamcity[message errorDetails='stack trace' status='ERROR' text='Exception text']" 17 | } 18 | } 19 | 20 | Describe "TeamCity-BlockOpened" { 21 | It "Writes ##teamcity[blockOpened name='MyServiceBlock']" { 22 | TeamCity-BlockOpened "MyServiceBlock" | ` 23 | Should BeExactly "##teamcity[blockOpened name='MyServiceBlock']" 24 | } 25 | 26 | It "Writes ##teamcity[blockOpened name='MyServiceBlock' description='Service block description.']" { 27 | TeamCity-BlockOpened "MyServiceBlock" "Service block description." | ` 28 | Should BeExactly "##teamcity[blockOpened name='MyServiceBlock' description='Service block description.']" 29 | } 30 | } 31 | 32 | Describe "TeamCity-BlockClosed" { 33 | It "Writes ##teamcity[blockClosed name='MyServiceBlock']" { 34 | TeamCity-BlockClosed "MyServiceBlock" | ` 35 | Should BeExactly "##teamcity[blockClosed name='MyServiceBlock']" 36 | } 37 | } 38 | 39 | Describe "TeamCity-WriteServiceMessage" { 40 | It "Writes ##teamcity[message 'Single parameter message.']" { 41 | TeamCity-WriteServiceMessage "message" "Single parameter message." | ` 42 | Should BeExactly "##teamcity[message 'Single parameter message.']" 43 | } 44 | 45 | It "Writes ##teamcity[message key='value']" { 46 | TeamCity-WriteServiceMessage "message" @{ key = 'value'} | ` 47 | Should BeExactly "##teamcity[message key='value']" 48 | } 49 | } 50 | 51 | Describe "TeamCity-TestSuiteStarted" { 52 | It "Writes ##teamcity[testSuiteStarted name='suiteName']" { 53 | TeamCity-TestSuiteStarted "suiteName" | ` 54 | Should BeExactly "##teamcity[testSuiteStarted name='suiteName']" 55 | } 56 | } 57 | 58 | Describe "TeamCity-TestSuiteFinished" { 59 | It "Writes ##teamcity[testSuiteFinished name='suiteName']" { 60 | TeamCity-TestSuiteFinished "suiteName" | ` 61 | Should BeExactly "##teamcity[testSuiteFinished name='suiteName']" 62 | } 63 | } 64 | 65 | Describe "TeamCity-TestStarted" { 66 | It "Writes ##teamcity[testStarted name='testName']" { 67 | TeamCity-TestStarted "testName" | ` 68 | Should BeExactly "##teamcity[testStarted name='testName']" 69 | } 70 | } 71 | 72 | Describe "TeamCity-TestFinished" { 73 | It "Writes ##teamcity[testFinished duration='0' name='testName'] when no duration is given" { 74 | TeamCity-TestFinished "testName" | ` 75 | Should BeExactly "##teamcity[testFinished duration='0' name='testName']" 76 | } 77 | 78 | It "Writes ##teamcity[testFinished duration='0' name='testName'] when 0 duration is given" { 79 | TeamCity-TestFinished "testName" 0 | ` 80 | Should BeExactly "##teamcity[testFinished duration='0' name='testName']" 81 | } 82 | 83 | It "Writes ##teamcity[testFinished duration='247' name='testName'] when 247 duration is given" { 84 | TeamCity-TestFinished "testName" 247 | ` 85 | Should BeExactly "##teamcity[testFinished duration='247' name='testName']" 86 | } 87 | 88 | It "Writes ##teamcity[testFinished duration='-1' name='testName'] when duration is negative number" { 89 | TeamCity-TestFinished "testName" -1 | ` 90 | Should BeExactly "##teamcity[testFinished duration='-1' name='testName']" 91 | } 92 | } 93 | 94 | Describe "TeamCity-TestIgnored" { 95 | It "Writes ##teamcity[testIgnored message='' name='testName']" { 96 | TeamCity-TestIgnored "testName" | ` 97 | Should BeExactly "##teamcity[testIgnored message='' name='testName']" 98 | } 99 | 100 | It "Writes ##teamcity[testIgnored message='ignore comment' name='testName']" { 101 | TeamCity-TestIgnored "testName" "ignore comment" | ` 102 | Should BeExactly "##teamcity[testIgnored message='ignore comment' name='testName']" 103 | } 104 | } 105 | 106 | Describe "TeamCity-TestOutput" { 107 | It "Writes ##teamcity[testStdOut name='className.testName' out='text']" { 108 | TeamCity-TestOutput "className.testName" "text" | ` 109 | Should BeExactly "##teamcity[testStdOut name='className.testName' out='text']" 110 | } 111 | } 112 | 113 | Describe "TeamCity-TestError" { 114 | It "Writes ##teamcity[testStdErr name='className.testName' out='error text']" { 115 | TeamCity-TestError "className.testName" "error text" | ` 116 | Should BeExactly "##teamcity[testStdErr name='className.testName' out='error text']" 117 | } 118 | } 119 | 120 | Describe "TeamCity-TestFailed" { 121 | It "Writes ##teamcity[testFailed message='failure message' type='comparisonFailure' actual='actual value' expected='expected value' details='message and stack trace' name='MyTest.test2']" { 122 | TeamCity-TestFailed "MyTest.test2" "failure message" "message and stack trace" "comparisonFailure" "expected value" "actual value" | ` 123 | Should BeExactly "##teamcity[testFailed message='failure message' type='comparisonFailure' actual='actual value' expected='expected value' details='message and stack trace' name='MyTest.test2']" 124 | } 125 | } 126 | 127 | Describe "TeamCity-ConfigureDotNetCoverage" { 128 | It "Writes ##teamcity[dotNetCoverage ncover3_home='C:\tools\ncover3']" { 129 | TeamCity-ConfigureDotNetCoverage "ncover3_home" "C:\tools\ncover3" | ` 130 | Should BeExactly "##teamcity[dotNetCoverage ncover3_home='C:\tools\ncover3']" 131 | } 132 | 133 | It "Writes ##teamcity[dotNetCoverage partcover_report_xslts='file.xslt=>generatedFileName.html']" { 134 | TeamCity-ConfigureDotNetCoverage "partcover_report_xslts" "file.xslt=>generatedFileName.html" | ` 135 | Should BeExactly "##teamcity[dotNetCoverage partcover_report_xslts='file.xslt=>generatedFileName.html']" 136 | } 137 | } 138 | 139 | Describe "TeamCity-ImportDotNetCoverageResult" { 140 | It "Writes ##teamcity[importData type='dotNetCoverage' tool='ncover3' path='C:\BuildAgent\work\build1\results.xml']" { 141 | TeamCity-ImportDotNetCoverageResult "ncover3" "C:\BuildAgent\work\build1\results.xml" | ` 142 | Should BeExactly "##teamcity[importData path='C:\BuildAgent\work\build1\results.xml' tool='ncover3' type='dotNetCoverage']" 143 | } 144 | } 145 | 146 | Describe "TeamCity-ImportFxCopResult" { 147 | It "Writes ##teamcity[importData type='FxCop' path='C:\BuildAgent\work\results.xml']" { 148 | TeamCity-ImportFxCopResult "C:\BuildAgent\work\results.xml" | ` 149 | Should BeExactly "##teamcity[importData type='FxCop' path='C:\BuildAgent\work\results.xml']" 150 | } 151 | } 152 | 153 | Describe "TeamCity-ImportDuplicatesResult" { 154 | It "Writes ##teamcity[importData type='DotNetDupFinder' path='C:\BuildAgent\work\results.xml']" { 155 | TeamCity-ImportDuplicatesResult "C:\BuildAgent\work\results.xml" | ` 156 | Should BeExactly "##teamcity[importData type='DotNetDupFinder' path='C:\BuildAgent\work\results.xml']" 157 | } 158 | } 159 | 160 | Describe "TeamCity-ImportInspectionCodeResult" { 161 | It "Writes ##teamcity[importData type='ReSharperInspectCode' path='C:\BuildAgent\work\results.xml']" { 162 | TeamCity-ImportInspectionCodeResult "C:\BuildAgent\work\results.xml" | ` 163 | Should BeExactly "##teamcity[importData type='ReSharperInspectCode' path='C:\BuildAgent\work\results.xml']" 164 | } 165 | } 166 | 167 | Describe "TeamCity-ImportNUnitReport" { 168 | It "Writes ##teamcity[importData type='nunit' path='C:\BuildAgent\work\results.xml']" { 169 | TeamCity-ImportNUnitReport "C:\BuildAgent\work\results.xml" | ` 170 | Should BeExactly "##teamcity[importData type='nunit' path='C:\BuildAgent\work\results.xml']" 171 | } 172 | } 173 | 174 | Describe "TeamCity-ImportJSLintReport" { 175 | It "Writes ##teamcity[importData type='jslint' path='C:\BuildAgent\work\results.xml']" { 176 | TeamCity-ImportJSLintReport "C:\BuildAgent\work\results.xml" | ` 177 | Should BeExactly "##teamcity[importData type='jslint' path='C:\BuildAgent\work\results.xml']" 178 | } 179 | } 180 | 181 | Describe "TeamCity-PublishArtifact" { 182 | It "Writes ##teamcity[publishArtifacts 'artifacts\*.exe -> App.zip']" { 183 | TeamCity-PublishArtifact "artifacts\*.exe -> App.zip" | ` 184 | Should BeExactly "##teamcity[publishArtifacts 'artifacts\*.exe -> App.zip']" 185 | } 186 | } 187 | 188 | Describe "TeamCity-ReportBuildStart" { 189 | It "Writes ##teamcity[progressStart 'Compilation started']" { 190 | TeamCity-ReportBuildStart "Compilation started" | ` 191 | Should BeExactly "##teamcity[progressStart 'Compilation started']" 192 | } 193 | } 194 | 195 | Describe "TeamCity-ReportBuildProgress" { 196 | It "Writes ##teamcity[progressMessage 'Build progress message']" { 197 | TeamCity-ReportBuildProgress "Build progress message" | ` 198 | Should BeExactly "##teamcity[progressMessage 'Build progress message']" 199 | } 200 | } 201 | 202 | Describe "TeamCity-ReportBuildFinish" { 203 | It "Writes ##teamcity[progressFinish 'Build finished.']" { 204 | TeamCity-ReportBuildFinish "Build finished." | ` 205 | Should BeExactly "##teamcity[progressFinish 'Build finished.']" 206 | } 207 | } 208 | 209 | Describe "TeamCity-ReportBuildStatus" { 210 | It "Writes ##teamcity[buildStatus text='{build.status.text}, 10/10 tests passed' status='SUCCESS']" { 211 | TeamCity-ReportBuildStatus "SUCCESS" "{build.status.text}, 10/10 tests passed" | ` 212 | Should BeExactly "##teamcity[buildStatus text='{build.status.text}, 10/10 tests passed' status='SUCCESS']" 213 | } 214 | 215 | It "Writes ##teamcity[buildStatus text='{build.status.text}, 10/10 tests passed'] without optional status attribute." { 216 | TeamCity-ReportBuildStatus -text "{build.status.text}, 10/10 tests passed" | ` 217 | Should BeExactly "##teamcity[buildStatus text='{build.status.text}, 10/10 tests passed']" 218 | } 219 | } 220 | 221 | Describe "TeamCity-ReportBuildProblem" { 222 | It "Writes ##teamcity[buildProblem description='A problem occured.' identity='SOME_IDENTITY']" { 223 | TeamCity-ReportBuildProblem "A problem occured." "SOME_IDENTITY" | ` 224 | Should BeExactly "##teamcity[buildProblem description='A problem occured.' identity='SOME_IDENTITY']" 225 | } 226 | 227 | It "Writes ##teamcity[buildStatus text='A problem occured.'] without optional identity attribute." { 228 | TeamCity-ReportBuildStatus -text "A problem occured." | ` 229 | Should BeExactly "##teamcity[buildStatus text='A problem occured.']" 230 | } 231 | } 232 | 233 | Describe "TeamCity-SetBuildNumber" { 234 | It "Writes ##teamcity[buildNumber '1.2.3_{build.number}-ent']" { 235 | TeamCity-SetBuildNumber "1.2.3_{build.number}-ent" | ` 236 | Should BeExactly "##teamcity[buildNumber '1.2.3_{build.number}-ent']" 237 | } 238 | } 239 | 240 | Describe "TeamCity-SetParameter" { 241 | It "Writes ##teamcity[setParameter value='value1' name='system.p1']" { 242 | TeamCity-SetParameter "system.p1" "value1" | ` 243 | Should BeExactly "##teamcity[setParameter value='value1' name='system.p1']" 244 | } 245 | } 246 | 247 | Describe "TeamCity-SetBuildStatistic" { 248 | It "Writes ##teamcity[buildStatisticValue key='unittests.count' value='19']" { 249 | TeamCity-SetBuildStatistic "unittests.count" "19" | ` 250 | Should BeExactly "##teamcity[buildStatisticValue key='unittests.count' value='19']" 251 | } 252 | } 253 | 254 | Describe "TeamCity-EnableServiceMessages" { 255 | It "Writes ##teamcity[enableServiceMessages]" { 256 | TeamCity-EnableServiceMessages| ` 257 | Should BeExactly "##teamcity[enableServiceMessages]" 258 | } 259 | } 260 | 261 | Describe "TeamCity-DisableServiceMessages" { 262 | It "Writes ##teamcity[disableServiceMessages]" { 263 | TeamCity-DisableServiceMessages | ` 264 | Should BeExactly "##teamcity[disableServiceMessages]" 265 | } 266 | } 267 | --------------------------------------------------------------------------------