【问题标题】:TFS 2010 API: Queue builds synchronously and get the state of each queued build: "Run On Agent (waiting for build agent)"TFS 2010 API:队列同步构建并获取每个排队构建的状态:“在代理上运行(等待构建代理)”
【发布时间】:2012-06-27 00:25:48
【问题描述】:

是否可以同步对构建进行排队?

我尝试过这样的事情:

代码活动:

[BuildActivity(HostEnvironmentOption.Agent)]
public sealed class QueueNewBuild : CodeActivity<BuildResult>
{
    // The Team Project that the build definition belongs to.
    [RequiredArgument]
    public InArgument<IBuildDetail> BuildDetail { get; set; }

    // The build definition to queue
    [RequiredArgument]
    public InArgument<String> BuildDefinition { get; set; }

    protected override BuildResult Execute(CodeActivityContext context)
    {
        // Obtain the runtime value of the input arguments
        var buildDefinitionName = context.GetValue(BuildDefinition);
        var buildDetail = context.GetValue(BuildDetail);

        // Obtain the Team Project for the current build definition.
        var tfsProject = buildDetail.BuildDefinition.TeamProject;

        var configurationServerUri = buildDetail.BuildServer.TeamProjectCollection.Uri.ToString();

        var server = new TfsTeamProjectCollection(new Uri(configurationServerUri));
        server.EnsureAuthenticated();
        var buildServer = server.GetService<IBuildServer>();
        var buildDefinition = buildServer.GetBuildDefinition(tfsProject, buildDefinitionName);

        var queuedBuild = buildServer.QueueBuild(buildDefinition);

        var buildStatusWatcher = new BuildStatusWatcher(queuedBuild.Id);
        buildStatusWatcher.Connect(buildServer, tfsProject);

        do
        {
        } while (buildStatusWatcher.Status != QueueStatus.Completed && buildStatusWatcher.Status != QueueStatus.Canceled);

        buildStatusWatcher.Disconnect();

        return new BuildResult
        {
            WasSuccessfully = buildStatusWatcher.Build.CompilationStatus == BuildPhaseStatus.Succeeded, 
            BuildDetail = buildStatusWatcher.Build
        };
    }
}

构建结果:

public class BuildResult
{
    public bool WasSuccessfully { get; set; }
    public IBuildDetail BuildDetail { get; set; }
}

BuildStatusWatcher:

public class BuildStatusWatcher
{
    private IQueuedBuildsView _queuedBuildsView;
    private readonly int _queueBuildId;
    private QueueStatus _status;
    private IBuildDetail _build;

    public BuildStatusWatcher(int queueBuildId)
    {
        _queueBuildId = queueBuildId;
    }

    public IBuildDetail Build
    {
        get { return _build; }
    }

    public QueueStatus Status
    {
        get { return _status; }
    }

    public void Connect(IBuildServer buildServer, string tfsProject)
    {
        _queuedBuildsView = buildServer.CreateQueuedBuildsView(tfsProject);
        _queuedBuildsView.StatusChanged += QueuedBuildsViewStatusChanged;
        _queuedBuildsView.Connect(10000, null);
    }

    public void Disconnect()
    {
        _queuedBuildsView.Disconnect();
    }

    private void QueuedBuildsViewStatusChanged(object sender, StatusChangedEventArgs e)
    {
        if (e.Changed)
        {
            var queuedBuild = _queuedBuildsView.QueuedBuilds.FirstOrDefault(x => x.Id == _queueBuildId);
            if (queuedBuild != null)
            {
                _status = queuedBuild.Status;
                _build = queuedBuild.Build;
            }
        }
    }
}

所以我试图等待构建完成或取消,但这不起作用,因为子构建的构建代理一直在等待。

我有一个主构建过程(在代理 1 上运行),它调用 13 个子构建过程(全部在代理 2 上运行)。而且我想等待每个子构建过程,以便在子构建过程失败时中止主构建过程。

