private async Task SyncTeamWithCodeOwnerFile(BuildDefinition pipeline, WebApiTeam team, GitHubToAADConverter gitHubToAADConverter, GitHubService gitHubService, bool persistChanges) { using (logger.BeginScope("Team Name = {0}", team.Name)) { if (pipeline.Process.Type != PipelineYamlProcessType) { return; } // Get contents of CODEOWNERS logger.LogInformation("Fetching CODEOWNERS file"); Uri repoUrl = pipeline.Repository.Url; if (repoUrl != null) { repoUrl = new Uri(Regex.Replace(repoUrl.ToString(), @"\.git$", String.Empty)); } else { logger.LogError("No repository url returned from pipeline. Repo id: {0}", pipeline.Repository.Id); return; } var codeOwnerEntries = await gitHubService.GetCodeownersFile(repoUrl); if (codeOwnerEntries == default) { logger.LogInformation("CODEOWNERS file not found, skipping sync"); return; } var process = pipeline.Process as YamlProcess; logger.LogInformation("Searching CODEOWNERS for matching path for {0}", process.YamlFilename); var codeOwnerEntry = CodeOwnersFile.FindOwnersForClosestMatch(codeOwnerEntries, process.YamlFilename); codeOwnerEntry.FilterOutNonUserAliases(); logger.LogInformation("Matching Contacts Path = {0}, NumContacts = {1}", process.YamlFilename, codeOwnerEntry.Owners.Count); // Get set of team members in the CODEOWNERS file var codeownersDescriptors = new List <String>(); foreach (var contact in codeOwnerEntry.Owners) { if (!codeOwnerCache.ContainsKey(contact)) { // TODO: Better to have retry if no success on this call. var userPrincipal = gitHubToAADConverter.GetUserPrincipalNameFromGithub(contact); if (!string.IsNullOrEmpty(userPrincipal)) { codeOwnerCache[contact] = await service.GetDescriptorForPrincipal(userPrincipal); } else { logger.LogInformation("Cannot find the user principal for github {0}", contact); codeOwnerCache[contact] = null; } } codeownersDescriptors.Add(codeOwnerCache[contact]); } var codeownersSet = new HashSet <string>(codeownersDescriptors); // Get set of team members in the DevOps teams var teamMembers = await service.GetMembersAsync(team); var teamDescriptors = new List <String>(); foreach (var member in teamMembers) { if (!teamMemberCache.ContainsKey(member.Identity.Id)) { var teamMemberDescriptor = (await service.GetUserFromId(new Guid(member.Identity.Id))).SubjectDescriptor.ToString(); teamMemberCache[member.Identity.Id] = teamMemberDescriptor; } teamDescriptors.Add(teamMemberCache[member.Identity.Id]); } var teamSet = new HashSet <string>(teamDescriptors); var contactsToRemove = teamSet.Except(codeownersSet); var contactsToAdd = codeownersSet.Except(teamSet); foreach (var descriptor in contactsToRemove) { if (persistChanges && descriptor != null) { var teamDescriptor = await service.GetDescriptorAsync(team.Id); logger.LogInformation("Delete Contact TeamDescriptor = {0}, ContactDescriptor = {1}", teamDescriptor, descriptor); await service.RemoveMember(teamDescriptor, descriptor); } } foreach (var descriptor in contactsToAdd) { if (persistChanges && descriptor != null) { var teamDescriptor = await service.GetDescriptorAsync(team.Id); logger.LogInformation("Add Contact TeamDescriptor = {0}, ContactDescriptor = {1}", teamDescriptor, descriptor); await service.AddToTeamAsync(teamDescriptor, descriptor); } } } }