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); }
private bool IsPushAddingNewCommits(PushInfoModel pushInfo) { if (!pushInfo.Deleted && !pushInfo.Forced && pushInfo.Ref.StartsWith(Consts.RefsHeads)) { return(true); } _logger.LogDebug("Push does not add new commits. Perhaps it is force push, tags push etc."); return(false); }
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 Handle(PushInfoModel pushInfo) { if (!CanProcess(pushInfo)) { return; } DoCriticalTasks(pushInfo); DoNonCriticalTasks(pushInfo); }
private bool IsPushedToIgnoredBranch(PushInfoModel pushInfo) { var branchName = pushInfo.GetPushedBranchName(); if (branchName.Name.StartsWith(_cfg.CreatedBranchesPrefix)) { _logger.LogDebug("Push is on branch {branchName} which is created by us", branchName); return(true); } return(false); }
private bool CanProcess(PushInfoModel pushInfo) { using (var repoContext = _repositoryConnectionProvider.GetRepositoryConnection()) { if (!_processPushPredicate.CanProcessPush(pushInfo, repoContext)) { return(false); } } return(true); }
private void DoNonCriticalTasks(PushInfoModel pushInfo) { try { using (var repoContext = _repositoryConnectionProvider.GetRepositoryConnection()) { _pullRequestMergeRetryier.RetryMergePullRequestsCreatedBefore(pushInfo, repoContext); } } catch (Exception e) { _logger.LogError(e, "Failed performing non-critical tasks when processing push notification"); } }
private void DoCriticalTasks(PushInfoModel pushInfo) { try { using (var repoContext = _repositoryConnectionProvider.GetRepositoryConnection()) { TryDoMerges(pushInfo, repoContext); } } catch (Exception e) { _logger.LogCritical(e, "Failed performing critical tasks when processing push notification"); } }
private void HandlePushNotification(HttpContext context, string payloadJson) { var pushPayload = JsonConvert.DeserializeObject <JObject>(payloadJson); var pushHandler = context.RequestServices.GetRequiredService <MergingBranchesPushHandler>(); var pushInfoModel = PushInfoModel.CreateFromPayload(pushPayload); _logger.LogInformation("Started processing push notification {@payloadModel}", pushInfoModel); try { pushHandler.Handle(pushInfoModel); } finally { _logger.LogInformation("Finished processing push notification"); } }
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 bool IsAutomergeEnabledForAuthorOfLastestCommit(PushInfoModel pushInfo) { if (!(_cfg.AutomergeOnlyForAuthors ?? new List <string>()).Any()) { return(true); } var headCommitAuthor = pushInfo.HeadCommitAuthorUserName.Trim(); if (headCommitAuthor == _cfg.AutomergeBotGitHubUserName) { return(true); } var enabledForAuthor = _cfg.AutomergeOnlyForAuthors.Any(x => x.Trim() == headCommitAuthor); if (!enabledForAuthor) { _logger.LogWarning("Automerging is not enabled for commit author {commitAuthorUserName}", pushInfo.HeadCommitAuthorUserName); } return(enabledForAuthor); }
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); } }
private static string CreateCoAuthoredByMessageForNewPullRequest(PushInfoModel pushInfo) { return($"Co-authored-by: {pushInfo.HeadCommitAuthorUserName} <{pushInfo.HeadCommitAuthorEmail}>"); }