16

Is there a simple way to have TeamCity include a text or html change-log as one of its output artifacts?

Perhaps I need to go down the route of having msbuild or some other process create the change log but as TeamCity generates one for every build, I'm wondering if there is already a simple way to access it as an artifact and include it in the artifact paths directives so that it can be part of a release package.

grenade
  • 31,451
  • 23
  • 97
  • 126

4 Answers4

15

Yes, the change-log is accessible as a file, path to this file is in the TeamCity build parameter:

%system.teamcity.build.changedFiles.file%

So you could do this:

  • Add a command-line build step to your build.
  • Use type Custom Script.
  • Enter this script:
copy "%system.teamcity.build.changedFiles.file%" changelog.txt
  • Finally edit the artifact rules for your build to include the changelog.txt in your artifacts (General settings -> Artifact paths -> Add "changelog.txt").
leviathanbadger
  • 1,682
  • 15
  • 23
peter_raven
  • 1,654
  • 14
  • 19
  • +1 This is a good start for me. But the changelog.txt file lacks the comments associated with each file listed. Do you know if there is a way to get the comments included as well? – slolife Jan 30 '14 at 08:45
  • 1
    Don't think so. We had the same requirement and ended up making a console app that accessed TC's rest api to get the commit comments. Running this console app was then added as the final build step. – peter_raven Jan 30 '14 at 12:46
  • Any chance you can share that code? Or explain what data/parameters you got from TC to call the console app? – slolife Jan 30 '14 at 16:23
13

You can generate a change log via the REST API of TeamCity. A PowerShell script for this can be found here

For TeamCity v10.x & above:

<#
.SYNOPSIS
    Generates a project change log file.
.LINK
    Script posted over:
    http://open.bekk.no/generating-a-project-change-log-with-teamcity-and-powershell
    Also See https://stackoverflow.com/questions/4317409/create-changelog-artifact-in-teamcity

#>

# Where the changelog file will be created
$outputFile = "%system.teamcity.build.tempDir%\releasenotesfile_%teamcity.build.id%.txt"
# the url of teamcity server
$teamcityUrl = "%teamcity.serverUrl%"
# username/password to access Teamcity REST API
$authToken=[Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("%system.teamcity.auth.userId%:%system.teamcity.auth.password%"))
# Build id for the release notes
$buildId = %teamcity.build.id%

