3

I'm using BuildHttpClient's .GetDefinitionAsync and .CreateDefinitionAsync to clone a VSTS Build Definition. This works fine but I'd like to create the build defs in a different folder, other than the root folder of the project. I can add a folders via the "Manage Folders" link web but how can I do this programatically?

I've tried the following:

    buildDef = JsonConvert.DeserializeObject<BuildDefinition>(json);
    buildDef.BuildNumberFormat = buildNumFormat;
    buildDef.Name = newBuildDefName;

    Uri tfsURI = new Uri(CollectionReleaseURL);
    buildDef.Url = tfsURI.ToString();
    await buildClient.CreateDefinitionAsync(buildDef, project);

Where the Collection release is the path: CollectionReleaseURL = @"http://my.tfs.server8080/tfs/DefaultCollection/Project/Product/Release";

but when I run the program it just puts the new build def in the following folder: @"http://my.tfs.server8080/tfs/DefaultCollection/Project"

How can i clone the build def to the ../Product/Release folder?

UPDATE: I'm using the following to Clone the build def but it is NOT copying the Build Steps! Anyone know why that is?

private static async Task CloneBuildDefAsync(int buildDefId, string newBuildDefName, string buildNumFormat, string sourceControlPath, string URLpath)
{
    var buildClient = createClient();
    var buildDef = (await buildClient.GetDefinitionAsync(project, buildDefId)) as BuildDefinition;

    buildDef.Project = null;

    var json = JsonConvert.SerializeObject(buildDef);
    json = json.Replace(sourceControlMainline, sourceControlPath);
    buildDef = JsonConvert.DeserializeObject<BuildDefinition>(json);

    buildDef.BuildNumberFormat = buildNumFormat;
    buildDef.Name = newBuildDefName;
    buildDef.Path = URLpath;

    await buildClient.CreateDefinitionAsync(buildDef, project);
}

Seems to copy everything but the build step, everything else is cloned and updated perfectly: enter image description here

riQQ
  • 9,878
  • 7
  • 49
  • 66
Tom
  • 162
  • 8

3 Answers3

5

You want to set the Path property to the folder you want.

An example of the clone in PowerShell:

$uri = 'https://dev.azure.com/{organization}/{project}/_apis/build/definitions/{definitionId}'

$result = Invoke-RestMethod -Method Get -Uri $uri -UseDefaultCredentials
$result.path = '\NewFolder\Location'
$result.name = "Testing"

$body = $result | ConvertTo-Json -Depth 7

Invoke-RestMethod -Method POST -uri 'https://dev.azure.com/{organization}/{project}/_apis/build/definitions?api-version=4.0' -UseDefaultCredentials -Body $body -ContentType 'application/json'

Creates a build folder structure like this:

enter image description here

Matt
  • 3,658
  • 3
  • 14
  • 27
  • Thanks Matt - I ended up using your solution. I'm using this to automate the creation of a branch in source control and cloning the Mainlines build defs to the branch, also going to create a "Create Release" build def that will call this powershell script. Works like a charm. Now I'm going to create a powershell script to create the source control branch since I've written that in c# as well. – Tom Feb 11 '19 at 17:37
4

If you use Microsoft.TeamFoundationServer.Client you may just set Path:

BuildDefinition cpBuild = BuildClient.GetDefinitionAsync(teamProjectName, 8).Result;

BuildDefinition build = new BuildDefinition();
build.Path = "New Build Folder";
build.Name = "new Build";
build.BuildNumberFormat = cpBuild.BuildNumberFormat;
build.Repository = cpBuild.Repository;
build.Process = cpBuild.Process;
var newBuild = BuildClient.CreateDefinitionAsync(build, teamProjectName).Result;
Shamrai Aleksander
  • 13,096
  • 3
  • 24
  • 31
  • When I try this, which I had tried before I get the following: 'BuildDefinition' does not contain a definition for 'Path' and no extension for 'Path'... – Tom Feb 04 '19 at 23:02
  • I use the last version of [Microsoft.TeamFoundationServer.Client](https://www.nuget.org/packages/Microsoft.TeamFoundationServer.Client/15.131.1) 15.131.1 – Shamrai Aleksander Feb 04 '19 at 23:05
  • Awesome, thanks Shamrai. Once I upgraded the .Client from 14.89.0 to 15.131.1 I was able to set the path...thanks. – Tom Feb 04 '19 at 23:59
  • I've updated my question - do you know why the build steps are not copied? – Tom Feb 08 '19 at 14:46
  • Your example works fine for me (except I do not use json replace). But I can not find the build section in my json, I see the Process section. Also Microsoft.TeamFoundation.Build.WebApi.BuildDefinition does not contain the build member. – Shamrai Aleksander Feb 10 '19 at 15:55
1

This is what I used to accomplish this using:

https://gist.github.com/HockeyJustin/c1cb4e543806c16cab6e2c2322f5d830

$thisBuildDef.Name = $Clone_Name
$thisBuildDef.path = $BuildDefURL  # Relative to the Project name; like "Release/2019"
$thisBuildDef.buildNumberFormat = $BuildNumberFormat

# Update source control path to new branch
$defAsJson = $thisBuildDef | ConvertTo-Json -Depth 100
$defAsJson = $defAsJson.Replace($sourceControlMainline, $sourceControlBranch)

$Uri = "$(TFSCollection)/$(TFSProject)/_apis/build/definitions?api-version=2.0"

$newBuildDef = Invoke-RestMethod -Uri $Uri -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method Post -Body $defAsJson -ContentType "application/json" -ErrorAction Stop
Tom
  • 162
  • 8