public void NotifyUserAboutPullRequestWithUnresolvedConflicts( int pullRequestNumber, string gitHubUserName, IRepositoryConnectionContext repoContext, string pullRequestBranchName, string destinationBranch, string pullRequestUrl) { var comment = $"Cannot merge automatically. @{gitHubUserName} please resolve conflicts manually, approve review and merge pull request." + "\r\n\r\n" + "How to do it (using the GIT command line):\r\n" + $"1. Fetch changes from server and checkout '{destinationBranch}' branch\r\n" + " ```\r\n" + $" git fetch && git checkout {destinationBranch} && " + "git reset --hard @{u}\r\n" + " ```\r\n" + $"2. Merge 'origin/{pullRequestBranchName}' branch and resolve conflicts\r\n" + " ```\r\n" + $" git merge --no-ff origin/{pullRequestBranchName}\r\n" + " ```\r\n" + $"4. Approve [pull request]({pullRequestUrl}/files#submit-review) review\r\n" + $"5. Push changes to {destinationBranch}\r\n" + " ```\r\n" + $" git push origin {destinationBranch}\r\n" + " ```\r\n"; repoContext.AddPullRequestComment(pullRequestNumber, comment); repoContext.AddReviewerToPullRequest(pullRequestNumber, new[] { gitHubUserName }); repoContext.AssignUsersToPullRequest(pullRequestNumber, new[] { gitHubUserName }); }
private string RetrieveChangesOriginalAuthorFromPush( PushInfoModel pushInfo, IRepositoryConnectionContext repoContext, out string coAuthorString ) { var coAuthoredLineRegex = new Regex(@"^Co-authored-by: (?<author>.+)", RegexOptions.IgnoreCase | RegexOptions.Multiline); if (pushInfo.HeadCommitAuthorUserName == _cfg.AutomergeBotGitHubUserName) { var headCommitMessage = repoContext.GetCommitMessage(pushInfo.HeadCommitSha); var match = coAuthoredLineRegex.Match(headCommitMessage); if (match.Success) { coAuthorString = match.Value; var r = new Regex(@"^Co-authored-by: (?<author>[\w\.\-]+)"); return(r.Match(match.Value).Groups["author"].Value); } _logger.LogError( "Pushed changes head commit is made by us ({automergeBotGitHubUserName}) but does not contain \"Co-authored-by\" in the message. " + "Merge commit will be missing original author of the changes.", _cfg.AutomergeBotGitHubUserName); } coAuthorString = CreateCoAuthoredByMessageForNewPullRequest(pushInfo); return(pushInfo.HeadCommitAuthorUserName); }
public RepositoryConnectionContextExceptionHandlingWrapper( ILogger logger, IRepositoryConnectionContext repositoryConnectionContext) { _logger = logger; _repositoryConnectionContext = repositoryConnectionContext; }
private List <PullRequest> GetOpenPullRequestsTargetingBranch(IRepositoryConnectionContext repoContext, string targetBranchName) { var openPullRequestsTargetingBranch = repoContext .GetOpenPullRequests() .Where(pr => pr.Base.Ref == targetBranchName) .Where(pr => pr.User.Login == _cfg.AutomergeBotGitHubUserName); return(openPullRequestsTargetingBranch.ToList()); }
private bool IsMonitoredRepository(PushInfoModel pushInfo, IRepositoryConnectionContext repoContext) { if (repoContext.IsMonitoredRepository(pushInfo.RepositoryId)) { return(true); } _logger.LogDebug("Push is not from monitored repository ({RepositoryOwner}/{RepositoryName})", _cfg.RepositoryOwner, _cfg.RepositoryName); return(false); }
public void RetryMergePullRequestsCreatedBefore(PushInfoModel pushInfo, IRepositoryConnectionContext repoContext) { var targetBranchName = pushInfo.GetPushedBranchName().Name; _logger.LogInformation("Retrying merging pull requests created by AutomergeBot before and not merged yet to branch {targetBranch}", targetBranchName); var openPullRequestsTargetingBranch = GetOpenPullRequestsTargetingBranch(repoContext, targetBranchName); if (openPullRequestsTargetingBranch.Any()) { _logger.LogDebug("There is {openPullRequestsCount} pull requests which potentially could be merged", openPullRequestsTargetingBranch.Count); foreach (var pullRequest in openPullRequestsTargetingBranch) { _mergePerformer.TryMergeExistingPullRequest(pullRequest, repoContext); } } }
public bool CanProcessPush(PushInfoModel pushInfo, IRepositoryConnectionContext repoContext) { if (!IsMonitoredRepository(pushInfo, repoContext)) { return(false); } if (!IsPushAddingNewCommits(pushInfo)) { return(false); } if (IsPushedToIgnoredBranch(pushInfo)) { return(false); } if (!IsAutomergeEnabledForAuthorOfLastestCommit(pushInfo)) { return(false); } return(true); }
private bool TryCreatePullRequest(BranchName sourceBranch, BranchName destinationBranchName, IRepositoryConnectionContext repoContext, out PullRequest pullRequest, PushInfoModel pushInfo, string changesOriginalAuthor) { var title = $"{Consts.AutomergeBotPullRequestTitlePrefix} {pushInfo.GetPushedBranchName()} @{pushInfo.GetHeadCommitShaShort()} -> {destinationBranchName}"; var body = $"Last change author: {changesOriginalAuthor}"; try { pullRequest = repoContext.CreatePullRequest(sourceBranch, destinationBranchName, title, body); return(true); } catch (Exception e) { _logger.LogError(e, "Creating pull request failed."); pullRequest = null; return(false); } }
private void TryDoMerges(PushInfoModel pushInfo, IRepositoryConnectionContext repoContext) { if (!TryGetMergeDestinationBranches(pushInfo.GetPushedBranchName(), out var destinationBranchNames)) { return; } _logger.LogInformation("Merging to {destinationBranchesCount} branches: {destinationBranchNames}", destinationBranchNames.Length, destinationBranchNames); foreach (var destinationBranchName in destinationBranchNames) { try { _mergePerformer.TryMergePushedChanges(pushInfo, destinationBranchName, repoContext); } catch (Exception e) { _logger.LogCritical(e, "Failed merging {commitSha} to {branchName}", pushInfo.HeadCommitSha, pushInfo.GetPushedBranchName()); } } }
public void TryMergePushedChanges( PushInfoModel pushInfo, BranchName destinationBranchName, IRepositoryConnectionContext repoContext) { var branchForPullRequest = CreateBranchNameForPush(pushInfo.GetPushedBranchName(), pushInfo.HeadCommitSha, destinationBranchName); repoContext.CreateBranch(branchForPullRequest, pushInfo.HeadCommitSha); var changesOriginalAuthor = RetrieveChangesOriginalAuthorFromPush(pushInfo, repoContext, out var coAuthorString); var createPullRequestSucceeded = TryCreatePullRequest(branchForPullRequest, destinationBranchName, repoContext, out var pullRequest, pushInfo, changesOriginalAuthor); if (!createPullRequestSucceeded) { _logger.LogWarning("Temp branch {branchName} for not created pull request has to be removed manually", branchForPullRequest); return; } var mergeCommitMessage = CreateMergeCommitMessage(pullRequest, coAuthorString); if (repoContext.MergePullRequest(pullRequest.Number, mergeCommitMessage)) { _logger.LogInformation("Pull request {pullRequestNumber} created and merged", pullRequest.Number); } else { _logger.LogInformation("Pull request {pullRequestNumber} created but could not be merged automatically", pullRequest.Number); _userNotifier.NotifyUserAboutPullRequestWithUnresolvedConflicts( pullRequest.Number, changesOriginalAuthor, repoContext, branchForPullRequest.Name, destinationBranchName.Name, pullRequest.HtmlUrl); } }
public void TryMergeExistingPullRequest( PullRequest pullRequest, IRepositoryConnectionContext repoContext) { var coAuthorString = CreateCoAuthoredByMessageForExistingPullRequest(pullRequest); var mergeCommitMessage = CreateMergeCommitMessage(pullRequest, coAuthorString); if (repoContext.MergePullRequest(pullRequest.Number, mergeCommitMessage)) { _logger.LogInformation( "Successfully merged pull request #{pullRequestNumber} into {branchName}", pullRequest.Number, pullRequest.Base.Ref); repoContext.AddPullRequestComment(pullRequest.Number, Consts.SuccessfulMergeComment); } else { _logger.LogInformation( "Merging pull request #{pullRequestNumber} into {branchName} cannot be done automatically", pullRequest.Number, pullRequest.Base.Ref); } }