private async System.Threading.Tasks.Task PushChangeAsync(BuildManifestClient client)
        {
            try
            {
                var location = new BuildManifestLocation(
                    new GitHubProject(VersionsRepo, VersionsRepoOwner),
                    $"heads/{VersionsRepoBranch}",
                    VersionsRepoPath);

                IEnumerable <JoinSemaphoreGroup> joinGroups = JoinSemaphoreGroups
                                                              .Select(item => new
                {
                    ParallelPartPath  = item.ItemSpec,
                    JoinSemaphorePath = item.GetMetadata(JoinSemaphorePathMetadataName)
                })
                                                              .GroupBy(j => j.JoinSemaphorePath, j => j.ParallelPartPath)
                                                              .Select(g => new JoinSemaphoreGroup
                {
                    JoinSemaphorePath      = g.Key,
                    ParallelSemaphorePaths = g
                });

                SupplementaryUploadRequest[] supplementaryUploads =
                    PushOrchestratedBuildManifest.CreateUploadRequests(SupplementaryFiles);

                var change = new BuildManifestChange(
                    location,
                    CommitMessage,
                    OrchestratedBuildId,
                    SemaphoreNames,
                    manifest =>
                {
                    foreach (var update in ManifestUpdates ?? Enumerable.Empty <ITaskItem>())
                    {
                        ApplyUpdate(manifest, update);
                    }
                })
                {
                    SupplementaryUploads = supplementaryUploads,
                    JoinSemaphoreGroups  = joinGroups,
                };

                await client.PushChangeAsync(change);
            }
            catch (ManifestChangeOutOfDateException e)
            {
                Log.LogWarningFromException(e);
            }
        }
        public async Task TestPushConflictingChangeAsync()
        {
            var mockGitHub = new Mock <IGitHubClient>(MockBehavior.Strict);

            var    client           = new BuildManifestClient(mockGitHub.Object);
            var    proj             = new GitHubProject("versions", "dotnet");
            string @ref             = "heads/master";
            string basePath         = "build-info/dotnet/product/cli/master";
            string message          = "Test change manifest commit";
            string addSemaphorePath = "add-identity.semaphore";

            var fakeExistingBuild = new OrchestratedBuildModel(new BuildIdentity {
                Name = "orch", BuildId = "123"
            });
            var fakeNewExistingBuild = new OrchestratedBuildModel(new BuildIdentity {
                Name = "orch", BuildId = "456"
            });
            string fakeCommitHash = "fakeCommitHash";

            mockGitHub
            .Setup(c => c.GetReferenceAsync(proj, @ref))
            .ReturnsAsync(() => new GitReference
            {
                Object = new GitReferenceObject {
                    Sha = fakeCommitHash
                }
            });

            mockGitHub
            .Setup(c => c.GetGitHubFileContentsAsync(It.IsAny <string>(), proj, fakeCommitHash))
            .ReturnsAsync(() => fakeNewExistingBuild.ToXml().ToString());

            var pushClient = client.PushChangeAsync(
                new BuildManifestChange(
                    new BuildManifestLocation(proj, @ref, basePath),
                    message,
                    fakeExistingBuild.Identity.BuildId,
                    new[] { addSemaphorePath },
                    _ => { }
                    ));
            Func <Task> act = async() => { await pushClient; };
            await act.Should().ThrowAsync <ManifestChangeOutOfDateException>();

            mockGitHub.VerifyAll();
        }
        public async Task TestPushChangeSemaphoreAsync()
        {
            var mockGitHub = new Mock <IGitHubClient>(MockBehavior.Strict);

            var    client           = new BuildManifestClient(mockGitHub.Object);
            var    proj             = new GitHubProject("versions", "dotnet");
            string @ref             = "heads/master";
            string basePath         = "build-info/dotnet/product/cli/master";
            string message          = "Test change manifest commit";
            string addSemaphorePath = "add-identity.semaphore";

            var fakeExistingBuild = new OrchestratedBuildModel(new BuildIdentity {
                Name = "orch", BuildId = "123"
            });
            string fakeExistingBuildString = fakeExistingBuild.ToXml().ToString();
            string fakeCommitHash          = "fakeCommitHash";
            string fakeTreeHash            = "fakeTreeHash";
            string fakeNewCommitHash       = "fakeNewCommitHash";

            mockGitHub
            .Setup(c => c.GetReferenceAsync(proj, @ref))
            .ReturnsAsync(() => new GitReference
            {
                Object = new GitReferenceObject {
                    Sha = fakeCommitHash
                }
            });

            mockGitHub
            .Setup(c => c.GetGitHubFileContentsAsync(
                       $"{basePath}/{BuildManifestClient.BuildManifestXmlName}",
                       proj,
                       fakeCommitHash))
            .ReturnsAsync(() => fakeExistingBuildString);

            mockGitHub
            .Setup(c => c.PostTreeAsync(
                       proj,
                       fakeCommitHash,
                       It.Is <GitObject[]>(
                           objects =>
                           objects.Length == 1 &&
                           objects[0].Path == $"{basePath}/{addSemaphorePath}" &&
                           objects[0].Content == fakeExistingBuild.Identity.BuildId + "\n")))
            .ReturnsAsync(() => new GitTree {
                Sha = fakeTreeHash
            });

            mockGitHub
            .Setup(c => c.PostCommitAsync(
                       proj,
                       message,
                       fakeTreeHash,
                       It.Is <string[]>(parents => parents.Single() == fakeCommitHash)))
            .ReturnsAsync(() => new GitCommit {
                Sha = fakeNewCommitHash
            });

            mockGitHub
            .Setup(c => c.PatchReferenceAsync(proj, @ref, fakeNewCommitHash, false))
            .ReturnsAsync(() => null);

            await client.PushChangeAsync(
                new BuildManifestChange(
                    new BuildManifestLocation(proj, @ref, basePath),
                    message,
                    fakeExistingBuild.Identity.BuildId,
                    new[] { addSemaphorePath },
                    _ => { }));

            mockGitHub.VerifyAll();
        }