private Subscription CreateSubscription(MergePolicy mergePolicy) { return(new Subscription { SourceRepository = "source.repo", TargetRepository = "target.repo", TargetBranch = "target.branch", PolicyObject = new SubscriptionPolicy { MergePolicy = mergePolicy, UpdateFrequency = UpdateFrequency.EveryDay } }); }
private async Task <bool> ShouldMergePrAsync(IRemote darc, string url, MergePolicy policy) { IList <Check> checks = await darc.GetPullRequestChecksAsync(url); if (checks.Count == 0) { return(false); // Don't auto merge anything that has no checks. } if (checks.All(c => c.Status == CheckState.Success)) { return(true); // If every check succeeded merge the pr } return(false); }
public async Task SynchronizeInProgressPRAsync() { Subscription subscription = await Context.Subscriptions.FindAsync(SubscriptionId); if (subscription == null) { await Reminders.TryUnregisterReminderAsync(PullRequestCheck); await StateManager.TryRemoveStateAsync(PullRequest); return; } ConditionalValue <InProgressPullRequest> maybePr = await StateManager.TryGetStateAsync <InProgressPullRequest>(PullRequest); if (maybePr.HasValue) { InProgressPullRequest pr = maybePr.Value; long installationId = await Context.GetInstallationId(subscription.TargetRepository); IRemote darc = await DarcFactory.CreateAsync(pr.Url, installationId); MergePolicy policy = subscription.PolicyObject.MergePolicy; PrStatus status = await darc.GetPullRequestStatusAsync(pr.Url); switch (status) { case PrStatus.Open: switch (policy) { case MergePolicy.Never: return; case MergePolicy.BuildSucceeded: case MergePolicy.UnitTestPassed: // for now both of these cases are the same if (await ShouldMergePrAsync(darc, pr.Url, policy)) { await darc.MergePullRequestAsync(pr.Url); goto merged; } return; default: Logger.LogError("Unknown merge policy '{policy}'", policy); return; } case PrStatus.Merged: merged: subscription.LastAppliedBuildId = pr.BuildId; await Context.SaveChangesAsync(); goto case PrStatus.Closed; case PrStatus.Closed: await StateManager.RemoveStateAsync(PullRequest); break; default: Logger.LogError("Unknown pr status '{status}'", status); return; } } await Reminders.TryUnregisterReminderAsync(PullRequestCheck); }
public async Task Test( MergePolicy mergePolicy, PrStatus prStatus, bool existingPrHasChecks, bool existingPrPassedChecks) { Channel channel = CreateChannel(); Build oldBuild = CreateOldBuild(); Build build = CreateNewBuild(); var buildChannels = new[] { new BuildChannel { Build = oldBuild, Channel = channel }, new BuildChannel { Build = build, Channel = channel } }; Subscription subscription = CreateSubscription(mergePolicy); subscription.Channel = channel; var repoInstallation = new RepoInstallation { Repository = subscription.TargetRepository, InstallationId = 1 }; Asset asset = build.Assets[0]; await Context.RepoInstallations.AddAsync(repoInstallation); await Context.Subscriptions.AddAsync(subscription); await Context.BuildChannels.AddRangeAsync(buildChannels); await Context.SaveChangesAsync(); var actorId = new ActorId(subscription.Id); var existingPr = "https://repo.pr/existing"; var pr = "https://repo.pr/new"; bool shouldMergeExistingPr = prStatus == PrStatus.Open && mergePolicy == MergePolicy.BuildSucceeded && existingPrHasChecks && existingPrPassedChecks; void SetupCreatePr() { Darc.Setup( d => d.CreatePullRequestAsync( subscription.TargetRepository, subscription.TargetBranch, build.Commit, It.IsAny <IList <AssetData> >(), null, null, null)) .ReturnsAsync( ( string repo, string branch, string commit, IList <AssetData> assets, string baseBranch, string title, string description) => { assets.Should().BeEquivalentTo(new AssetData { Name = asset.Name, Version = asset.Version }); return(pr); }); } void SetupUpdatePr() { Darc.Setup( r => r.UpdatePullRequestAsync( existingPr, build.Commit, subscription.TargetBranch, It.IsAny <IList <AssetData> >(), null, null)) .ReturnsAsync( ( string url, string commit, string branch, IList <AssetData> assets, string title, string description) => { return(url); }); } void SetupExistingPr() { StateManager.Data[SubscriptionActor.PullRequest] = new InProgressPullRequest { BuildId = oldBuild.Id, Url = existingPr }; Darc.Setup(r => r.GetPullRequestStatusAsync(existingPr)).ReturnsAsync(prStatus); if (mergePolicy == MergePolicy.BuildSucceeded && prStatus == PrStatus.Open) { if (existingPrHasChecks) { Darc.Setup(r => r.GetPullRequestChecksAsync(existingPr)) .ReturnsAsync( new List <Check> { new Check( existingPrPassedChecks ? CheckStatus.Succeeded : CheckStatus.Failed, "check", "https://check.stuff/1") }); } else { Darc.Setup(r => r.GetPullRequestChecksAsync(existingPr)).ReturnsAsync(new List <Check>()); } } if (shouldMergeExistingPr) { Darc.Setup(r => r.MergePullRequestAsync(existingPr, null, null, null, null)) .Returns(Task.CompletedTask); } } switch (prStatus) { case PrStatus.None: SetupCreatePr(); break; case PrStatus.Open: SetupExistingPr(); if (shouldMergeExistingPr) { SetupCreatePr(); } else { SetupUpdatePr(); } break; case PrStatus.Merged: SetupExistingPr(); SetupCreatePr(); break; case PrStatus.Closed: SetupExistingPr(); SetupCreatePr(); break; } var actor = ActivatorUtilities.CreateInstance <SubscriptionActor>(Scope.ServiceProvider, actorId); await actor.UpdateAsync(build.Id); if (shouldMergeExistingPr || prStatus == PrStatus.Merged) { subscription.LastAppliedBuildId.Should().Be(oldBuild.Id); } else { subscription.LastAppliedBuildId.Should().Be(null); } StateManager.Data.Should() .BeEquivalentTo( new Dictionary <string, object> { [SubscriptionActor.PullRequest] = new InProgressPullRequest { BuildId = build.Id, Url = prStatus == PrStatus.Open && !shouldMergeExistingPr ? existingPr : pr } }); Reminders.Data.Should() .BeEquivalentTo( new Dictionary <string, MockReminderManager.Reminder> { [SubscriptionActor.PullRequestCheck] = new MockReminderManager.Reminder( SubscriptionActor.PullRequestCheck, Array.Empty <byte>(), TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)) }); }