有什么想法吗?

更新:

服务“XXX - Agent1”出现异常:异常消息: 操作未在分配的 00:00:30 超时内完成。 分配给此操作的时间可能是 更长的超时时间。 (类型 FaultException`1)

异常堆栈跟踪:在 Microsoft.TeamFoundation.Build.Machine.BuildAgentService.TerminateWorkflow(TerminatingException 例如)

工作流程:

【问题讨论】:

  • 那么,您是说您以编程方式请求的所有构建都无法启动?或者您不能订购多个版本?
  • 它在第一个排队的子构建时挂起。 “Run On Agent(等待构建代理)。子构建的构建代理状态为:“正在执行工作流”。主构建的构建代理状态为:“就绪”
  • 我发现,代理并非每次都挂起。构建代理“挂起”零星。

标签: c# tfs build-agent


【解决方案1】:

由于您所拥有的只是一个额外的构建代理,因此我认为使用如此复杂的模块并没有给您带来太多好处。

我将实现两个单独的活动:
一个活动作为输入一个BuildDetail 和一个BuildDefinition 作为退出,一旦一个新的构建被排队。
我会在 XAML 的循环中调用这个活动。这会将构建代理#2 中的所有构建排入队列。

第二个活动将检查构建代理 #2 的状态并等待代理再次空闲。
一旦这样做,我将检查每个应该已成功运行的构建定义在代理 #2 中使用类似的东西:
if(buildDefinition.LastGoodBuildUri != buildDefinition.LastBuildUri)

这种方法可能看起来不利的是,构建不会在第一次破坏“子”构建时失败/停止。
在我看来,这实际上是一个优势:如果不止一个失败,您会立即知道。

【讨论】:

  • 我确实找到了问题。我的主构建选择了错误的构建代理。但我不知道为什么会这样,因为我将主构建定义设置为使用特定的代理。
【解决方案2】:

我创建了一个特殊的模板来按顺序开始构建。基本模板:

获取构建、QueueBuild、M​​onitorBuild

Get the Build 是默认模板中的活动。这是我模板中的第一个活动,并且在开始时只调用一次。

QueueBuild 是从 codeplex 的 TFSExtensions 获得的活动。我创建了一个包含 IQueueBuild 对象的变量,该对象是我计划启动的每个构建的 QueueBuild 活动的结果。我将结果设置为此变量。我打电话给我的 CurrentQueueBuild。在每个 QueueBuild 活动开始构建后,该变量会被活动更新为当前的构建队列。

MonitorBuild 是我制作的一个序列,它完成了大部分“同步”:

First 是一个检查 CurrentQueueBuild 是否为空(CurrentQueueBuild Is Nothing)的活动。如果是,我会抛出异常,因为我不能拥有它。

第二个是 While 活动(称为“While building”)。它的条件是'CurrentQueueBuild.Status = BuildStatus.InProgress'。在正文中,虽然我有另一个包含 InvokeMethod 活动的序列(TargetObject = CurrentQueueBuild,MethodName = Refresh 并且我添加了 QueryOptions 类型的 In 参数设置为 QueryOptions.All)。我遵循 InvokeMethod 并将延迟活动设置为等待 5 秒。

最后我写了一条构建消息来记录状态。这显然是可选的。

总结一下,我开始了 5 个构建,对于每个构建,我都有一个 QueueBuild 活动,然后是一个 Monitor Build 活动,如上所述。

希望这对某人有所帮助。

【讨论】:

  • 这不是公认的答案吗?这是一个非常简单的解决方案,可能是一个令人讨厌的问题。
猜你喜欢
  • 1970-01-01
  • 2020-01-25
  • 1970-01-01
  • 1970-01-01
  • 2017-02-05
  • 1970-01-01
  • 2017-03-21
  • 1970-01-01
  • 2011-10-28
相关资源
最近更新 更多