Building feature branches on VSTS automatically

Currently VSTS lacks(see edit below) really handy feature, which lets you trigger a build based on a branch wildcard. For instance, I'd like to run my build definition only for refs/heads/feature/* branches. While it's super-easy when using e.g. TeamCity, with VSTS it requires us to prepare a smart workaround.

Service hooks

VSTS provides us a wide collection of different service hooks. They can be used to command builds, analyze logs or trigger any kind of an event. In fact the list of available services is already impressing and gets bigger - I strongly recommend you to take a look when you have a minute.

Besides integrating with third-party services like Slack, Bamboo or Trello, it allows us to use a simple Web Hook, which can send a specific event to a specific endpoint. Because we can send any event to any endpoint, the sky is the only limit - you can create a "lambda" using Azure Function(like me), a simple WebAPI or old but still reliable MVC application using technology stack of your choice. Whatever solution you'll create, it will work as long as it can process HTTP requests.

Kick it back!

Retrieving an event from VSTS is cool but gives information only. What we really need is to orchestrate VSTS to schedule a build using a specific build definition on a specific branch. Once again REST API provided by VSTS comes to the rescue:

/
POST https://{instance}/DefaultCollection/{project}/_apis/build/builds?api-version={version}

The important part of this request is its body:

/
{
  "definition": {
    "id": 25
  },
  "sourceBranch": "refs/heads/master",
  "parameters": "{\"system.debug\":\"true\",\"BuildConfiguration\":\"debug\",\"BuildPlatform\":\"x64\"}"
}

There're two things which are required to make it work: build definition ID and source branch. You can obtain the former from the URL when you go directly to the definition you choose, once the latter is being passed along with the whole request in data.resource.refUpdates[0].name field.

Code example

An example of a Azure Function which can handle the functionality:

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info("C# HTTP trigger function processed a request.");

    // Get request body
    dynamic data = await req.Content.ReadAsAsync<object>();

    if(data.eventType == "git.push" && data.resource.refUpdates[0].name.Value.Contains("refs/heads/feature")) {
        using(var client = new HttpClient()) {
            var branch = data.resource.refUpdates[0].name.Value;
            log.Info($"Build will be scheduled for {branch} branch.");

            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", "yourToken");
            await client.PostAsync("https://{yourInstance}.visualstudio.com/DefaultCollection/{projectId}/_apis/build/builds?api-version=2.0", 
                new StringContent($"{{\"definition\": {{\"id\": {definitionId}},\"sourceBranch\": \"{branch}\"", Encoding.UTF8, "application/json"));

            log.Info($"Build scheduled successfully!.");
            return req.CreateResponse(HttpStatusCode.OK, "Build scheduled");
        }
    }

    return req.CreateResponse(HttpStatusCode.BadRequest, "Error occured");
}

Combining it all

To summarize steps needed here:

  1. Go to Service Hooks tab in VSTS and create new Web Hook pointing to your service endpoint using Code pushed as event
  2. Create a service, which will handle requests sent by VSTS and call its API to schedule a build
  3. Enjoy builds including only filtered branches

Edit:

After this was pointed I checked recently updated docs and it seems wildcard are now officially supported! You can treat this post as an inspiration for building new features around VSTS. You can find new documentation here -> https://www.visualstudio.com/en-us/docs/build/define/triggers

Accessing build and release logs in VSTS

VSTS becomes somehow better and better with each release, yet some activities are not so trivial to perform. Don't get me wrong - I'm not talking about hard-to-manage workarounds, rather subtle tricks just to make the job done.

Finding logs

It's fairly easy to download all build/release logs from the GUI - just go to the Logs tab and you will see big Downloads all logs as zip button. But what if I'd like to incorporate them in my build or release process? Unfortunately VSTS doesn't have an artifact source like e.g. TeamCity has, where you can access all information about a build. Fortunately it has a REST API, which happens to be quite helpful there.

REST for the rescue!

When you go to this site, you'll find an overview for the whole API, which VSTS shares. There are two categories - Build and Release - which are the most interesting for us now. Let's look at signatures, which are the most interesting for us:

Get build details
/
GET https://{instance}/DefaultCollection/{project}/_apis/build/builds/{buildId}/timeline?api-version={version}
Get release details
/
GET https://{account}.vsrm.visualstudio.com/defaultcollection/{project}/_apis/release/releases/{releaseId}?api-version={version}

To perform those requests, Authorization header is required containing your personal token. You can obtain it by going to https://{your_instance}.visualstudio.com/_details/security/tokens.

Accessing logs

Performing above requests will return detailed info about a build or a release. What is interesting, they will return URLs to the logs related to the specific build or release. Even more - you can access specific step - e.g. output for your custom Powershell script.

Typical URLs for logs:

Build
/
https://{yourInstance}.visualstudio.com/DefaultCollection/{projectId}/_apis/build/builds/{buildId}/logs/{stepId}
Release
/
https://{yourInstance}.vsrm.visualstudio.com/{projectId}/_apis/Release/releases/{releaseId}/environments/{environmentId}/tasks/{taskId}/logs?releaseDeployPhaseId={phaseId}

Summary

Although still not without a little overhead, above solution should help you when logs produced by a build or a release are needed. URLs presented may change or differ a little, but remember, that you can get them by requesting build/release info as presented.