3

I'm trying to export and import build definitions from tfs2013, following this link and this, but none of them are working for me. I know there are some plugins for tfs which do this, but I can't use any of them due to company policies restrictions.

GetDefinitionAsync and GetFullDefinitionsAsync methods are fine, but they don't retrieve ProcessParameters property, so I can't export a full build definition which includes this important information. I've tried to get this property calling IBuildServer.QueryBuildDefinitions, but when I try to create new build definition I can't because the data type is Microsoft.TeamFoundation.Build.Client.IBuildDefinition and I can't create new instance of Microsoft.TeamFoundation.Build.Client.BuildDefinition due to it's a sealed class. how can I copy ProcessParameters and the other info to import the full build definition?

Thanks

Community
  • 1
  • 1
LeonardoX
  • 1,113
  • 14
  • 31
  • Hi, LeonardoX any update on this issue? Did my reply helped or gave a right direction? – PatrickLu-MSFT Jul 17 '17 at 02:40
  • 1
    Hi Patrick, not really indeed. As I said, I want to export/import build definitions. This means I can't use your code to copy from an existing one, because the parameters and all info I need comes from json deserialized files, but I found the way to do so and I will post it asap. Anyway thank you for your time! – LeonardoX Jul 17 '17 at 11:13
  • Thanks for the sharing. Always better when you fixed yourself, as you understood how it works completely. You could put your solution here and mark it, which will also help others in the community. – PatrickLu-MSFT Jul 17 '17 at 14:22

2 Answers2

2

Ok, finally after a deep research I found the way to export/import

Export to JSon:

    public static void ExportBuildDef(Microsoft.TeamFoundation.Build.Client.IBuildDefinition buildDefinition, string project, string filePath)
    {
        Console.WriteLine($"Exporting build definition '{buildDefinition.Name}' from '{project}' project.");

        var json = JsonConvert.SerializeObject(
            buildDefinition, 
            Newtonsoft.Json.Formatting.Indented, 
            new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });

        json = removePasswords(json);

        File.WriteAllText(filePath, json);
        Console.WriteLine($"Build definition '{buildDefinition.Name}' succesfully exported to '{filePath}'.");
    }

    private static string removePasswords(string json)
    {
        string res = json;

        var searchFor = "\"Password\":";
        var startIndex = json.IndexOf(searchFor);
        if (startIndex >= 0)
        {
            var endIndex = json.IndexOf(",", startIndex);
            var pwd = json.Substring(startIndex, endIndex - startIndex);
            if (pwd.IndexOf(":") > 0)
            {
                pwd = json.Substring(startIndex, endIndex - startIndex).Split(':')[1].Trim();
                res = json.Replace(pwd, "\"{hidden}\"");
            }

        }

        return res;
    }

Import from JSon

Note: BuildDefinitionModel class is a set of "POCO" classes (not included long file) which can be obtained converting to C# classes from the exported JSon file from here

