diff --git a/tools/TCDependencyManager/Models/Triggers.cs b/tools/TCDependencyManager/Models/Triggers.cs new file mode 100644 index 0000000000..6577fb258e --- /dev/null +++ b/tools/TCDependencyManager/Models/Triggers.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace TCDependencyManager +{ + public class Triggers + { + public List Trigger { get; set; } + } + + public class Trigger + { + public string Id { get; set; } + + public string Type { get; set; } + + public Properties Properties { get; set; } + } +} diff --git a/tools/TCDependencyManager/Program.cs b/tools/TCDependencyManager/Program.cs index 2dad525fcb..7856526da7 100644 --- a/tools/TCDependencyManager/Program.cs +++ b/tools/TCDependencyManager/Program.cs @@ -8,7 +8,7 @@ namespace TCDependencyManager { class Program { - private static readonly string[] _excludedRepos = new[] { "xunit", "kruntime", "coreclr", "universe" }; + private static readonly string[] _excludedRepos = new[] { "xunit", "kruntime", "coreclr", "universe", "rolsyn" }; static int Main(string[] args) { @@ -39,7 +39,10 @@ namespace TCDependencyManager Console.WriteLine("Ensuring dependencies are consistent on TeamCity"); foreach (var repo in repos.Where(p => p.Dependencies.Any())) { - teamCity.EnsureDependencies(repo.Name, repo.Dependencies.Select(r => r.Name)); + var dependencies = repo.Dependencies + .Select(r => r.Name) + .Concat(new[] { "CoreCLR" }); + teamCity.EnsureDependencies(repo.Name, dependencies); } return 0; } diff --git a/tools/TCDependencyManager/TCDependencyManager.csproj b/tools/TCDependencyManager/TCDependencyManager.csproj index 0a0f024d71..26f65d0050 100644 --- a/tools/TCDependencyManager/TCDependencyManager.csproj +++ b/tools/TCDependencyManager/TCDependencyManager.csproj @@ -50,6 +50,7 @@ + diff --git a/tools/TCDependencyManager/TeamCityAPI.cs b/tools/TCDependencyManager/TeamCityAPI.cs index 5d89a1dae7..c9269fb57d 100644 --- a/tools/TCDependencyManager/TeamCityAPI.cs +++ b/tools/TCDependencyManager/TeamCityAPI.cs @@ -11,6 +11,7 @@ namespace TCDependencyManager { public class TeamCityAPI { + private const string TriggersEndPoint = "httpAuth/app/rest/buildTypes/{0}/triggers"; private readonly string _teamCityUrl; private readonly ICredentials _creds; @@ -20,7 +21,7 @@ namespace TCDependencyManager _creds = creds; } - public bool TryGetDependencies(string configId, out List dependencies) + public bool TryGetSnapshotDependencies(string configId, out List dependencies) { string url = String.Format("httpAuth/app/rest/buildTypes/{0}/snapshot-dependencies", configId); var client = GetClient(); @@ -39,6 +40,50 @@ namespace TCDependencyManager return true; } + public List GetTriggers(string configId) + { + string url = String.Format(TriggersEndPoint, configId); + var client = GetClient(); + var response = client.GetAsync(url).Result; + if (response.StatusCode == HttpStatusCode.NotFound) + { + // We don't have the config setup on the CI. That is ok. + return null; + } + var triggers = response.EnsureSuccessStatusCode() + .Content.ReadAsAsync() + .Result; + + return triggers.Trigger; + } + + public void AddFinishTriggers(string configId, IEnumerable finishConfigIds) + { + foreach (var finishConfigId in finishConfigIds) + { + var props = new Properties + { + Property = new List + { + new NameValuePair("afterSuccessfulBuildOnly", "true"), + new NameValuePair("dependsOn", finishConfigId) + } + }; + + var trigger = new Trigger + { + Id = "Trigger_" + finishConfigId, + Properties = props, + Type = "buildDependencyTrigger" + }; + + string url = String.Format(TriggersEndPoint, configId); + var client = GetClient(); + var response = client.PostAsync(url, GetJsonContent(trigger)).Result; + response.EnsureSuccessStatusCode(); + } + } + public void SetDependencies(string configId, IEnumerable dependencies) { foreach (var dependencyId in dependencies) @@ -70,31 +115,49 @@ namespace TCDependencyManager ProjectName = "AspNet" } }; - var serialized = JsonConvert.SerializeObject(snapshotDependency, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); - var content = new StringContent(serialized); - content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + var content = GetJsonContent(snapshotDependency); var response = client.PostAsync(url, content).Result; response.EnsureSuccessStatusCode(); } } + public void EnsureDependencies(string configId, IEnumerable dependencies) + { + List currentDependencies; + if (TryGetSnapshotDependencies(configId, out currentDependencies)) + { + dependencies = dependencies.Select(NormalizeId); + + var dependenciesToAdd = dependencies.Except(currentDependencies, StringComparer.OrdinalIgnoreCase); + + SetDependencies(configId, dependenciesToAdd); + + var currentTriggers = GetTriggers(configId) + .Where(t => t.Type.Equals("buildDependencyTrigger", StringComparison.OrdinalIgnoreCase)) + .Select(t => t.Properties.Property.First(f => f.Name.Equals("dependsOn", StringComparison.OrdinalIgnoreCase)).Value); + + var triggersToAdd = dependencies.Except(currentTriggers); + AddFinishTriggers(configId, triggersToAdd); + } + } + + private static StringContent GetJsonContent(TVal value) + { + var serialized = JsonConvert.SerializeObject(value, + new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + var content = new StringContent(serialized); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + return content; + } + private static string NormalizeId(string dependencyId) { return dependencyId.Replace(".", ""); } - public void EnsureDependencies(string configId, IEnumerable dependencies) - { - List currentDepenencies; - if (TryGetDependencies(configId, out currentDepenencies)) - { - var dependenciesToAdd = dependencies.Select(NormalizeId) - .Except(currentDepenencies, StringComparer.OrdinalIgnoreCase); - - SetDependencies(configId, dependenciesToAdd); - } - } - private HttpClient GetClient() { var handler = new HttpClientHandler