# Get the commit messages for the specified change id
# Ignore messages containing #ignore
# Ignore empty lines
Function GetCommitMessages($changeid)
{
    $request = [System.Net.WebRequest]::Create("$teamcityUrl/httpAuth/app/rest/changes/id:$changeid")     
    $request.Headers.Add("AUTHORIZATION", "Basic $authToken");
    $xml = [xml](new-object System.IO.StreamReader $request.GetResponse().GetResponseStream()).ReadToEnd()    
    Microsoft.PowerShell.Utility\Select-Xml $xml -XPath "/change" |
        where { ($_.Node["comment"].InnerText.Length -ne 0) -and (-Not $_.Node["comment"].InnerText.Contains('#ignore'))} |
        foreach {"+ $($_.Node["user"].name) : $($_.Node["comment"].InnerText.Trim().Replace("`n"," "))`n"}
}

# Grab all the changes
$request = [System.Net.WebRequest]::Create("$teamcityUrl/httpAuth/app/rest/changes?build=id:$($buildId)")
$request.Headers.Add("AUTHORIZATION", "Basic $authToken");
$xml = [xml](new-object System.IO.StreamReader $request.GetResponse().GetResponseStream()).ReadToEnd()

# Then get all commit messages for each of them
$changelog = Microsoft.PowerShell.Utility\Select-Xml $xml -XPath "/changes/change" | Foreach {GetCommitMessages($_.Node.id)}
$changelog > $outputFile
Write-Host "Changelog saved to ${outputFile}:"
$changelog

For versions before Teamcity v10.x:

<#
.SYNOPSIS
    Generates a project change log file.
.LINK
    Script posted over:
    http://open.bekk.no/generating-a-project-change-log-with-teamcity-and-powershell
    Also See https://stackoverflow.com/questions/4317409/create-changelog-artifact-in-teamcity
#>

# Where the changelog file will be created
$outputFile = "%system.teamcity.build.tempDir%\releasenotesfile_%teamcity.build.id%.txt"
# the url of teamcity server
$teamcityUrl = "%teamcity.serverUrl%"
# username/password to access Teamcity REST API
$authToken=[Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("%system.teamcity.auth.userId%:%system.teamcity.auth.password%"))
# Build id for the release notes
$buildId = %teamcity.build.id%

# Get the commit messages for the specified change id
# Ignore messages containing #ignore
# Ignore empty lines
Function GetCommitMessages($changeid)
{
    $request = [System.Net.WebRequest]::Create("$teamcityUrl/httpAuth/app/rest/changes/id:$changeid")     
    $request.Headers.Add("AUTHORIZATION", "$authToken");
    $xml = [xml](new-object System.IO.StreamReader $request.GetResponse().GetResponseStream()).ReadToEnd()    
    Microsoft.PowerShell.Utility\Select-Xml $xml -XPath "/change" |
        where { ($_.Node["comment"].InnerText.Length -ne 0) -and (-Not $_.Node["comment"].InnerText.Contains('#ignore'))} |
        foreach {"+ $($_.Node["user"].name) : $($_.Node["comment"].InnerText.Trim().Replace("`n"," "))`n"}
}

# Grab all the changes
$request = [System.Net.WebRequest]::Create("$teamcityUrl/httpAuth/app/rest/changes?build=id:$($buildId)")
$request.Headers.Add("AUTHORIZATION", "$authToken");
$xml = [xml](new-object System.IO.StreamReader $request.GetResponse().GetResponseStream()).ReadToEnd()

# Then get all commit messages for each of them
$changelog = Microsoft.PowerShell.Utility\Select-Xml $xml -XPath "/changes/change" | Foreach {GetCommitMessages($_.Node.id)}
$changelog > $outputFile
Write-Host "Changelog saved to ${outputFile}:"
$changelog
Jono
  • 3,949
  • 4
  • 28
  • 48
Daniel Rose
  • 17,233
  • 9
  • 65
  • 88
0

The above script works, however it only includes check in comments of the current build. So I've slightly amended this script so that it does include all changes since the last successful build. In Teamcity it's tricky to get the last successful build number so that's why I just get the last 10 builds and then iterate over these changes until I've found the last successful build.

<#
.SYNOPSIS
Generates a project change log file.
.LINK
Script posted over:
http://open.bekk.no/generating-a-project-change-log-with-teamcity-and-powershell
#>

# Where the changelog file will be created
$outputFile = 
"%system.teamcity.build.tempDir%\releasenotesfile_%teamcity.build.id%.txt"
# Get the commit messages for the specified change id
# Ignore messages containing #ignore
# Ignore empty lines

# the url of teamcity server
$teamcityUrl = "%teamcity.serverUrl%"

# username/password to access Teamcity REST API
$authToken=[Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("%system.teamcity.auth.userId%:%system.teamcity.auth.password%"))
# Build id for the release notes
$buildId = %teamcity.build.id%
#unique id of the project
$buildType = "%system.teamcity.buildType.id%"
$changelog =""

Function GetCommitMessages($changeid)
{   
    $request = [System.Net.WebRequest]::Create("$teamcityUrl/httpAuth/app/rest/changes/id:$changeid")     
    $request.Headers.Add("AUTHORIZATION", $authToken);
    $xml = [xml](new-object System.IO.StreamReader $request.GetResponse().GetResponseStream()).ReadToEnd()    
    Microsoft.PowerShell.Utility\Select-Xml $xml -XPath "/change" |
    where { ($_.Node["comment"].InnerText.Length -ne 0) -and (-Not $_.Node["comment"].InnerText.Contains('#ignore'))} |
    foreach {"+ $($_.Node["user"].name) : $($_.Node["comment"].InnerText.Trim().Replace("`n"," "))`n"}
}

# Grab the previous 10 builds together with their changes
$request = [System.Net.WebRequest]::Create($teamcityUrl +'/httpAuth/app/rest/builds? 
locator=untilBuild:(id:'+$buildId +'),count:10,running:any,buildType: 
(id:'+$buildType+')&fields=$long,build(id,number,status,changes($long))')
$request.Headers.Add("AUTHORIZATION", $authToken);
$xml = [xml](new-object System.IO.StreamReader 
$request.GetResponse().GetResponseStream()).ReadToEnd()

# Then get all commit messages for each of them
Foreach($x in Microsoft.PowerShell.Utility\Select-Xml $xml -XPath 
"/builds/build/changes/change") 
{    
#we collect the changes until we've found the previous successfull build. so we must always collect the changes of the current build and then stop once we find a succesful build
if($x.Node.ParentNode.ParentNode.status -eq "SUCCESS" -and $x.Node.ParentNode.ParentNode.id -ne $buildId)
  { break;}
   $changelog +=GetCommitMessages($x.Node.id)  
}

$changelog > $outputFile
Write-Host "Changelog saved to ${outputFile}:"
$changelog
Nico Timmerman
  • 1,427
  • 12
  • 12
-1

Possible you should use Service Messages

Alexander V. Ilyin
  • 2,440
  • 1
  • 16
  • 11