【问题标题】:Are TFS Build Agent User Capabilities' Values Obtainable Within Build Steps?TFS 构建代理用户能力的值是否可以在构建步骤中获得?
【发布时间】:2019-03-29 03:38:06
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: tfs build continuous-integration nuget build-agent


    【解决方案1】:

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

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

    【讨论】:

    • 是的,我考虑过这样做...然后我可以使用该功能来表示已设置此全局环境变量。不允许构建步骤访问那里的值似乎是一种浪费......如果你不介意,你如何访问它?
    • 包含的任务有一个嵌入的 nuget.exe,如果你正在编写一个任务(而不仅仅是一个命令步骤),你也可以这样做..
    • %GLOBAL_VAR_NAME% (facepalm) 但是你也可以把它作为一个用户变量,如果你知道构建代理运行的用户是什么。
    • 是的,当然。我只是喜欢它们全局,以便每个人都可以进入构建服务器以诊断问题..
    【解决方案2】:

    更新: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 不支持值类型方法参数的默认值,PS5 支持。

    这个 sn-p 做了一些非常有问题的事情——它依赖于代理配置文件的位置和结构。这是脆弱的。问题是GetAgentAsync方法需要pool ID和agent 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 时,任务是使用人工用户身份“项目集合构建服务”(而不是代理服务帐户)连接回 TFS。每个集合中都有一个这样的用户,他们是不同的。为了允许在集合中的版本中运行的任务向代理查询用户功能,您需要将读取者角色授予相关池(或所有池)到名为“项目集合构建服务”的用户帐户(TheCollectionName)" 来自该集合。

    看起来某些操作还将池上的隐式读者角色授予任务标识。


    或者,您可以使用 Windows 凭据从头开始构建 VssConnection,并授予代理帐户在池中的读取者角色。

    【讨论】:

    • 这是一个 Powershell 脚本任务吗?
    • 可能是 Powershell 或 Powershell++ 任务中的内联 sn-p。
    • 我把它做成了一个文件,因为内联 sn-ps 可能只有 500 个字符。但是,IPMO 一开始就抛出了一个未找到的模块。我需要在代理上安装特定的东西吗?
    • Powershell++ 任务被认为是遗留的,但没有 500 个字符的限制:) 只是说。也就是说,所有有问题的模块都应该存在于代理机器上,我没有单独安装它们。也许他们不在您的场景中?使用内联 sn-ps 他们是。
    • Powershell++ 在这里:marketplace.visualstudio.com/…
    猜你喜欢
    • 2017-01-05
    • 1970-01-01
    • 2011-12-16
    • 1970-01-01
    • 2018-04-27
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多