public static void ImportBuildDefinition(IBuildServer buildServer, string projectName, string filePath, string newBuildName, string sourceProvider)
    {
        if (!File.Exists(filePath))
            throw new FileNotFoundException("File does not exist!", filePath);

        Console.WriteLine($"Importing build definition from file '{filePath}' to '{projectName}' project.");
        var json = File.ReadAllText(filePath);
        var buildDef = JsonConvert.DeserializeObject<BuildDefinitionModel>(json);

        var newBuildDefinition = CloneBuildDefinition(buildServer, buildDef, newBuildName, projectName, sourceProvider);

        Console.WriteLine($"Build definition '{buildDef.Name}' succesfully imported to '{projectName}' project.");
    }

    static IBuildDefinition CloneBuildDefinition(IBuildServer buildServer, BuildDefinitionModel buildDefinitionSource, string newBuildName, string projectName, string sourceProvider)
    {
        var buildDefinitionClone = buildServer.CreateBuildDefinition(projectName);

        buildDefinitionClone.BuildController = GetBuildController(buildServer, "");
        buildDefinitionClone.Process = buildServer.QueryProcessTemplates(buildDefinitionSource.TeamProject).FirstOrDefault(c=> c.Id == buildDefinitionSource.Process.Id);
        buildDefinitionClone.ProcessParameters = buildDefinitionSource.ProcessParameters;
        buildDefinitionClone.TriggerType = buildDefinitionSource.TriggerType;
        buildDefinitionClone.ContinuousIntegrationQuietPeriod = buildDefinitionSource.ContinuousIntegrationQuietPeriod;
        buildDefinitionClone.DefaultDropLocation = buildDefinitionSource.DefaultDropLocation;
        buildDefinitionClone.Description = buildDefinitionSource.Description;
        buildDefinitionClone.QueueStatus = Microsoft.TeamFoundation.Build.Client.DefinitionQueueStatus.Enabled;
        buildDefinitionClone.BatchSize = buildDefinitionSource.BatchSize;
        buildDefinitionClone.Name = newBuildName;

        foreach (var schedule in buildDefinitionSource.Schedules)
        {
            var newSchedule = buildDefinitionClone.AddSchedule();
            newSchedule.DaysToBuild = schedule.DaysToBuild;
            newSchedule.StartTime = schedule.StartTime;
            newSchedule.TimeZone = schedule.TimeZone;
        }

        foreach (var mapping in buildDefinitionSource.Workspace.Mappings)
        {
            buildDefinitionClone.Workspace.AddMapping(
                mapping.ServerItem, mapping.LocalItem, mapping.MappingType, mapping.Depth);
        }

        buildDefinitionClone.RetentionPolicyList.Clear();

        foreach (var policy in buildDefinitionSource.RetentionPolicyList)
        {
            buildDefinitionClone.AddRetentionPolicy(
                policy.BuildReason, policy.BuildStatus, policy.NumberToKeep, policy.DeleteOptions);
        }

        //Source Provider
        IBuildDefinitionSourceProvider provider = buildDefinitionClone.CreateInitialSourceProvider(sourceProvider);
        if (sourceProvider == VersionControlType.TFGIT.ToString())
        {
            provider.Fields["RepositoryName"] = "Git";
        }
        buildDefinitionClone.SetSourceProvider(provider);


        buildDefinitionClone.Save();
        return buildDefinitionClone;
    }

    private static IBuildController GetBuildController(IBuildServer buildServer, string buildController)
    {
        if (string.IsNullOrEmpty(buildController))
            return buildServer.QueryBuildControllers(false).Where(c=> c.Status == ControllerStatus.Available).First();

        return buildServer.GetBuildController(buildController);
    }
LeonardoX
  • 1,113
  • 14
  • 31
0

For XAML build, you need to use IBuildServer.GetBuildDefinition method to get the build definition first and then do the copy.

First start by getting a reference to the build server via a call to the GetService method on the TfsTeamProjectCollection instance. Once you have the build server reference you can then load the build definition that you are making a copy of.

If you want to copy the build definitions with process parameters you should first copy the template also. Sample code sinnper

  // Copy Process parameters
  newBuildDefinition.Process = buildDefinitionToCopy.Process;
  var processParams = WorkflowHelpers.DeserializeProcessParameters(buildDefinitionToCopy.ProcessParameters);
  var copyProcessParams = WorkflowHelpers.DeserializeProcessParameters(newBuildDefinition.ProcessParameters);
 foreach (KeyValuePair<string, object> entry in processParams)
  {
      copyProcessParams.Add(entry.Key.ToString(), entry.Value);
  }
 newBuildDefinition.ProcessParameters = WorkflowHelpers.SerializeProcessParameters(copyProcessParams);

More details about how to achieve the copy through API, you could refer this blog --Copying a TFS Build Definition

PatrickLu-MSFT
  • 49,478
  • 5
  • 35
  • 62
  • Thanks for your comment Patrick, but I think maybe I didn't explain clearly. I'm not copying any definition, I'm trying to create a new one from scratch, basing on a serialized (json) copy, like an import tool, and I don't know the way to create instances of most of a build definition properties because most of them are interfaces or sealed clases. Thanks – LeonardoX Jul 05 '17 at 03:22
  • @LeonardoX The sample that Patrick provide is OK, you just need to save parameters to a json file (read each parameter in foreach scope), then you can read them from that json file and add to your new build definition. To conclude, they are similar, both of them create a new build definition, your scenario just store them in a json file. – starian chen-MSFT Jul 06 '17 at 07:55