使用 PowerShell 生成 TFS 更改清单以交付构建工件





5.00/5 (1投票)
使用 PowerShell 2.0 通过生成文件更改清单来辅助发布构建组件的交付。
引言
在许多企业软件开发环境中,将已准备好发布的代码交付给运营或发布团队进行部署,而不是直接部署代码,是很常见的做法。开发人员使用像 Hudson、Jenkins、CruiseControl、TeamCity 或 Bamboo 这样的构建自动化系统来启动项目构建。结果是一组作为发布周期一部分交付和部署的 构建组件。构建组件是可部署代码和其他文件的逻辑集合,它们构成应用程序。组件通常按类型进行区分,例如数据库、Web 代码、服务、配置文件等。每种类型的组件可能需要不同的部署方法。
交付组件进行部署有两种方法。一些组织将每次构建的所有组件都交付进行部署。或者,其他组织遵循部分交付和发布模型,仅交付自上次交付以来发生更改的组件。整个应用程序不会重新部署,只部署发生更改的部分。许多人认为这是一种更快速、更安全但软件发布方法。
部分交付的挑战在于准确了解自上次交付以来发生了哪些更改。几乎所有的源代码控制系统都会保留更改历史记录(‘变更集’)。基于上次构建的时间,开发人员可以检查历史记录并根据更改决定交付哪些组件。如果您有每日发布,交付之间的更改可能很少。但是,如果您的开发周期跨越几周,或者有多位开发人员在同一项目上工作,那么很可能会有许多变更集需要检查。找出要交付的组件是乏味且容易出错的。在数百次更改中遗漏一个小的更改就可能危及整个发布。由于我本人每几周都要执行这项繁琐的任务,所以我急于自动化这个过程!
解决方案当然是 PowerShell 和 Microsoft Team Foundation PowerShell Snap-In。使用这两个工具,我能够编写一个非常简单的脚本来为我完成这项工作。如果您不熟悉 Team Foundation Server (TFS) Snap-in,请参阅我之前的帖子 使用 PowerShell 自动化 Team Foundation Server 中的任务创建。该帖子讨论了 Snap-in 并解释了如何在您的 Windows 计算机上安装它。
使用代码
PowerShell 脚本以一系列变量开头。前两个变量基于您特定的 TFS 环境。变量包括:
- Team Project Collection 路径;
- 在集合中搜索更改的源位置;
- 用于搜索更改的日期和时间范围;
- 包含更改文件列表的文本文件的位置;
- 脚本完成后打开文本文件的选项。
给定 Team Project Collection 路径、源位置和日期范围,脚本将返回已更改的所有文件的排序列表。确保列表是唯一的很重要。文件在开发周期中可能会多次更改。您只想知道文件是否已更改。文件更改的次数或更改时间无关紧要。文件列表将保存到文本文件中,即清单,供查阅。脚本变量的值也包含在清单中。
在测试初始脚本时,我发现它返回的信息过多。主要有三个原因:
- 不相关的更改 – 并非所选位置中更改的每个文件都直接与要部署的项目相关。该位置的子目录(子节点)中可能存在多个相关项目。
- 辅助项目文件 – 并非所有更改的文件都会被部署。例如,构建定义文件、数据库发布配置文件和手动测试文档是任何项目的重要组成部分,但并非要部署的项目中的应用程序的直接组成部分。这些通常是构建系统使用的项目中的文件,或者 TFS 所必需的文件。
- 某些更改类型 – TFS 中的更改包括几种您可能不想包含在列表中的类型(
Microsoft.TeamFoundation.VersionControl.Client.ChangeType
)。例如,您可能不关心已删除或重命名的文件。有关如何使用 PowerShell 获取所有ChangeTypes
列表的帖子,请参阅脚本。
为了解决信息过多的问题,我们可以使用 `Where-Object` 命令和 `Select-Object` 命令,在 `Get-TfsItemHistory` 命令 管道 中过滤 `Get-TfsItemHistory` 命令的结果。使用 `Where-Object` 命令的 `-notlike` 属性(接受通配符),我们可以排除某些 ChangeTypes,排除按名称和大小显示的文件,以及排除基于文件路径的文件组。显然,您需要更改示例中的排除项以满足您自己的项目需求。
以下是 PowerShell 脚本,以及文件更改清单文本文件的一些示例内容,这些内容基于我之前的 SSDT 数据库解决方案帖子。
###############################################################
#
# Search for all unique file changes in TFS
# for a given date/time range and collection location.
# Write results to a manifest file.
#
# Author: Gary A. Stafford
# Created: 2012-04-18
# Revised: 2012-08-11
#
###############################################################
# Clear Output Pane
clear
# Enforce coding rules
Set-StrictMode -version 2.0
# Loads Windows PowerShell snap-in if not already loaded
if ( (Get-PSSnapin -Name Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{
Add-PSSnapin Microsoft.TeamFoundation.PowerShell
}
# Variables - CHECK EACH TIME
[string] $tfsCollectionPath = "http://tfs2010/tfsCollection"
[string] $locationToSearch = "$/Development/AdventureWorks/"
[string] $outputFile = "c:\ChangesToTFS.txt"
[string] $dateRange = "D2012-07-08 00:00:00Z~"
[bool] $openOutputFile = $true # Accepts $false or $true
# For a date/time range: 'D2012-08-06 00:00:00Z~D2012-08-09 23:59:59Z'
# For everything including and after a date/time: 'D2012-07-21 00:00:00Z~'
[Microsoft.TeamFoundation.Client.TfsTeamProjectCollection] $tfs = get-tfsserver $tfsCollectionPath
# Add informational header to file manifest
[string] $outputHeader =
"Team Collection: " + $tfsCollectionPath + "`r`n" +
"Source Location: " + $locationToSearch + "`r`n" +
"Date Range: " + $dateRange + "`r`n" +
"Created: " + (Get-Date).ToString() + "`r`n" +
"======================================================================"
$outputHeader | Out-File $outputFile
Get-TfsItemHistory $locationToSearch -Server $tfs -Version $dateRange `
-Recurse -IncludeItems |
Select-Object -Expand "Changes" |
Where-Object { $_.ChangeType -notlike '*Delete*'} |
Where-Object { $_.ChangeType -notlike '*Rename*'} |
Select-Object -Expand "Item" |
Where-Object { $_.ContentLength -gt 0} |
Where-Object { $_.ServerItem -notlike '*/sql/*' } |
Where-Object { $_.ServerItem -notlike '*/documentation/*' } |
Where-Object { $_.ServerItem -notlike '*/buildtargets/*' } |
Where-Object { $_.ServerItem -notlike 'build.xml'} |
Where-Object { $_.ServerItem -notlike '*.proj'} |
Where-Object { $_.ServerItem -notlike '*.publish.xml'} |
Select -Unique ServerItem | Sort ServerItem |
Format-Table -Property * -AutoSize | Out-String -Width 4096 |
Out-File $outputFile -append
Write-Host `n`r**** Script complete and file written ****
If ($openOutputFile) { Invoke-Item $outputFile }
文件更改清单文本文件的内容,基于我之前的 SSDT 数据库 Visual Studio 解决方案帖子。
Team Collection: http://tfs2010/tfsCollection
Source Location: $/Development/AdventureWorks2008/
Date Range: D2012-08-02 00:00:00Z~
Created: 8/10/2012 10:28:46 AM
======================================================================
ServerItem
----------
$/Development/AdventureWorks2008/AdventureWorks2008.sln $/Development/AdventureWorks2008/Development/Development.sln
$/Development/AdventureWorks2008/Development/Development.sqlproj
$/Development/AdventureWorks2008/Development/Schema Objects/ServerLevelObjects/Security/Logins/aw_dev.login.sql
$/Development/AdventureWorks2008/AdventureWorksSSDT/AdventureWorksSSDT.sqlproj
$/Development/AdventureWorks2008/AdventureWorksSSDT/dbo/StoredProcedures/uspGetBillOfMaterials.sql
$/Development/AdventureWorks2008/AdventureWorksSSDT/dbo/Stored Procedures/uspLogError.sql
$/Development/AdventureWorks2008/AdventureWorksSSDT/HumanResources/Tables/EmployeePayHistory.sql
$/Development/AdventureWorks2008/AdventureWorksSSDT/Purchasing/Tables/ShipMethod.sql
$/Development/AdventureWorks2008/AdventureWorksSSDT/Purchasing/Views/vVendorWithContacts.sql
$/Development/AdventureWorks2008/AdventureWorksSSDT/Security/aw_dev.sql
$/Development/AdventureWorks2008/AdventureWorksSSDT/Security/jenkins.sql
结论
此脚本节省了大量时间,尤其是在较长的发布周期中,并消除了因遗漏更改而可能产生的错误。为了进一步扩展此脚本,我想让它根据更改的文件确定要交付哪些组件,而不是让开发人员自行判断。进一步而言,我还希望它生成一个传递给构建的组件清单。构建将使用该清单将这些组件交付给发布团队。这将真正使其成为一个端到端的解决方案。挑战已接受……
脚本后,PowerShell 枚举
假设您在网上找不到列出所有 ChangeType
值的资源?您如何使用 PowerShell 获取所有已枚举的 ChangeType
值(Microsoft.TeamFoundation.VersionControl.Client.ChangeType
)的列表?一旦加载了 TFS 插件和程序集,这只需要一行代码。
if ( (Get-PSSnapin -Name Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{
Add-PSSnapin Microsoft.TeamFoundation.PowerShell
}
[Void][Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.VersionControl.Client")
[Enum]::GetNames( [Microsoft.TeamFoundation.VersionControl.Client.ChangeType] )
历史
v1.0:2012-08-10 - 脚本的原始版本。