예제 #1
0
        public async Task <bool> Run(string repositoryOwner, string repositoryName, int number)
        {
            TimeSpan pollingInterval = _configuration.PollingInterval;

            if (pollingInterval < _configuration.MinDelay)
            {
                pollingInterval = _configuration.MinDelay;
            }

            ConfiguredTaskAwaitable delay() => Task.Delay(pollingInterval).ConfigureAwait(true);
            DateTime timeAfterDelay() => DateTime.Now + pollingInterval;
            ConfiguredTaskAwaitable oneSecondDelay() => Task.Delay(_configuration.MinDelay).ConfigureAwait(true);

            void info(string message) => _logger.LogInformation(message);
            void error(string message) => _logger.LogError(message);

            // Only prompt user once
            bool prompted = false;

            while (true)
            {
                // We check the pull request in a loop by looking it up in the repository and
                // checking its status. If the head branch is behind the base branch, we merge base into head
                // and continue the loop. If the branch is clean and PR can be merged, the PR is merged without
                // deleting the head branch.
                PullRequest pullRequest = await _client.GetPullRequest(repositoryOwner, repositoryName, number);

                if (pullRequest == null)
                {
                    error($"Pull request #{number} was not found in {repositoryOwner}/{repositoryName}.");
                    return(false);
                }

                if (pullRequest.Merged == null)
                {
                    error($"Pull request #{pullRequest.Number} has unknown merge status.");
                    return(false);
                }

                if (pullRequest.Merged == true)
                {
                    info($"Pull request #{pullRequest.Number} has already been merged.");
                    return(true);
                }

                if (pullRequest.MergeableState == "dirty")
                {
                    error($"Pull request #{pullRequest.Number} cannot be merged due to unresolved conflicts.");
                    return(false);
                }

                // We ask user to confirm his intentions by entering the PR's title at the beginning
                if (!prompted)
                {
                    string input = _userInterface.Prompt($"Found pull request #{pullRequest.Number}.\n" +
                                                         $"Merging into '{pullRequest.Base.Ref}' from '{pullRequest.Head.Ref}'\n\n" +
                                                         $"To initiate auto-complete for pull request #{pullRequest.Number} enter its full title (case matters): ");

                    if (!string.Equals(input, pullRequest.Title, StringComparison.Ordinal))
                    {
                        error($"Pull request #{pullRequest.Number} title does not match the input.");
                        return(false);
                    }

                    prompted = true;
                }

                if (pullRequest.MergeableState == "blocked")
                {
                    // This happens when a background status check is pending, i.e. a CI build
                    info($"Pull request #{pullRequest.Number} merging is blocked. Will try again at {timeAfterDelay():T}.");
                    await delay();

                    continue;
                }

                if (pullRequest.MergeableState == "behind")
                {
                    info($"Pull request #{pullRequest.Number} branch has to be updated. Will try that now.");
                    bool updated = await _client.UpdatePullRequestHead(pullRequest);

                    if (!updated)
                    {
                        error($"Failed to merge {pullRequest.Base.Ref} into {pullRequest.Head.Ref}.");
                        return(false);
                    }

                    info($"Successfully merged {pullRequest.Base.Ref} into {pullRequest.Head.Ref}.");
                    continue;
                }

                if (pullRequest.MergeableState != "clean" &&
                    (!_configuration.MergeUnstable ||
                     pullRequest.MergeableState != "unstable"))
                {
                    error($"Pull request #{pullRequest.Number}'s branch is in unallowed mergeable state: {pullRequest.MergeableState}.");
                    return(false);
                }

                if (pullRequest.Mergeable == null)
                {
                    // This happens when GitHub has not finished computation
                    info($"Pull request #{pullRequest.Number} is being validated for mergeability. Will try again in a second.");
                    await oneSecondDelay();

                    continue;
                }

                if (pullRequest.Mergeable == false)
                {
                    // PR can still be not mergeable if something has changed since our last check
                    error($"Pull request #{pullRequest.Number} cannot be merged.");
                    return(false);
                }

                bool merged = await _client.MergePullRequest(pullRequest);

                if (!merged)
                {
                    error($"Pull request #{pullRequest.Number} failed to merge.");
                    return(false);
                }

                info($"Pull request #{pullRequest.Number} has been merged.");
                return(true);
            }
        }