5

是否可以同步排队构建?

我试过这样的事情:

代码活动:

[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 超时内完成。分配给此操作的时间可能是较长超时的一部分。(类型故障异常`1)

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

工作流程:

在此处输入图像描述

4

2 回答 2

1

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

获取构建、QueueBuild、M​​onitorBuild

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

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

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

首先是检查 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活动,如上所述。

希望这可以帮助某人。

于 2012-06-26T16:25:48.437 回答
1

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

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

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

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

于 2012-02-16T12:18:58.880 回答