Beispiel #1
0
    public static async Task <ImmutableArray <Path> > GetPathsAsync(VssConnection connection, string branch, string build, int buildDef, string component, string jsonFile, ILogger logger)
    {
        var gitClient   = connection.GetClient <GitHttpClient>();
        var buildClient = connection.GetClient <BuildHttpClient>();

        var allRefs = await gitClient.GetTagRefsAsync(REPO);

        var nonTempRefs      = allRefs.Where(r => !r.Name.Contains("temp"));
        var correctNamedRefs = nonTempRefs.Where(r => r.Name.Contains($"{branch}/"))
                               .Where(r => r.Name.EndsWith(build))
                               .ToArray();

        if (correctNamedRefs.Length == 0)
        {
            logger.LogWarning($"No VS tags found that mateched {branch}/{build}");
        }

        var jsonFilesAtLocation = await Task.WhenAll(correctNamedRefs.Select(async tag =>
        {
            logger.LogError(tag.Url);
            var desc         = new GitVersionDescriptor();
            desc.VersionType = GitVersionType.Tag;
            desc.Version     = tag.Name.Replace("refs/tags/", "");

            var componentsStream       = await gitClient.GetItemContentAsync(PROJECT, REPO, "/.corext/Configs/components.json", versionDescriptor: desc);
            var componentsStreamReader = new System.IO.StreamReader(componentsStream, System.Text.Encoding.UTF8);

            var customStream       = await gitClient.GetItemContentAsync(PROJECT, REPO, $"/.corext/Configs/{jsonFile}.json", versionDescriptor: desc);
            var customStreamReader = new System.IO.StreamReader(customStream, System.Text.Encoding.UTF8);

            return(new[] {
                (Tag: tag, JsonSource: await componentsStreamReader.ReadToEndAsync()),
                (Tag: tag, JsonSource: await customStreamReader.ReadToEndAsync()),
            });
        public async Task TestPull()
        {
            var data = File.ReadAllText("SerialisedObjects\\vsts_req.json");

            var obj = JsonConvert.DeserializeObject <VstsRequest>(data);

            var collectionId = AppSettings.AzureDevOpsCollectionName;

            var connection = new VssConnection(new Uri(collectionId),
                                               new VssBasicCredential(string.Empty, SecretOptions.Value.PAT));

            var gitClient = connection.GetClient <GitHttpClient>();

            var repoId = obj.resource.repository.id;

            var pr = await gitClient.GetPullRequestAsync(obj.resource.repository.id, obj.resource.pullRequestId);

            var versionDesc = new GitVersionDescriptor
            {
                VersionType = GitVersionType.Branch,
                Version     = obj.resource.sourceRefName.Replace("refs/heads/", "")
            };

            var items = await gitClient.GetItemsAsync(obj.resourceContainers.project.id, obj.resource.repository.id,
                                                      "/", VersionControlRecursionType.Full, versionDescriptor : versionDesc);

            foreach (var i in items.Where(_ => !_.IsFolder))
            {
                var content = await gitClient.GetItemAsync(obj.resource.repository.id, i.Path, includeContent : true,
                                                           versionDescriptor : versionDesc);
            }
        }
Beispiel #3
0
        public void Download(string targetFilePath)
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            Console.WriteLine($"-----> Downloading file {sourceFileUrlRelativeToTheRoot} into {targetFilePath} from azure devops collection: {collectionUrl}, project: {projectName}, repo: {repoName}");

            Directory.CreateDirectory(Path.GetDirectoryName(targetFilePath));

            var credentials = new VssCredentials(new VssBasicCredential("", apiToken));

            var connection = new VssConnection(collectionUrl, credentials);

            var client = connection.GetClient <GitHttpClient>();

            var repository = client.GetRepositoryAsync(projectName, repoName, string.Empty, default).Result;

            var descriptor = new GitVersionDescriptor()
            {
                Version = "master"
            };

            using (var file = client.GetItemContentAsync(repository.Id, sourceFileUrlRelativeToTheRoot, versionDescriptor: descriptor).Result)
            {
                using (var buffer = new MemoryStream())
                {
                    file.CopyTo(buffer);
                    File.WriteAllBytes(targetFilePath, buffer.ToArray());
                }
            }
        }
Beispiel #4
0
        public override IEnumerable <PSObject> GetLiteralItem(Segment segment, Segment childSegment)
        {
            segment.GetProvider().WriteDebug("DriveItems.Projects.Git.Repos.GetLiteralItem(Segment, Segment)");
            GitHttpClient httpClient = this.GetHttpClient(segment) as GitHttpClient;

            return(this.Wrap(
                       segment,
                       () =>
            {
                GitVersionDescriptor versionDescriptor = new GitVersionDescriptor();
                versionDescriptor.Version = SegmentHelper.GetBranchName(segment);
                versionDescriptor.VersionType = GitVersionType.Branch;
                return new[] {
                    this.ConvertToChildDriveItem(
                        segment,
                        httpClient
                        .GetItemAsync(
                            project: SegmentHelper.GetProjectName(segment),
                            repositoryId: SegmentHelper.GetRepoName(segment),
                            path: Uri.UnescapeDataString(childSegment.UnescapedName),
                            versionDescriptor: versionDescriptor)
                        .Result)
                };
            }));
        }
Beispiel #5
0
        private static async Task <(string branchName, string buildNumber)> TryGetRoslynBranchAndBuildNumberForReleaseAsync(
            VisualStudioVersion release,
            GitHttpClient vsGitClient)
        {
            GitRepository vsRepository = await GetVSRepositoryAsync(vsGitClient);

            var commit = new GitVersionDescriptor {
                VersionType = GitVersionType.Commit, Version = release.CommitSha
            };

            using var componentsJsonStream = await vsGitClient.GetItemContentAsync(
                      vsRepository.Id,
                      @".corext\Configs\dotnetcodeanalysis-components.json",
                      download : true,
                      versionDescriptor : commit);

            var componentsJsonContents = await new StreamReader(componentsJsonStream).ReadToEndAsync();
            var componentsJson         = JObject.Parse(componentsJsonContents);

            var languageServicesUrlAndManifestName = (string)componentsJson["Components"]["Microsoft.CodeAnalysis.LanguageServices"]["url"];

            var parts = languageServicesUrlAndManifestName.Split(';');

            if (parts.Length != 2)
            {
                return(default);
        public static async Task <VersionsUpdater> Create(GitHttpClient gitClient, string commitId, List <string> warningMessages)
        {
            var vsRepoId = RoslynInsertionTool.VSRepoId;
            var version  = new GitVersionDescriptor {
                VersionType = GitVersionType.Commit, Version = commitId
            };
            var versionsTemplateContent = await gitClient.GetItemContentAsync(vsRepoId, VersionsTemplatePath, download : true, versionDescriptor : version);

            using var reader = new StreamReader(versionsTemplateContent);
            string versionsTemplate = reader.ReadToEnd();

            return(new VersionsUpdater(versionsTemplate, warningMessages));
        }
Beispiel #7
0
        private static GitVersionDescriptor MakeVersionDescriptor(SourceInformation searchInformation)
        {
            Logger.Trace("Entering");

            var output = new GitVersionDescriptor
            {
                VersionType = GitVersionType.Branch,
                Version     = searchInformation.GitBranch
            };

            Logger.Trace("Exiting");

            return(output);
        }
Beispiel #8
0
        public async Task <List <(string path, string contents)> > GetFilesinPR(VstsRequest req)
        {
            //var connection = new VssConnection(new Uri(_secrets.Value.VSTSCollection),
            //    new VssBasicCredential(string.Empty, _secrets.Value.PAT));

            // var c = connection.AuthenticatedIdentity;

            var gitClient = _getGitClient(req);

            var repoId    = req.resource.repository.id;
            var projectId = req.resourceContainers.project.id;
            var branchRef = req.resource.sourceRefName.Replace("refs/heads/", "");


            var versionDesc = new GitVersionDescriptor
            {
                VersionType = GitVersionType.Branch,
                Version     = branchRef
            };

            try
            {
                var items = await gitClient.GetItemsAsync(projectId, repoId,
                                                          "/", VersionControlRecursionType.Full, versionDescriptor : versionDesc);

                var result = new List <(string path, string contents)>();

                foreach (var i in items.Where(_ => !_.IsFolder))
                {
                    if (i.Path.ToLower().IndexOf("gitignore", StringComparison.Ordinal) != -1)
                    {
                        continue;
                    }
                    var content = await gitClient.GetItemAsync(repoId,
                                                               i.Path, includeContent : true, versionDescriptor : versionDesc);

                    result.Add((i.Path, content.Content.ToString()));
                }

                return(result);
            }

            catch (Exception ex)
            {
                _logService.TrackException(ex);
            }

            return(new List <(string path, string contents)>());
        }
Beispiel #9
0
        static void Main(string[] args)
        {
            var            credentials      = new VssAadCredential();
            var            messageHandler   = new VssHttpMessageHandler(credentials, new VssHttpRequestSettings());
            Uri            uri              = new Uri(@"https://microsoft.visualstudio.com/");
            GitHttpClient  gitHttpClient    = new GitHttpClient(uri, messageHandler.Credentials);
            var            Repositories     = gitHttpClient.GetRepositoriesAsync().Result;
            GitRepository  repository       = Repositories.FirstOrDefault(r => r.Name.ToLowerInvariant() == "Localization".ToLowerInvariant());
            var            gitBranchStatuss = gitHttpClient.GetBranchesAsync(repository.Id).Result;
            GitBranchStats gitBranchStatus  = gitBranchStatuss.FirstOrDefault(branch => branch.Name.ToLowerInvariant() == "master");


            var descriptor = new GitVersionDescriptor()
            {
                Version = gitBranchStatus.Name, VersionOptions = GitVersionOptions.None, VersionType = GitVersionType.Branch
            };

            //GitItem item = gitHttpClient.GetItemAsync(repositoryId: repository.Id, path: "/intl/af-za/loc/windows/lcl/aad/brokerplugin/microsoft.aad.brokerplugin.dll.lcl", scopePath: "/intl/af-za/loc/windows/lcl/aad/brokerplugin/microsoft.aad.brokerplugin.dll.lcl", recursionLevel: VersionControlRecursionType.OneLevel, includeContentMetadata: true, latestProcessedChange: true, download: true, versionDescriptor: descriptor, userState: null, cancellationToken: new CancellationToken()).Result;


            VersionControlProjectInfo vvvvvv = new VersionControlProjectInfo();



            List <GitItem> items = gitHttpClient.GetItemsAsync(repositoryId: repository.Id, scopePath: "/intl/af-za/loc/windows/lcl/aad/brokerplugin/microsoft.aad.brokerplugin.dll.lcl", recursionLevel: VersionControlRecursionType.OneLevel, includeContentMetadata: true, latestProcessedChange: true, download: true, includeLinks: false, versionDescriptor: descriptor, userState: null, cancellationToken: new CancellationToken()).Result;


            List <GitCommitRef> aaaa = gitHttpClient.GetCommitsAsync(repositoryId: repository.Id, searchCriteria: new GitQueryCommitsCriteria(), skip: null, top: null, userState: null, cancellationToken: new CancellationToken()).Result;

            GitCommitChanges gitCommitChanges = gitHttpClient.GetChangesAsync(items[0].CommitId, repositoryId: repository.Id, top: null, skip: null, userState: null, cancellationToken: new CancellationToken()).Result;



            Stream ssss = gitHttpClient.GetItemContentAsync(repositoryId: repository.Id, path: items[0].Path, recursionLevel: VersionControlRecursionType.None, includeContentMetadata: true, latestProcessedChange: true, download: true, versionDescriptor: descriptor, userState: null, cancellationToken: new CancellationToken()).Result;

            using (MemoryStream memoryStream = new MemoryStream())
            {
                ssss.CopyTo(memoryStream);

                // Use StreamReader to read MemoryStream created from byte array
                using (StreamReader streamReader = new StreamReader(new MemoryStream(memoryStream.ToArray())))
                {
                    string fileString = streamReader.ReadToEnd();
                }
            }
        }
Beispiel #10
0
        private static async Task <RoslynBuildInformation?> TryGetRoslynBuildForReleaseAsync(VisualStudioVersion release, GitHttpClient gitClient, BuildHttpClient buildClient)
        {
            var commit = new GitVersionDescriptor {
                VersionType = GitVersionType.Commit, Version = release.CommitSha
            };
            GitRepository vsRepository = await GetVSRepositoryAsync(gitClient);

            using var componentsJsonStream = await gitClient.GetItemContentAsync(
                      vsRepository.Id,
                      @".corext\Configs\dotnetcodeanalysis-components.json",
                      download : true,
                      versionDescriptor : commit);

            var fileContents   = await new StreamReader(componentsJsonStream).ReadToEndAsync();
            var componentsJson = JObject.Parse(fileContents);

            var languageServicesUrlAndManifestName = (string)componentsJson["Components"]["Microsoft.CodeAnalysis.LanguageServices"]["url"];

            var parts = languageServicesUrlAndManifestName.Split(';');

            if (parts.Length != 2)
            {
                return(null);
            }

            if (!parts[1].EndsWith(".vsman"))
            {
                return(null);
            }

            var buildNumber = new Uri(parts[0]).Segments.Last();

            var buildDefinition = (await buildClient.GetDefinitionsAsync(vsRepository.ProjectReference.Id, name: "Roslyn-Signed")).Single();
            var build           = (await buildClient.GetBuildsAsync(buildDefinition.Project.Id, definitions: new[] { buildDefinition.Id }, buildNumber: buildNumber)).SingleOrDefault();

            if (build == null)
            {
                return(null);
            }

            var buildId = buildDefinition.Name + "_" + build.BuildNumber;

            return(new RoslynBuildInformation(commitSha: build.SourceVersion, build.SourceBranch.Replace("refs/heads/", ""), buildId));
        }
Beispiel #11
0
        public List <GitCommitRef> GetCommitsReachableFromACommitAndInPath()
        {
            GitRepository        repo       = GitSampleHelpers.FindAnyRepositoryOnAnyProject(this.Context);
            string               branchName = repo.DefaultBranch;
            string               branchNameWithoutRefsHeads = branchName.Remove(0, "refs/heads/".Length);
            GitVersionDescriptor tipCommitDescriptor        = new GitVersionDescriptor()
            {
                VersionType    = GitVersionType.Branch,
                VersionOptions = GitVersionOptions.None,
                Version        = branchNameWithoutRefsHeads
            };


            return(this.Context.Connection.GetClient <GitHttpClient>()
                   .GetCommitsAsync(repo.Id, new GitQueryCommitsCriteria()
            {
                CompareVersion = tipCommitDescriptor,
                ItemVersion = m_oldestDescriptor,
                ItemPath = "/README.md",
            }).Result);
        }
Beispiel #12
0
        public override IEnumerable <PSObject> GetItems(Segment segment)
        {
            segment.GetProvider().WriteDebug("DriveItems.Projects.Git.Items.GetItems(Segment)");
            GitHttpClient httpClient = this.GetHttpClient(segment) as GitHttpClient;

            return(this.Wrap(
                       segment,
                       () =>
            {
                GitVersionDescriptor versionDescriptor = new GitVersionDescriptor();
                versionDescriptor.Version = SegmentHelper.GetBranchName(segment);
                versionDescriptor.VersionType = GitVersionType.Branch;
                return httpClient
                .GetItemsAsync(
                    project: SegmentHelper.GetProjectName(segment),
                    repositoryId: SegmentHelper.GetRepoName(segment),
                    recursionLevel: VersionControlRecursionType.Full,
                    versionDescriptor: versionDescriptor)
                .Result
                .Select(x => this.ConvertToChildDriveItem(segment, x))
                .ToArray();
            }));
        }
Beispiel #13
0
        private object GetFirstReleaseBranchByCommit(Guid repoId, GitCommitRef lastCommit, List <GitRef> releaseBranches)
        {
            foreach (var releaseBranch in releaseBranches)
            {
                var newBranchVersionDescriptor = new GitVersionDescriptor()
                {
                    VersionType = GitVersionType.Branch, Version = AzureGitHelper.WithoutRefsAndHeadsPrefix(releaseBranch.Name)
                };
                var criteria = new GitQueryCommitsCriteria()
                {
                    Ids = new List <string>()
                    {
                        lastCommit.CommitId
                    }, Top = 1, ItemVersion = newBranchVersionDescriptor
                };

                if (gitClient.GetCommitsAsync(repoId, criteria, top: 1).Result.Any())
                {
                    return(releaseBranch);
                }
            }

            return(null);
        }
Beispiel #14
0
        public void Run()
        {
            Console.WriteLine($"Retaining builds from [{_options.BuildQueueName}] for component [{_options.ComponentName}].");

            var stopwatch = Stopwatch.StartNew();

            Console.WriteLine("Getting KeyVault secret...");
            var password = GetSecret(Settings.Default.VsoSecretName, _options).Result;

            var uri        = new Uri(Settings.Default.VSTSUrl);
            var connection = new VssConnection(uri, new WindowsCredential(new NetworkCredential(Settings.Default.UserName, password)));

            Console.WriteLine("Authenticating connection...");
            using (var gitHttpClient = connection.GetClient <GitHttpClient>())
            {
                Console.WriteLine("Finding repository...");
                var allRepos = gitHttpClient.GetRepositoriesAsync().Result;
                var vsRepos  = allRepos.Where(r => r.Name == Settings.Default.RepositoryName && r.ProjectReference.Name == Settings.Default.TFSProjectName).ToList();
                if (vsRepos.Count != 1)
                {
                    Console.WriteLine($"Expected to find one repository matching {Settings.Default.TFSProjectName}/{Settings.Default.RepositoryName} but found {vsRepos.Count}.");
                    Environment.Exit(1);
                }

                var vsRepo     = vsRepos.Single();
                var vsRepoGuid = vsRepo.Id;
                Console.WriteLine("Getting all tags...");
                var tagRefs = gitHttpClient.GetTagRefsAsync(vsRepoGuid).Result;

                var allTagData = ReadCachedTags();

                (int numSkipped, int numProcessed, int numFailed, int numCached)operationTracker = (0, 0, 0, 0);

                Console.WriteLine();
                Console.WriteLine($"Processing {tagRefs.Count} tags...");
                Parallel.ForEach(tagRefs, (tagRef) =>
                {
                    try
                    {
                        TagData tag;

                        if (allTagData.TryGetValue(tagRef.Name, out tag) && tag.ObjectId == tagRef.ObjectId)
                        {
                            Interlocked.Increment(ref operationTracker.numCached);

                            // We already have the tag and it's data. Let's recheck if it's been expired
                            if (tag.MaybeExists)
                            {
                                tag.MaybeExists = Directory.Exists(tag.RawDropPath);
                            }

                            return;
                        }

                        tag         = new TagData();
                        tag.TagName = tagRef.Name;

                        if (!Regex.IsMatch(tagRef.Name, @"refs/tags/drop/.*/official\.\d{5}.\d{2}") && !tag.IsRelease())
                        {
                            Interlocked.Increment(ref operationTracker.numSkipped);
                            return;
                        }

                        tag.ObjectId = tagRef.ObjectId;

                        var versionDescriptor = new GitVersionDescriptor()
                        {
                            Version        = tagRef.Name.Substring("refs/tags/".Length),
                            VersionOptions = GitVersionOptions.None,
                            VersionType    = GitVersionType.Tag
                        };

                        var annotatedTagDataTask = gitHttpClient.GetAnnotatedTagAsync(Settings.Default.TFSProjectName, vsRepoGuid, tagRef.ObjectId);
                        var annotatedTagData     = JObject.Parse(annotatedTagDataTask.Result.Message);

                        tag.RawDropPath = annotatedTagData["RawDropLocation"].Value <string>();
                        tag.MaybeExists = Directory.Exists(tag.RawDropPath);

                        // If the build is already gone, no reason to get the components.json for it
                        if (tag.MaybeExists)
                        {
                            if (!TryUpdateTag(gitHttpClient, vsRepoGuid, tag, versionDescriptor, @".corext\Configs\components.json") &&
                                !TryUpdateTag(gitHttpClient, vsRepoGuid, tag, versionDescriptor, @".corext\Configs\dotnetcodeanalysis-components.json") &&
                                !TryUpdateTag(gitHttpClient, vsRepoGuid, tag, versionDescriptor, @".corext\Configs\lutandsbd-components.json") &&
                                !TryUpdateTag(gitHttpClient, vsRepoGuid, tag, versionDescriptor, @".corext\Configs\dotnetprojectsystem-components.json"))
                            {
                                throw new Exception("Unable to locate component.");
                            }
                        }

                        if (!allTagData.TryAdd(tagRef.Name, tag))
                        {
                            throw new Exception("Tag already exists which shouldn't have happened.");
                        }

                        Interlocked.Increment(ref operationTracker.numProcessed);
                    }
                    catch (Exception)
                    {
                        // Catastrophic error: don't cache anything since we have no idea what happened
                        allTagData.TryRemove(tagRef.Name, out _);
                        Interlocked.Increment(ref operationTracker.numFailed);
                    }
                });

                Console.WriteLine($"    Processed {operationTracker.numProcessed + operationTracker.numSkipped + operationTracker.numFailed + operationTracker.numCached} tags. {operationTracker.numProcessed} were new, {operationTracker.numSkipped} were skipped, {operationTracker.numFailed} failed to be interpreted, and {operationTracker.numCached} where previously processed.");

                WriteCachedTags(allTagData);

                Console.WriteLine();
                Console.WriteLine("Locating unique builds...");

                // Figure out all our builds that have been inserted, and which release tags if they happen to be release tags
                var uniqueInsertedBuilds = (from tag in allTagData.Values
                                            where tag.MaybeExists
                                            group tag by new { tag.InsertedBuildBranch, tag.InsertedBuildNumber } into g
                                            select new { g.Key.InsertedBuildBranch, g.Key.InsertedBuildNumber, ReleaseTags = g.Where(t => t.IsRelease()) })
                                           .Distinct().ToList();

                Console.WriteLine($"    Found {uniqueInsertedBuilds.Count} unique builds.");

                Console.WriteLine();
                var currentBranch = string.Empty;

                foreach (var insertedBuildsByBranch in uniqueInsertedBuilds.GroupBy(b => b.InsertedBuildBranch))
                {
                    Console.WriteLine(insertedBuildsByBranch.Key);

                    foreach (var build in insertedBuildsByBranch.OrderBy(b => b.InsertedBuildNumber))
                    {
                        Console.Write($"    {build.InsertedBuildNumber}");

                        if (build.ReleaseTags.Any())
                        {
                            Console.Write($" (released to public as {string.Join(", ", build.ReleaseTags.Select(t => t.TagName).OrderBy(t => t))})");
                        }

                        Console.WriteLine();
                    }
                }

                using (var buildClient = connection.GetClient <BuildHttpClient>())
                {
                    Console.WriteLine("Finding build definition");
                    var projectId        = vsRepo.ProjectReference.Id;
                    var definitions      = buildClient.GetDefinitionsAsync(projectId).Result;
                    var buildDefinitions = definitions.Where(d => d.Name == _options.BuildQueueName && d.DefinitionQuality != DefinitionQuality.Draft).ToList();
                    if (buildDefinitions.Count != 1)
                    {
                        throw new Exception($"Expected one build definition named {_options.BuildQueueName} but found {buildDefinitions.Count}.");
                    }

                    var buildDefinitionIds = new int[]
                    {
                        buildDefinitions.Single().Id
                    };

                    var builds         = buildClient.GetBuildsAsync(projectId, buildDefinitionIds, statusFilter: BuildStatus.Completed).Result;
                    var modifiedBuilds = new HashSet <Build>();

                    Console.WriteLine();
                    Console.WriteLine("Determining which builds should be marked for retention...");
                    foreach (var build in builds)
                    {
                        var insertedBuild = uniqueInsertedBuilds.FirstOrDefault(b => NormalizeBranchName(b.InsertedBuildBranch) == NormalizeBranchName(build.SourceBranch) &&
                                                                                b.InsertedBuildNumber == build.BuildNumber);

                        if (insertedBuild != null && insertedBuild.ReleaseTags.Any() && !build.Tags.Contains(ReleasedToPublicTag))
                        {
                            buildClient.AddBuildTagAsync(projectId, build.Id, ReleasedToPublicTag).Wait();
                        }

                        if (insertedBuild != null || build.Tags.Contains(ReleasedToPublicTag))
                        {
                            if (!build.KeepForever.GetValueOrDefault())
                            {
                                Console.WriteLine($"Adding '{NormalizeBranchName(build.SourceBranch)} - {build.BuildNumber}' to the list of builds to keep forever.");
                                build.KeepForever = true;
                                modifiedBuilds.Add(build);
                            }
                        }
                        else if (build.KeepForever.GetValueOrDefault())
                        {
                            // no longer needed
                            Console.WriteLine($"Removing '{NormalizeBranchName(build.SourceBranch)} - {build.BuildNumber}' from the list of builds to keep forever.");
                            build.KeepForever = false;
                            modifiedBuilds.Add(build);
                        }
                    }
                    Console.WriteLine($"    Found {modifiedBuilds.Count} builds which need their retention setting modified.");
                    Console.WriteLine();

                    if (modifiedBuilds.Count > 0)
                    {
                        buildClient.UpdateBuildsAsync(modifiedBuilds, projectId).Wait();
                    }
                }

                stopwatch.Stop();

                Console.WriteLine();
                Console.WriteLine($"Total Time: {stopwatch.Elapsed.TotalSeconds} seconds");
            }
        }
Beispiel #15
0
        private bool TryUpdateTag(GitHttpClient gitHttpClient, Guid vsRepoGuid, TagData tag, GitVersionDescriptor versionDescriptor, string componentsJsonFile)
        {
            Stream itemContentStream;

            try
            {
                itemContentStream = gitHttpClient.GetItemContentAsync(Settings.Default.TFSProjectName, vsRepoGuid, componentsJsonFile, versionDescriptor: versionDescriptor).Result;
            }
            catch (AggregateException e) when(e.InnerException.Message.Contains("TF401174"))  // item could not be found
            {
                // That file doesn't exist in this drop;
                return(false);
            }

            using (itemContentStream)
                using (var jsonReader = new JsonTextReader(new StreamReader(itemContentStream)))
                {
                    var jsonDocument = (JObject)(JToken.ReadFrom(jsonReader));

                    var components = jsonDocument["Components"];
                    var component  = components[_options.ComponentName];

                    if (component == null)
                    {
                        return(false);
                    }

                    var componentUrl   = component["url"].Value <string>();
                    var componentParts = componentUrl.Split(';').First().Split('/');

                    tag.InsertedBuildBranch = componentParts[componentParts.Length - 2];
                    tag.InsertedBuildNumber = componentParts[componentParts.Length - 1];

                    // Just to protect against us getting something bad, assert the build number is numeric
                    if (!Regex.IsMatch(tag.InsertedBuildNumber, "^[0-9.]+$"))
                    {
                        throw new Exception($"{tag.InsertedBuildNumber} doesn't look numeric.");
                    }
                }

            return(true);
        }
        public static async Task <(bool success, int pullRequestId)> PerformInsertionAsync(
            RoslynInsertionToolOptions options,
            CancellationToken cancellationToken)
        {
            Options = options;
            Console.WriteLine($"{Environment.NewLine}New Insertion Into {Options.VisualStudioBranchName} Started{Environment.NewLine}");

            var newPackageFiles = new List <string>();

            try
            {
                Console.WriteLine($"Verifying given authentication for {Options.VSTSUri}");
                try
                {
                    ProjectCollection.Authenticate();
                }
                catch (Exception ex)
                {
                    LogError($"Could not authenticate with {Options.VSTSUri}");
                    LogError(ex);
                    return(false, 0);
                }

                Console.WriteLine($"Verification succeeded for {Options.VSTSUri}");

                // ********************** Create dummy PR *****************************
                if (Options.CreateDummyPr)
                {
                    GitPullRequest dummyPR;
                    try
                    {
                        dummyPR = await CreatePlaceholderBranchAsync(cancellationToken);
                    }
                    catch (Exception ex)
                    {
                        LogError($"Unable to create placeholder PR for '{options.VisualStudioBranchName}'");
                        LogError(ex);
                        return(false, 0);
                    }

                    if (dummyPR == null)
                    {
                        LogError($"Unable to create placeholder PR for '{options.VisualStudioBranchName}'");
                        return(false, 0);
                    }

                    return(true, dummyPR.PullRequestId);
                }

                // ********************** Get Last Insertion *****************************
                cancellationToken.ThrowIfCancellationRequested();

                BuildVersion buildVersion;

                Build buildToInsert;
                Build latestBuild = null;
                bool  retainBuild = false;

                // Get the version from DevOps Pipelines queue, e.g. Roslyn-Master-Signed-Release.
                if (string.IsNullOrEmpty(Options.SpecificBuild))
                {
                    buildToInsert = await GetLatestPassedBuildAsync(cancellationToken);

                    buildVersion = BuildVersion.FromTfsBuildNumber(buildToInsert.BuildNumber, Options.BuildQueueName);
                    Console.WriteLine("Found build number " + buildVersion);

                    //  Get the latest build, whether passed or failed.  If the buildToInsert has already been inserted but
                    //  there is a later failing build, then send an error
                    latestBuild = await GetLatestBuildAsync(cancellationToken);
                }
                else
                {
                    buildVersion  = BuildVersion.FromString(Options.SpecificBuild);
                    buildToInsert = await GetSpecificBuildAsync(buildVersion, cancellationToken);
                }

                var insertionArtifacts = await GetInsertionArtifactsAsync(buildToInsert, cancellationToken);

                cancellationToken.ThrowIfCancellationRequested();

                // *********** Look up existing PR ********************
                var gitClient = ProjectCollection.GetClient <GitHttpClient>();
                var branches  = await gitClient.GetRefsAsync(
                    VSRepoId,
                    filter : $"heads/{Options.VisualStudioBranchName}",
                    cancellationToken : cancellationToken);

                var baseBranch = branches.Single(b => b.Name == $"refs/heads/{Options.VisualStudioBranchName}");

                var pullRequestId = Options.UpdateExistingPr;
                var useExistingPr = pullRequestId != 0;

                GitPullRequest pullRequest;
                string         insertionBranchName;
                if (useExistingPr)
                {
                    pullRequest = await gitClient.GetPullRequestByIdAsync(pullRequestId, cancellationToken : cancellationToken);

                    insertionBranchName = pullRequest.SourceRefName.Substring("refs/heads/".Length);

                    var refs = await gitClient.GetRefsAsync(VSRepoId, filter : $"heads/{insertionBranchName}", cancellationToken : cancellationToken);

                    var insertionBranch = refs.Single(r => r.Name == $"refs/heads/{insertionBranchName}");

                    if (Options.OverwritePr)
                    {
                        // overwrite existing PR branch back to base before pushing new commit
                        var updateToBase = new GitRefUpdate
                        {
                            OldObjectId = insertionBranch.ObjectId,
                            NewObjectId = baseBranch.ObjectId,
                            Name        = $"refs/heads/{insertionBranchName}"
                        };
                        await gitClient.UpdateRefsAsync(new[] { updateToBase }, VSRepoId, cancellationToken : cancellationToken);
                    }
                    else
                    {
                        // not overwriting PR, so the insertion branch is actually the base
                        baseBranch = insertionBranch;
                    }
                }
                else
                {
                    pullRequest         = null;
                    insertionBranchName = GetNewBranchName();
                }

                var allChanges = new List <GitChange>();

                var coreXT = await CoreXT.Load(gitClient, baseBranch.ObjectId);

                if (Options.InsertCoreXTPackages)
                {
                    // ************** Update Nuget Packages For Branch************************
                    cancellationToken.ThrowIfCancellationRequested();
                    Console.WriteLine($"Updating Nuget Packages");
                    bool success;
                    (success, newPackageFiles) = UpdatePackages(
                        buildVersion,
                        coreXT,
                        insertionArtifacts.GetPackagesDirectory(),
                        cancellationToken);
                    retainBuild |= success;

                    // *********** Copy OptimizationInputs.props file ***********************
                    foreach (var propsFile in insertionArtifacts.GetOptProfPropertyFiles())
                    {
                        var targetFilePath = "src/Tests/config/runsettings/Official/OptProf/External/" + Path.GetFileName(propsFile);

                        var version = new GitVersionDescriptor {
                            VersionType = GitVersionType.Commit, Version = baseBranch.ObjectId
                        };
                        var stream = await gitClient.GetItemContentAsync(VSRepoId, targetFilePath, download : true, versionDescriptor : version);

                        var originalContent = new StreamReader(stream).ReadToEnd();

                        var newContent = File.ReadAllText(propsFile);

                        if (GetChangeOpt(targetFilePath, originalContent, newContent) is GitChange change)
                        {
                            allChanges.Add(change);
                        }
                    }
                }

                if (Options.UpdateCoreXTLibraries || Options.UpdateAssemblyVersions)
                {
                    // ************** Update assembly versions ************************
                    cancellationToken.ThrowIfCancellationRequested();
                    Console.WriteLine($"Updating assembly versions");
                    if (await UpdateAssemblyVersionsOpt(gitClient, baseBranch.ObjectId, insertionArtifacts) is GitChange assemblyVersionChange)
                    {
                        allChanges.Add(assemblyVersionChange);
                    }

                    // if we got this far then we definitely need to retain this build
                    retainBuild = true;
                }

                // *********** Update toolset ********************
                if (Options.InsertToolset)
                {
                    UpdateToolsetPackage(coreXT, insertionArtifacts, buildVersion);
                    retainBuild = true;
                }

                // ************ Update .corext\Configs\default.config ********************
                cancellationToken.ThrowIfCancellationRequested();
                Console.WriteLine($"Updating CoreXT default.config file");
                if (coreXT.SaveConfigOpt() is GitChange configChange)
                {
                    allChanges.Add(configChange);
                }

                // *********** Update .corext\Configs\components.json ********************

                BuildVersion oldComponentVersion = default;
                if (Options.InsertWillowPackages)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    Console.WriteLine($"Updating CoreXT components file");

                    var components = await GetLatestComponentsAsync(buildToInsert, cancellationToken);

                    var shouldSave = false;
                    foreach (var newComponent in components)
                    {
                        if (coreXT.TryGetComponentByName(newComponent.Name, out var oldComponent))
                        {
                            if (oldComponent.BuildVersion != default)
                            {
                                oldComponentVersion = oldComponent.BuildVersion;
                            }
                            coreXT.UpdateComponent(newComponent);
                            shouldSave = true;
                        }
                    }
                    if (shouldSave)
                    {
                        var allComponentChanges = coreXT.SaveComponents();
                        allChanges.AddRange(allComponentChanges);
                        retainBuild = true;
                    }
                }

                // ************* Ensure the build is retained on the servers *************
                if (Options.RetainInsertedBuild && retainBuild && !buildToInsert.KeepForever.GetValueOrDefault())
                {
                    Console.WriteLine("Marking inserted build for retention.");
                    buildToInsert.KeepForever = true;
                    var buildClient = ProjectCollection.GetClient <BuildHttpClient>();
                    await buildClient.UpdateBuildAsync(buildToInsert);
                }

                // ************* Bail out if there are no changes ************************
                if (!allChanges.Any())
                {
                    LogWarning("No meaningful changes since the last insertion was merged. PR will not be created or updated.");
                    return(true, 0);
                }

                // ********************* Create push *************************************
                var insertionBranchUpdate = new GitRefUpdate
                {
                    Name        = $"refs/heads/{insertionBranchName}",
                    OldObjectId = baseBranch.ObjectId
                };

                var commit = new GitCommitRef
                {
                    Comment = $"Updating {Options.InsertionName} to {buildVersion}",
                    Changes = allChanges
                };
                var push = new GitPush
                {
                    RefUpdates = new[] { insertionBranchUpdate },
                    Commits    = new[] { commit }
                };

                await gitClient.CreatePushAsync(push, VSRepoId, cancellationToken : cancellationToken);

                // ********************* Create pull request *****************************
                var oldBuild = await GetSpecificBuildAsync(oldComponentVersion, cancellationToken);

                var prDescriptionMarkdown = CreatePullRequestDescription(oldBuild, buildToInsert, useMarkdown: true);

                if (buildToInsert.Result == BuildResult.PartiallySucceeded)
                {
                    prDescriptionMarkdown += Environment.NewLine + ":warning: The build being inserted has partially succeeded.";
                }

                if (!useExistingPr || Options.OverwritePr)
                {
                    try
                    {
                        var nl = Environment.NewLine;
                        if (oldBuild is null)
                        {
                            prDescriptionMarkdown += $"{nl}---{nl}Unable to find details for previous build ({oldComponentVersion}){nl}";
                        }
                        else
                        {
                            var(changes, diffLink) = await GetChangesBetweenBuildsAsync(oldBuild, buildToInsert, cancellationToken);

                            var diffDescription = changes.Any()
                                ? $"[View Complete Diff of Changes]({diffLink})"
                                : "No source changes since previous insertion";

                            prDescriptionMarkdown += nl + "---" + nl + diffDescription + nl;
                            prDescriptionMarkdown  = AppendChangesToDescription(prDescriptionMarkdown, oldBuild ?? buildToInsert, changes);
                        }
                    }
                    catch (Exception e)
                    {
                        LogWarning("Failed to create diff links.");
                        LogWarning(e.Message);
                    }
                }

                if (useExistingPr)
                {
                    try
                    {
                        if (Options.OverwritePr)
                        {
                            pullRequest = await OverwritePullRequestAsync(pullRequestId, prDescriptionMarkdown, buildVersion.ToString(), options.TitlePrefix, cancellationToken);
                        }
                        pullRequestId = pullRequest.PullRequestId;
                    }
                    catch (Exception ex)
                    {
                        LogError($"Unable to update pull request for '{pullRequest.SourceRefName}'");
                        LogError(ex);
                        return(false, 0);
                    }
                }
                else
                {
                    // create a new PR
                    Console.WriteLine($"Create Pull Request");
                    try
                    {
                        // If this insertion was queued for PR validation, for a dev branch, or for a feature branch,
                        // then add the build queuer as a reviewer instead of mlinfraswat.
                        var isPrValidation       = !string.IsNullOrEmpty(GetBuildPRNumber(buildToInsert));
                        var isDevOrFeatureBranch = Options.BranchName.StartsWith("dev/") || Options.BranchName.StartsWith("features/");

                        var reviewerId = isPrValidation || isDevOrFeatureBranch
                            ? buildToInsert.RequestedBy.Id
                            : MLInfraSwatUserId.ToString();

                        pullRequest = await CreatePullRequestAsync(insertionBranchName, prDescriptionMarkdown, buildVersion.ToString(), options.TitlePrefix, reviewerId, cancellationToken);

                        if (pullRequest == null)
                        {
                            LogError($"Unable to create pull request for '{insertionBranchName}'");
                            return(false, 0);
                        }

                        pullRequestId = pullRequest.PullRequestId;
                    }
                    catch (Exception ex)
                    {
                        LogError($"Unable to create pull request for '{insertionBranchName}'");
                        LogError(ex);
                        return(false, 0);
                    }
                }

                // ********************* Create validation build *****************************
                if (Options.QueueValidationBuild)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    Console.WriteLine($"Create Validation Build");
                    try
                    {
                        if (Options.CreateDraftPr)
                        {
                            // When creating Draft PRs no policies are automatically started.
                            // If we do not queue a CloudBuild the Perf DDRITs request will
                            // spin waiting for a build to test against until it timesout.
                            await QueueBuildPolicy(pullRequest, "CloudBuild - PR");
                        }

                        await QueueBuildPolicy(pullRequest, "Request Perf DDRITs");
                    }
                    catch (Exception ex)
                    {
                        LogWarning($"Unable to create a CloudBuild validation build for '{insertionBranchName}'");
                        LogWarning(ex);
                    }

                    if (Options.CreateDraftPr)
                    {
                        // When creating Draft PRs no policies are automatically started.
                        await TryQueueBuildPolicy(pullRequest, "Insertion Hash Check", insertionBranchName);
                        await TryQueueBuildPolicy(pullRequest, "Insertion Sign Check", insertionBranchName);
                        await TryQueueBuildPolicy(pullRequest, "Insertion Symbol Check", insertionBranchName);
                    }
                }

                // ********************* Set PR to Auto-Complete *****************************
                if (Options.SetAutoComplete)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    Console.WriteLine($"Set PR to Auto-Complete");
                    try
                    {
                        var prDescriptionText = CreatePullRequestDescription(oldBuild, buildToInsert, useMarkdown: false);
                        await SetAutoCompleteAsync(pullRequest, prDescriptionText, cancellationToken);
                    }
                    catch (Exception ex)
                    {
                        LogWarning($"Unable to Set PR to Auto-Complete for '{insertionBranchName}'");
                        LogWarning(ex);
                    }
                }

                return(true, pullRequestId);
            }
            catch (Exception ex)
            {
                LogError(ex);
                return(false, 0);
            }
            finally
            {
                Options = null;
            }
        }
Beispiel #17
0
        private async Task <List <GitChange> > GetChanges(Guid repoId, IPackageHandler handler, string packageVersion, GitVersionDescriptor versionRef, IEnumerable <GitItem> items)
        {
            var changes = new List <GitChange>();

            foreach (var item in items)
            {
                var shouldUpdate = await handler.ShouldUpdate(item.Path).ConfigureAwait(false);

                if (shouldUpdate)
                {
                    var itemRef = await _gitClient.GetItemContentAsync(repoId, item.Path, includeContent : true, versionDescriptor : versionRef).ConfigureAwait(false);

                    var oldContent = await itemRef.GetContent().ConfigureAwait(false);

                    var newContent = await handler.Update(oldContent, packageVersion).ConfigureAwait(false);

                    if (!string.Equals(oldContent, newContent))
                    {
                        changes.Add(CreateChange(item.Path, newContent));
                        Log($"Item content of {item.Path} received and changed.");
                    }
                }
            }

            return(changes);
        }
Beispiel #18
0
 private static GitQueryCommitsCriteria GetBaseCommits(GitVersionDescriptor itemVersion) => new GitQueryCommitsCriteria
 {
     ItemVersion = itemVersion,
 };
        public static async Task <(bool success, int pullRequestId)> PerformInsertionAsync(
            RoslynInsertionToolOptions options,
            CancellationToken cancellationToken)
        {
            Options = options;
            Console.WriteLine($"{Environment.NewLine}New Insertion Into {Options.VisualStudioBranchName} Started{Environment.NewLine}");
            var newPackageFiles = new List <string>();

            try
            {
                Console.WriteLine($"Verifying given authentication for {Options.VisualStudioRepoAzdoUri}");
                try
                {
                    VisualStudioRepoConnection.Authenticate();
                }
                catch (Exception ex)
                {
                    LogError($"Could not authenticate with {Options.VisualStudioRepoAzdoUri}");
                    LogError(ex);
                    return(false, 0);
                }

                Console.WriteLine($"Verification succeeded for {Options.VisualStudioRepoAzdoUri}");

                if (ComponentBuildConnection != VisualStudioRepoConnection)
                {
                    Console.WriteLine($"Verifying given authentication for {Options.ComponentBuildAzdoUri}");
                    try
                    {
                        ComponentBuildConnection.Authenticate();
                    }
                    catch (Exception ex)
                    {
                        LogError($"Could not authenticate with {Options.ComponentBuildAzdoUri}");
                        LogError(ex);
                        return(false, 0);
                    }

                    Console.WriteLine($"Verification succeeded for {Options.ComponentBuildAzdoUri}");
                }

                // ********************** Create dummy PR *****************************
                if (Options.CreateDummyPr)
                {
                    GitPullRequest dummyPR;
                    try
                    {
                        dummyPR = await CreatePlaceholderVSBranchAsync(cancellationToken);
                    }
                    catch (Exception ex)
                    {
                        LogError($"Unable to create placeholder PR for '{options.VisualStudioBranchName}'");
                        LogError(ex);
                        return(false, 0);
                    }

                    if (dummyPR == null)
                    {
                        LogError($"Unable to create placeholder PR for '{options.VisualStudioBranchName}'");
                        return(false, 0);
                    }

                    return(true, dummyPR.PullRequestId);
                }

                // ********************** Get Last Insertion *****************************
                cancellationToken.ThrowIfCancellationRequested();

                BuildVersion buildVersion;

                Build buildToInsert;
                Build latestBuild = null;
                bool  retainBuild = false;

                // Get the version from DevOps Pipelines queue, e.g. Roslyn-Main-Signed-Release.
                if (string.IsNullOrEmpty(Options.SpecificBuild))
                {
                    buildToInsert = await GetLatestPassedComponentBuildAsync(cancellationToken);

                    buildVersion = BuildVersion.FromTfsBuildNumber(buildToInsert.BuildNumber, Options.ComponentBuildQueueName);
                    Console.WriteLine("Found " + buildToInsert.Definition.Name + " build number " + buildVersion);

                    //  Get the latest build, whether passed or failed.  If the buildToInsert has already been inserted but
                    //  there is a later failing build, then send an error
                    latestBuild = await GetLatestComponentBuildAsync(cancellationToken);
                }
                else
                {
                    buildVersion  = BuildVersion.FromString(Options.SpecificBuild);
                    buildToInsert = await GetSpecificComponentBuildAsync(buildVersion, cancellationToken);
                }

                var insertionArtifacts = await GetInsertionArtifactsAsync(buildToInsert, cancellationToken);

                cancellationToken.ThrowIfCancellationRequested();

                // *********** Look up existing PR ********************
                var gitClient = VisualStudioRepoConnection.GetClient <GitHttpClient>();
                var branches  = await gitClient.GetRefsAsync(
                    VSRepoId,
                    filter : $"heads/{Options.VisualStudioBranchName}",
                    cancellationToken : cancellationToken);

                var baseBranch = branches.Single(b => b.Name == $"refs/heads/{Options.VisualStudioBranchName}");

                var pullRequestId = Options.UpdateExistingPr;
                var useExistingPr = pullRequestId != 0;

                GitPullRequest pullRequest;
                string         insertionBranchName;
                if (useExistingPr)
                {
                    pullRequest = await gitClient.GetPullRequestByIdAsync(pullRequestId, cancellationToken : cancellationToken);

                    insertionBranchName = pullRequest.SourceRefName.Substring("refs/heads/".Length);

                    var refs = await gitClient.GetRefsAsync(VSRepoId, filter : $"heads/{insertionBranchName}", cancellationToken : cancellationToken);

                    var insertionBranch = refs.Single(r => r.Name == $"refs/heads/{insertionBranchName}");

                    if (Options.OverwritePr)
                    {
                        // overwrite existing PR branch back to base before pushing new commit
                        var updateToBase = new GitRefUpdate
                        {
                            OldObjectId = insertionBranch.ObjectId,
                            NewObjectId = baseBranch.ObjectId,
                            Name        = $"refs/heads/{insertionBranchName}"
                        };
                        var results = await gitClient.UpdateRefsAsync(new[] { updateToBase }, VSRepoId, cancellationToken : cancellationToken);

                        foreach (var result in results)
                        {
                            if (!result.Success)
                            {
                                LogError("Failed to overwrite PR: " + result.CustomMessage);
                            }
                        }
                    }
                    else
                    {
                        // not overwriting PR, so the insertion branch is actually the base
                        baseBranch = insertionBranch;
                    }
                }
                else
                {
                    pullRequest         = null;
                    insertionBranchName = GetNewBranchName();
                }

                var allChanges = new List <GitChange>();

                var coreXT = await CoreXT.Load(gitClient, baseBranch.ObjectId);

                if (Options.InsertCoreXTPackages)
                {
                    // ************** Update Nuget Packages For Branch************************
                    cancellationToken.ThrowIfCancellationRequested();
                    Console.WriteLine($"Updating Nuget Packages");
                    bool success;
                    (success, newPackageFiles) = UpdatePackages(
                        buildVersion,
                        coreXT,
                        insertionArtifacts.GetPackagesDirectory(),
                        Options.SkipCoreXTPackages,
                        cancellationToken);
                    retainBuild |= success;

                    // *********** Copy OptimizationInputs.props file ***********************
                    foreach (var propsFile in insertionArtifacts.GetOptProfPropertyFiles())
                    {
                        var propsFilename = Path.GetFileName(propsFile);
                        if (propsFilename == "dotnet-roslyn.props")
                        {
                            // Since the propsFilename is based on repo name, during Roslyn's transition from inserting
                            // from GH dotnet/roslyn builds to inserting from dnceng dotnet-roslyn builds, this will
                            // ensure that we look for the proper props filename.
                            propsFilename = "dotnet.roslyn.props";
                        }

                        var targetFilePath = $"src/Tests/config/runsettings/Official/OptProf/External/{propsFilename}";

                        var version = new GitVersionDescriptor {
                            VersionType = GitVersionType.Commit, Version = baseBranch.ObjectId
                        };
                        var stream = await gitClient.GetItemContentAsync(VSRepoId, targetFilePath, download : true, versionDescriptor : version);

                        var originalContent = new StreamReader(stream).ReadToEnd();

                        var newContent = File.ReadAllText(propsFile);

                        if (GetChangeOpt(targetFilePath, originalContent, newContent) is GitChange change)
                        {
                            allChanges.Add(change);
                        }
                    }
                }

                if (Options.UpdateCoreXTLibraries || Options.UpdateAssemblyVersions)
                {
                    // ************** Update assembly versions ************************
                    cancellationToken.ThrowIfCancellationRequested();
                    Console.WriteLine($"Updating assembly versions");
                    if (await UpdateAssemblyVersionsOpt(gitClient, baseBranch.ObjectId, insertionArtifacts) is GitChange assemblyVersionChange)
                    {
                        allChanges.Add(assemblyVersionChange);
                    }

                    // if we got this far then we definitely need to retain this build
                    retainBuild = true;
                }

                // *********** Update toolset ********************
                if (Options.InsertToolset)
                {
                    UpdateToolsetPackage(coreXT, insertionArtifacts, buildVersion);
                    retainBuild = true;
                }

                // ************ Update .corext\Configs\default.config ********************
                cancellationToken.ThrowIfCancellationRequested();
                Console.WriteLine($"Updating CoreXT default.config and props files under src/ConfigData/Packages");
                foreach (var configChange in coreXT.SaveConfigs())
                {
                    if (configChange is not null)
                    {
                        allChanges.Add(configChange);
                    }
                }

                // *********** Update .corext\Configs\components.json ********************

                BuildVersion oldComponentVersion = default;
                if (Options.InsertWillowPackages)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    Console.WriteLine($"Updating CoreXT components file");

                    var components = GetLatestBuildComponents(buildToInsert, insertionArtifacts, cancellationToken);
                    var shouldSave = false;
                    foreach (var newComponent in components)
                    {
                        if (coreXT.TryGetComponentByName(newComponent.Name, out var oldComponent))
                        {
                            if (oldComponent.BuildVersion != default)
                            {
                                oldComponentVersion = oldComponent.BuildVersion;
                            }
                            coreXT.UpdateComponent(newComponent);
                            shouldSave = true;
                        }
                    }
                    if (shouldSave)
                    {
                        var allComponentChanges = coreXT.SaveComponents();
                        allChanges.AddRange(allComponentChanges);
                        retainBuild = true;
                    }
                }

                // ************* Ensure the build is retained on the servers *************
                if (Options.RetainInsertedBuild && retainBuild && !buildToInsert.KeepForever.GetValueOrDefault())
                {
                    await RetainComponentBuild(buildToInsert);
                }

                // ************* Bail out if there are no changes ************************
                if (!allChanges.Any() && options.CherryPick.IsDefaultOrEmpty)
                {
                    LogWarning("No meaningful changes since the last insertion was merged. PR will not be created or updated.");
                    return(true, 0);
                }

                // ********************* Create push *************************************
                var currentCommit = baseBranch.ObjectId;
                if (allChanges.Any())
                {
                    var insertionBranchUpdate = new GitRefUpdate
                    {
                        Name        = $"refs/heads/{insertionBranchName}",
                        OldObjectId = baseBranch.ObjectId
                    };

                    var commit = new GitCommitRef
                    {
                        Comment = $"Updating {Options.InsertionName} to {buildVersion}",
                        Changes = allChanges
                    };
                    var push = new GitPush
                    {
                        RefUpdates = new[] { insertionBranchUpdate },
                        Commits    = new[] { commit }
                    };
                    push = await gitClient.CreatePushAsync(push, VSRepoId, cancellationToken : cancellationToken);

                    currentCommit = push.Commits.Single().CommitId;
                }

                // ********************* Cherry-pick VS commits *****************************
                var cherryPickCommits = Options.CherryPick;
                if (!cherryPickCommits.IsDefaultOrEmpty)
                {
                    Console.WriteLine("Cherry-picking the following VS commits:");
                    foreach (var cherryPickCommit in cherryPickCommits)
                    {
                        var gc = await gitClient.GetCommitAsync(cherryPickCommit, VSRepoId, cancellationToken : cancellationToken);

                        Console.WriteLine("- " + gc.RemoteUrl);
                    }
                    var commitRefs = cherryPickCommits.Select(id => new GitCommitRef()
                    {
                        CommitId = id
                    }).ToArray();

                    var cherryPickBranchName = $"{insertionBranchName}-cherry-pick-{DateTime.Now:yyyyMMddHHmmss}";
                    var cherryPickArgs       = new GitAsyncRefOperationParameters()
                    {
                        Source = new GitAsyncRefOperationSource()
                        {
                            CommitList = commitRefs
                        },
                        OntoRefName      = $"refs/heads/{insertionBranchName}",
                        GeneratedRefName = $"refs/heads/{cherryPickBranchName}"
                    };
                    // Cherry-pick VS commits into insertion branch.
                    var cherryPick = await gitClient.CreateCherryPickAsync(cherryPickArgs, Options.VisualStudioRepoProjectName, VSRepoId, cancellationToken : cancellationToken);

                    while (cherryPick.Status < GitAsyncOperationStatus.Completed)
                    {
                        Console.WriteLine($"Cherry-pick progress: {cherryPick.DetailedStatus?.Progress ?? 0:P}");
                        await Task.Delay(5000);

                        cherryPick = await gitClient.GetCherryPickAsync(options.VisualStudioRepoProjectName, cherryPick.CherryPickId, VSRepoId, cancellationToken : cancellationToken);
                    }
                    Console.WriteLine($"Cherry-pick status: {cherryPick.Status}");

                    if (cherryPick.Status == GitAsyncOperationStatus.Completed)
                    {
                        var cherryPickBranch = await gitClient.GetBranchAsync(VSRepoId, cherryPickBranchName, cancellationToken : cancellationToken);

                        var addCherryPickedCommits = new GitRefUpdate
                        {
                            OldObjectId = currentCommit,
                            NewObjectId = cherryPickBranch.Commit.CommitId,
                            Name        = $"refs/heads/{insertionBranchName}"
                        };
                        var results = await gitClient.UpdateRefsAsync(new[] { addCherryPickedCommits }, VSRepoId, cancellationToken : cancellationToken);

                        foreach (var result in results)
                        {
                            if (!result.Success)
                            {
                                LogError("Failed to reset ref to cherry-pick branch: " + result.CustomMessage);
                            }
                        }
                    }
                    else
                    {
                        LogError("Cherry-picking failed: " + cherryPick.DetailedStatus.FailureMessage);
                    }
                }

                // ********************* Create pull request *****************************
                var oldBuild = await GetSpecificComponentBuildAsync(oldComponentVersion, cancellationToken);

                var prDescriptionMarkdown = CreatePullRequestDescription(oldBuild, buildToInsert, useMarkdown: true);

                if (buildToInsert.Result == BuildResult.PartiallySucceeded)
                {
                    prDescriptionMarkdown += Environment.NewLine + ":warning: The build being inserted has partially succeeded.";
                }

                if (!useExistingPr || Options.OverwritePr)
                {
                    try
                    {
                        var nl = Environment.NewLine;
                        if (oldBuild is null)
                        {
                            prDescriptionMarkdown += $"{nl}---{nl}Unable to find details for previous build ({oldComponentVersion}){nl}";
                        }
                        else
                        {
                            var(changes, diffLink) = await GetChangesBetweenBuildsAsync(oldBuild, buildToInsert, cancellationToken);

                            var diffDescription = changes.Any()
                                ? $"[View Complete Diff of Changes]({diffLink})"
                                : "No source changes since previous insertion";

                            prDescriptionMarkdown += nl + "---" + nl + diffDescription + nl;
                            prDescriptionMarkdown  = AppendChangesToDescription(prDescriptionMarkdown, oldBuild ?? buildToInsert, changes);
                        }
                    }
                    catch (Exception e)
                    {
                        LogWarning("Failed to create diff links.");
                        LogWarning(e.Message);
                    }
                }

                if (useExistingPr)
                {
                    try
                    {
                        if (Options.OverwritePr)
                        {
                            pullRequest = await OverwritePullRequestAsync(pullRequestId, prDescriptionMarkdown, buildVersion.ToString(), cancellationToken);
                        }
                        pullRequestId = pullRequest.PullRequestId;
                    }
                    catch (Exception ex)
                    {
                        LogError($"Unable to update pull request for '{pullRequest.SourceRefName}'");
                        LogError(ex);
                        return(false, 0);
                    }
                }
                else
                {
                    // create a new PR
                    Console.WriteLine($"Create Pull Request");
                    try
                    {
                        // If this insertion was queued for PR validation, for a dev branch, for a feature branch,
                        // or if no default reviewer is specified, then add the build queuer as a reviewer.
                        var  isPrValidation       = !string.IsNullOrEmpty(GetBuildPRNumber(buildToInsert));
                        var  isDevOrFeatureBranch = Options.ComponentBranchName.StartsWith("dev/") || Options.ComponentBranchName.StartsWith("features/");
                        bool hasReviewer          = !string.IsNullOrEmpty(Options.ReviewerGUID);

                        // Easiest way to get the reviewer GUIDs is to create a PR search in AzDo
                        // You'll get something like https://dev.azure.com/devdiv/DevDiv/_git/VS/pullrequests?_a=active&createdBy=GUID-here
                        var reviewerId = (isPrValidation || isDevOrFeatureBranch) || !hasReviewer
                            ? buildToInsert.RequestedBy.Id
                            : Options.ReviewerGUID;

                        pullRequest = await CreateVSPullRequestAsync(insertionBranchName, prDescriptionMarkdown, buildVersion.ToString(), reviewerId, cancellationToken);

                        if (pullRequest == null)
                        {
                            LogError($"Unable to create pull request for '{insertionBranchName}'");
                            return(false, 0);
                        }

                        pullRequestId = pullRequest.PullRequestId;
                    }
                    catch (Exception ex)
                    {
                        LogError($"Unable to create pull request for '{insertionBranchName}'");
                        LogError(ex);
                        return(false, 0);
                    }
                }

                // ********************* Create validation build *****************************
                if (Options.QueueValidationBuild)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    Console.WriteLine($"Create Validation Build");
                    try
                    {
                        if (Options.CreateDraftPr)
                        {
                            // When creating Draft PRs no policies are automatically started.
                            // If we do not queue a CloudBuild the Perf DDRITs request will
                            // spin waiting for a build to test against until it timesout.
                            await QueueVSBuildPolicy(pullRequest, "CloudBuild - PR");
                        }

                        await QueueVSBuildPolicy(pullRequest, "Request Perf DDRITs");
                    }
                    catch (Exception ex)
                    {
                        LogWarning($"Unable to create a CloudBuild validation build for '{insertionBranchName}'");
                        LogWarning(ex);
                    }

                    if (Options.CreateDraftPr)
                    {
                        // When creating Draft PRs no policies are automatically started.
                        await TryQueueVSBuildPolicy(pullRequest, "Insertion Hash Check", insertionBranchName);
                        await TryQueueVSBuildPolicy(pullRequest, "Insertion Sign Check", insertionBranchName);
                        await TryQueueVSBuildPolicy(pullRequest, "Insertion Symbol Check", insertionBranchName);
                    }
                }

                // ********************* Set PR to Auto-Complete *****************************
                if (Options.SetAutoComplete)
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    Console.WriteLine($"Set PR to Auto-Complete");
                    try
                    {
                        var prDescriptionText = CreatePullRequestDescription(oldBuild, buildToInsert, useMarkdown: false);
                        await SetAutoCompleteAsync(pullRequest, prDescriptionText, cancellationToken);
                    }
                    catch (Exception ex)
                    {
                        LogWarning($"Unable to Set PR to Auto-Complete for '{insertionBranchName}'");
                        LogWarning(ex);
                    }
                }

                return(true, pullRequestId);
            }
            catch (Exception ex)
            {
                LogError(ex);
                return(false, 0);
            }
            finally
            {
                Options = null;
            }
        }