【问题标题】:TFS 2015 no longer adds build number to Global List upon build complete?TFS 2015 不再在构建完成后将内部版本号添加到全局列表?
【发布时间】:2015-11-11 14:32:24
【问题描述】:

在 TFS 2015 新构建系统中,是否删除了构建完成后自动将构建号添加到全局列表(构建 - 项目名称)的功能?

我是否需要编写自定义 PowerShell 任务来完成此操作?

注意:XAML 构建仍然像以前一样将内部版本号添加到全局列表中。

【问题讨论】:

    标签: powershell tfs tfsbuild tfs-2015 azure-pipelines


    【解决方案1】:

    [免责声明 - 我在新的构建系统上工作]

    工作项上的全局列表是一种可以追溯到 TFS 原始版本的机制。它在那个时代(每天夜间构建、预 CI 和 CD 敏捷性)是一种工作方式。它开始分崩离析,并且在 TFS 中没有显示为适当的关系。当时我在 WIT 工作,我们需要一个可查询的机制,这就是我们所拥有的(怪我 :)

    所以,当我们开始一个新的构建系统时,我们不想重新构建东西并重复同样的错误。我们正在尝试采用敏捷、增量的方法来构建更好的构建系统。

    在下一个 sprint (88) 中,我们开始着手在构建和工作项之间建立适当的链接,WIT 团队也在努力使它们更加一流。您将看到的第一件事是 WIT 表格上的一个链接,希望它也能成为 QU1(至少是其中的一部分)。

    我们意识到这确实会留下一些差距,但我们正在努力弥补这些差距(封闭和标签来源是另外两个),并希望以更好的方式获得更好的长期产品。

    就解决方法而言,应该可以通过 powershell 和我们的客户端实现自动化,但我们没有任何罐头可供其他人使用。

    【讨论】:

    • 这方面有更新吗?我看到问题仍然存在。谢谢!
    • @bryanmac:1.2Y 自答案以来,似乎问题仍然存在。有什么说法吗?
    • 请更新。 Sprint 88 可能会失控:)
    • 有这方面的消息吗?
    【解决方案2】:

    由于 vNext 构建系统中仍然缺少许多功能,我制作了一个 PowerShell 脚本来完成这项工作。 在不久的将来,我计划更新此脚本以支持 IntegratedIn 字段填充并将脚本转换为自定义构建任务。

    [CmdletBinding(SupportsShouldProcess=$false)]
    param()
    
    function Update-GlobalListXml
    {
        [CmdletBinding(SupportsShouldProcess=$false)]
        param(
             [xml]$globalListsDoc,
             [parameter(Mandatory=$true)][string][ValidateNotNullOrEmpty()]$glName,
             [parameter(Mandatory=$true)][string][ValidateNotNullOrEmpty()]$buildNumber
        )
    
        Write-Verbose "Checking whether '$glName' exists"
        $buildList = $globalListsDoc.GLOBALLISTS.GLOBALLIST | Where-Object { $_.name -eq $glName }
        if ($buildList -eq $null)
        {
            Write-Host "GlobalList '$glName' does not exist and will be created"
            $globalLists = $globalListsDoc.GLOBALLISTS
            if($globalLists.OuterXml -eq $null)
            {
                $newDoc = [xml]"<gl:GLOBALLISTS xmlns:gl="""http://schemas.microsoft.com/VisualStudio/2005/workitemtracking/globallists"""></gl:GLOBALLISTS>"
                $globalLists = $newDoc.GLOBALLISTS
            }
            $globalList = $globalLists.OwnerDocument.CreateElement("GLOBALLIST")
            $globalList.SetAttribute("name", $glName)
            $buildList = $globalLists.AppendChild($globalList)
        }
        if(($buildList.LISTITEM | where-object { $_.value -eq $buildNumber }) -ne $null)
        {
            throw "The LISTITEM value: '$buildNumber' already exists in the GLOBALLIST: '$glName'"
        }
    
        Write-Host "Adding '$buildNumber' as a new LISTITEM in '$glName'"
        $build = $buildList.OwnerDocument.CreateElement("LISTITEM")
        $build.SetAttribute("value", $buildNumber)
        $buildList.AppendChild($build) | out-null
    
        return $buildList.OwnerDocument
    }
    
    function Invoke-GlobalListAPI()
    {
        [CmdletBinding(SupportsShouldProcess=$false)]
        param(
            [parameter(Mandatory=$true)][Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]$wiStore,
            [parameter(Mandatory=$true,ParameterSetName="Import")][switch]$import,
            [parameter(Mandatory=$true,ParameterSetName="Import")][xml]$globalLists,
            [parameter(ParameterSetName="Export")][switch]$export
        )
    
        try {
            if($import)
            {
                $wiStore.ImportGlobalLists($globalLists.OuterXml) # Account must be explicitly in the Project Administrator Group
            }
            if($export)
            {
                return [xml]$wiStore.ExportGlobalLists()
            }
        }
        catch [Microsoft.TeamFoundation.TeamFoundationServerException] {
            Write-Error "An error has occured while exporting or importing GlobalList"
            throw $_
        }
    }
    
    function Get-WorkItemStore()
    {
        [CmdletBinding(SupportsShouldProcess=$false)]
        param(
            [parameter(Mandatory=$true)][string][ValidateNotNullOrEmpty()]$tpcUri,
            [parameter(Mandatory=$true)][string][ValidateNotNullOrEmpty()]$agentWorker
        )
    
        # Loads client API binaries from agent folder
        $clientDll = Join-Path $agentWorker "Microsoft.TeamFoundation.Client.dll"
        $wiTDll = Join-Path $agentWorker "Microsoft.TeamFoundation.WorkItemTracking.Client.dll"
        [System.Reflection.Assembly]::LoadFrom($clientDll) | Write-Verbose
        [System.Reflection.Assembly]::LoadFrom($wiTDll) | Write-Verbose
    
        try {
            Write-Host "Connecting to $tpcUri"
            $tfsTpc = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($tpcUri)
            return $tfsTpc.GetService([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore])
        }
        catch [Microsoft.TeamFoundation.TeamFoundationServerException] {
            Write-Error "An error has occured while retrieving WorkItemStore"
            throw $_
        }
    }
    
    function Get-WITDataStore64
    {
        [CmdletBinding(SupportsShouldProcess=$false)]
        param()
    
        if($env:VS140COMNTOOLS -eq $null)
        {
            throw New-Object System.InvalidOperationException "Visual Studio 2015 must be installed on the build agent" # TODO: Change it by checking agent capabilities
        }
    
        $idePath = Join-Path (Split-Path -Parent $env:VS140COMNTOOLS) "IDE"
        return Get-ChildItem -Recurse -Path $idePath -Filter "Microsoft.WITDataStore64.dll" | Select-Object -First 1 -ExpandProperty FullName
    }
    
    function Update-GlobalList
    {
        [CmdletBinding(SupportsShouldProcess=$false)]
        param()
    
        # Get environment variables
        $tpcUri = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI
        Write-Verbose "Team Project Collection Url: '$tpcUri'"
        $teamProjectName = $env:SYSTEM_TEAMPROJECT
        Write-Verbose "Team Project: '$teamProjectName'"
        $buildNumber = $env:BUILD_BUILDNUMBER
        Write-Verbose "Build Number: '$buildNumber'"
        $agentHome = $env:AGENT_HOMEDIRECTORY
        Write-Verbose "Agent home direrctory: '$agentHome'"
        $globalListName = "Builds - $teamProjectName"
        Write-Verbose "GlobalList name: '$teamProjectName'"
    
        # Copy 'Microsoft.WITDataStore64.dll' from Visual Studio directory to AgentBin directory if it does not exist
        $agentWorker = Join-Path $agentHome "agent\Worker"
        $targetPath = Join-Path $agentWorker "Microsoft.WITDataStore64.dll" # Only compatible with x64 process #TODO use constant instead
        if(-not (Test-Path $targetPath))
        {
            $wITDataStore64FilePath = Get-WITDataStore64
            Write-Host "Copying $wITDataStore64FilePath to $targetPath"
            Copy-Item $wITDataStore64FilePath $targetPath | Write-Verbose
        }
    
        $wiStore = Get-WorkItemStore -tpcUri $tpcUri -agentWorker $agentWorker
    
        # Retrive GLOBALLISTS
        $xmlDoc = Invoke-GlobalListAPI -export -wiStore $wiStore
        $gls2 = Update-GlobalListXml -globalListsDoc $xmlDoc -glName $globalListName -buildNumber $buildNumber
    
        Invoke-GlobalListAPI -import -globalLists $gls2 -wiStore $wiStore
    }
    
    Update-GlobalList
    

    这里是 Github repo 的链接,欢迎反馈 => https://github.com/GregoryOtt/UpdateWiBuildNum/blob/master/Update-GlobalList.ps1

    【讨论】:

    • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
    • 我不知道如何处理您的评论,但我已经添加了相关代码。
    • 由于引号转义不起作用,我不得不编辑 XML 行。我不得不用反引号 (`") 替换三重双引号 (""")。此外,这需要在对 TFS 具有产品管理权限的帐户下运行。
    猜你喜欢
    • 2016-03-25
    • 2016-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-23
    相关资源
    最近更新 更多