TFS 2010 API: Queue builds synchronously and get the state of each queued build: “Run On Agent (waiting for build agent)”

本秂侑毒 提交于 2019-12-22 06:59:47

问题


Is it possible to queue builds synchronously?

I tried something like this:

CodeActivity:

[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
        };
    }
}

BuildResult:

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;
            }
        }
    }
}

So I am trying to wait as long as the build is completed or canceled, but this does not work, because the build agent of the sub build is waiting the whole time.

I have one master build process (runs on agent 1) which invokes 13 sub build process (all run on agent 2). And I want to wait for each sub build process so that I can abort the master build process when a sub build process fails.

Any ideas?

UPDATE:

Service 'XXX - Agent1' had an exception: Exception Message: The operation did not complete within the allotted timeout of 00:00:30. The time allotted to this operation may have been a portion of a longer timeout. (type FaultException`1)

Exception Stack Trace: at Microsoft.TeamFoundation.Build.Machine.BuildAgentService.TerminateWorkflow(TerminatingException ex)

The workflow:


回答1:


Since all you have is one additional Build Agent, I think it doesn't bring you much to employ such a complex module.

I would implement two separate Activities:
One Activity that takes as input a BuildDetail and a BuildDefinition as exits once a new build has been queued.
I would invoke this activity within a loop in the XAML. This would queue all builds in build agent #2.

The second activity would check on the Status of build agent #2 & wait the agent to become idle again.
Once it does so, I would check for each build definition that is supposed to have ran successfully in Agent #2 with something like that:
if(buildDefinition.LastGoodBuildUri != buildDefinition.LastBuildUri)

What might seem as a disadvantage with this approach, is fact that build will NOT fail/stop at the very first breaking "child" builds.
In my opinion, that is actually an advantage: If more than one fails, you 'll know immediately.




回答2:


I created a special template for kicking off builds in order. The basic template:

Get the build, QueueBuild, MonitorBuild

Get the Build is the activity that is in the default template. It is the first activity in my template and only called once in the beginning.

QueueBuild is the activity gotten from TFSExtensions at codeplex. I create a variable that holds an IQueueBuild object which is the Result of the QueueBuild activity for each build I plan to kick off. I set the Result to this variable. I called mine CurrentQueueBuild. After every QueueBuild activity kicks off a build, this variable gets updated to the current build queue by the activity.

MonitorBuild is a sequence that I made that does most of the 'syncing':

First is an activity that checks if CurrentQueueBuild is null (CurrentQueueBuild Is Nothing). If it is I throw an exception as I can't have that.

Second is a While activity (called 'While building'). Its condition is 'CurrentQueueBuild.Status = BuildStatus.InProgress'. In the body of the the While I have another sequence that contains an InvokeMethod activity (TargetObject = CurrentQueueBuild, MethodName = Refresh and I added In parameter of type QueryOptions set to QueryOptions.All). I follow the InvokeMethod with a delay activity set to wait 5 seconds.

Finally I write a build message to log the status. This obviously is optional.

So to wrap up, I have 5 builds I kick off and for each one I have a QueueBuild activity followed by a Monitor Build activity put together as described above.

Hope this helps someone.



来源:https://stackoverflow.com/questions/9308980/tfs-2010-api-queue-builds-synchronously-and-get-the-state-of-each-queued-build

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!