Sync TeamCity build configurations with Git branches
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Collections.Specialized; | |
using System.Net; | |
using System.Text; | |
using System.Xml.Linq; | |
using System.Xml.XPath; | |
using LibGit2Sharp; | |
namespace TCBuildConfigSync | |
{ | |
/// <summary> | |
/// Syncs TeamCity build configurations with Git branches. | |
/// | |
/// Uses libgit2sharp to read the list of branches from Git, the TeamCity REST API and HTTP POSTs to the TeamCity dashboard URLs. | |
/// | |
/// libgit2sharp: https://github.com/libgit2/libgit2sharp | |
/// </summary> | |
class Program | |
{ | |
private const string TeamCityUrl = "http://bob/httpAuth"; | |
static void Main(string[] args) | |
{ | |
using (var repo = new Repository(args[0])) | |
{ | |
// Collect all the TeamCity build configurations | |
Dictionary<string, string> tcBranches = new Dictionary<string, string>(); // Name to Id map | |
List<string> tcBranchesToDelete = new List<string>(); | |
using (WebClient webClient = GetWebClient()) | |
{ | |
XDocument doc = XDocument.Parse(webClient.DownloadString(TeamCityUrl + "/app/rest/projects/id:project9")); | |
foreach (XElement buildTypeEl in doc.Root.XPathSelectElements("buildTypes/buildType")) | |
{ | |
string id = buildTypeEl.Attribute("id").Value; | |
string name = buildTypeEl.Attribute("name").Value; | |
tcBranches.Add(name, id); | |
tcBranchesToDelete.Add(name); | |
} | |
} | |
// Collect all the branches in this repo | |
foreach (Branch branch in repo.Branches) | |
{ | |
// Only look at remote branches | |
if (!branch.IsRemote) continue; | |
// Only look at private branches | |
if (!branch.Name.StartsWith("origin/private/")) continue; | |
string gitBranchName = branch.Name.Substring("origin/".Length); | |
string tcConfigName = branch.Name.Substring("origin/private/".Length).Replace('/', '-'); | |
if (!tcBranches.ContainsKey(tcConfigName)) | |
{ | |
Console.ForegroundColor = ConsoleColor.Yellow; | |
Console.WriteLine("+" + tcConfigName); | |
Console.ResetColor(); | |
// Create a new configuration for this branch | |
using (WebClient webClient = GetWebClient()) | |
{ | |
NameValueCollection parameters = new NameValueCollection(); | |
parameters.Add("buildTypeName", tcConfigName); | |
parameters.Add("createFromTemplate", "Create"); | |
parameters.Add("templateId", "btTemplate6"); | |
parameters.Add("param:BRANCH_NAME", gitBranchName); | |
byte[] responseArray = webClient.UploadValues(TeamCityUrl + "/admin/action.html", "POST", parameters); | |
string response = Encoding.ASCII.GetString(responseArray); | |
Console.WriteLine(" ->" + response); | |
} | |
} | |
else | |
{ | |
Console.ForegroundColor = ConsoleColor.Green; | |
Console.WriteLine(" " + tcConfigName); | |
Console.ResetColor(); | |
// Remove branch from deletion list, there is a matching Git branch | |
tcBranchesToDelete.Remove(tcConfigName); | |
} | |
} | |
// Delete build configurations without Git branches | |
foreach (string tcConfigName in tcBranchesToDelete) | |
{ | |
Console.ForegroundColor = ConsoleColor.Red; | |
Console.WriteLine("-" + tcConfigName); | |
Console.ResetColor(); | |
// Delete this dead configuration | |
using (WebClient webClient = GetWebClient()) | |
{ | |
NameValueCollection parameters = new NameValueCollection(); | |
parameters.Add("buildTypeId", tcBranches[tcConfigName]); | |
parameters.Add("removeBuildType", "1"); | |
byte[] responseArray = webClient.UploadValues(TeamCityUrl + "/admin/action.html", "POST", parameters); | |
string response = Encoding.ASCII.GetString(responseArray); | |
Console.WriteLine(" -> " + response); | |
} | |
} | |
} | |
} | |
private static WebClient GetWebClient() | |
{ | |
WebClient webClient = new WebClient(); | |
webClient.Credentials = new NetworkCredential("user", "password"); | |
return webClient; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment