是否可以在构建步骤中获取 TFS 构建代理用户功能的值?

Are TFS Build Agent User Capabilities' Values Obtainable Within Build Steps?

我正在尝试在 TFS 中编写一个构建步骤,该步骤依赖于了解构建代理 nuget.exe 的存储位置(标准的 nuget-install 步骤以一种破坏参数顺序的方式处理构建执行,所以我想 运行 使用 batch/shell/ps 步骤之一自己执行 exe。

似乎使用该路径在构建代理上设置功能是有意义的,但我似乎无法在我的任何构建步骤中引用该值,而且我在 MSDN 上找不到任何有用的信息。

我希望它类似于 $(Env.MyUserCapability),但它永远不会解析为值。

是否可以在构建步骤中检索功能值?如果是这样,你是怎么做到的?如果没有,什么是可行的替代方案?

用户定义的功能只是元数据。但是你可以设置一个全局环境变量(例如 NUGET)并将其设置为 nuget.exe 的路径,当你重新启动代理时,机器范围的环境就会被发现作为能力,然后你可以使用它。

如果您正在编写自定义任务,您还可以将 nuget.exe 添加到将下载到执行代理的任务中。

更新:I made a public extension out of this.

更新:这适用于 Azure DevOps 2019。

在 TFS 2018u1 中,以下工作:

Import-Module "Microsoft.TeamFoundation.DistributedTask.Task.Common"
Import-Module "Microsoft.TeamFoundation.DistributedTask.Task.Internal"
Add-Type -Assembly "Microsoft.TeamFoundation.DistributedTask.WebApi"

$VSS = Get-VssConnection -TaskContext $distributedTaskContext
$AgentCli = $VSS.GetClient([Microsoft.TeamFoundation.DistributedTask.WebApi.TaskAgentHttpClient])

$AgentConfig = Get-Content "$Env:AGENT_HOMEDIRECTORY\.agent" -Raw | ConvertFrom-Json
$Agent = $AgentCli.GetAgentAsync($AgentConfig.PoolId, $Env:AGENT_ID, $TRUE, $FALSE, $NULL, $NULL, [System.Threading.CancellationToken]::None).GetAwaiter().GetResult()

if($Agent.UserCapabilities.MyCapability)
{
    Write-Host "Got the capability!";
} 

CancellationToken::None 结尾的一长串默认参数是为了与 Powershell 4 兼容。PS4 不支持 value-typed 方法参数的默认值,PS5

这个片段做了一些非常有问题的事情——它依赖于代理配置文件的位置和结构。这是脆弱的。问题在于 GetAgentAsync 方法同时需要池 ID 和代理 ID,而前者未暴露在环境变量中。一种稍微不那么骇人听闻的方法是检查所有池并通过代理 ID 找到正确的池:

$Pools = $AgentCli.GetAgentPoolsAsync($NULL, $NULL, $NULL, $NULL, $NULL, [System.Threading.CancellationToken]::None).GetAwaiter().GetResult()
$Demands = New-Object 'System.Collections.Generic.List[string]'
foreach($Pool in $Pools)
{
    $Agent = $AgentCli.GetAgentsAsync($Pool.ID, $Env:AGENT_NAME, $TRUE, $FALSE, $NULL, $Demands, $NULL, [System.Threading.CancellationToken]::None).Result
    if($Agent -and $Agent.Id -eq $Env:AGENT_ID)
    {
        Break
    }
}

这依赖于另一个未记录的实施细节,特别是代理 ID 是全局唯一的。这似乎一直持续到 TFS 2018,但谁知道呢。


当您使用 $distributedTaskContext 时,任务是使用人工用户身份 "Project Collection Build Service"(不使用代理服务帐户)连接回 TFS。每个集合中都有一个这样的用户,他们是不同的。为了允许集合中版本中的任务 运行 查询代理的用户功能,您需要将 Reader 角色授予用户帐户的相关池(或所有池)从该集合中调用 "Project Collection Build Service (TheCollectionName)"。

看起来有些操作还向任务标识授予池上的隐式 Reader 角色。


或者,您可以使用 Windows 凭据从头构建一个 VssConnection,并授予代理帐户 Reader 池中的角色。