Cmdlets/src/XpandPwsh.Cmdlets/GitHub/CheckpointGithubIssue/Checkpoint-GithubIssue.cs
using System;
using System.Linq; using System.Management.Automation; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Octokit; using SmartFormat; using XpandPwsh.Cmdlets.GitHub.GetGitHubCommitIssue; namespace XpandPwsh.Cmdlets.GitHub.CheckpointGithubIssue{ [CmdletBinding(SupportsShouldProcess = true)] [Cmdlet(VerbsData.Checkpoint, "GitHubIssue",SupportsShouldProcess = true)] [CmdLetTag(CmdLetTag.GitHub,CmdLetTag.Reactive,CmdLetTag.RX)][PublicAPI] public class CheckpointGitHubIssue : GitHubCmdlet{ [Parameter(Mandatory = true)] public string Message{ get; set; } [Parameter(Mandatory = true)] public ICommitIssues[] CommitIssues{ get; set; } protected override Task ProcessRecordAsync(){ return LinkCommits() .HandleErrors(this) .WriteObject(this) .ToTask(); } private IObservable<PSObject> LinkCommits( ){ var synchronizationContext = SynchronizationContext.Current; var repositories = GitHubClient.Repository.GetAllForOrg(Organization) .ToObservable().Replay().AutoConnect() .ToEnumerable() .SelectMany(list => list) .ToArray(); var issueToNotify = CommitIssues .Select(_ => _.Issues.Select(issue => (_.GitHubCommit, issue)).ToObservable() .WriteVerboseObject(this,__ => $"Commit ({__.GitHubCommit.Sha}){__.GitHubCommit.Commit.Message} links to {__.issue.Number}",synchronizationContext) .SelectMany(tuple => IssueComments(GitHubClient, _, tuple, synchronizationContext))) .Merge(); return issueToNotify .GroupBy(_ => (_.issue, _.repo1,_.repo2)) .SelectMany(_ => _.TakeUntil(_.LastAsync()).ToArray() .Select(tuples => tuples.Select(valueTuple => valueTuple.GitHubCommit).ToArray()) .Select(hubCommits => (key: _.Key, commits: hubCommits))) .ObserveOn(synchronizationContext) .SelectMany(_ => { var comment = GenerateComment(_,repositories); var issue = _.key.issue; var psObject = Observable.Return(new PSObject(new { IssueNumber = issue.Number, Milestone = issue.Milestone?.Title,issue.Title, Commits = string.Join(",", _.commits.Select(commit => commit.Commit.Message)), Comment = comment })); var text = $"Create comment for issue {_.key.issue.Number}"; WriteVerbose(text); if (ShouldProcess(text)){ return GitHubClient.Issue.Comment.Create(_.key.repo1, _.key.issue.Number, comment) .ToObservable().Select(issueComment => psObject).Concat(); } return psObject; }) .DefaultIfEmpty(); } private IObservable<(long repo1, long repo2, GitHubCommit GitHubCommit, Issue issue)> IssueComments(GitHubClient appClient, ICommitIssues _, (GitHubCommit GitHubCommit, Issue issue) tuple, SynchronizationContext synchronizationContext){ return appClient.Issue.Comment .GetAllForIssue(_.Repository1.Id, tuple.issue.Number) .ToObservable() .ObserveOn(synchronizationContext) .Where(list => { WriteVerbose($"Searching {list.Count} comments in Issue {tuple.issue.Number} for {tuple.GitHubCommit.Sha}"); var checkpoint = list.Any(comment => comment.Body.Contains(tuple.GitHubCommit.Sha)); WriteVerbose($"Checkpoint {(!checkpoint ? " not " : null)}exists"); return !checkpoint; }) .Select(list => (repo1: _.Repository1.Id, repo2: _.Repository2.Id, _.GitHubCommit, tuple.issue)); } private string GenerateComment(((Issue issue, long repo1, long repo2) key, GitHubCommit[] commits) _, Repository[] repositories){ var repositoryName = repositories.First(repository => repository.Id==_.key.repo2).Name; var objects = new object[] { new{ Options = this, Commits = string.Join(Environment.NewLine, _.commits.Select(commit => $@"* [{commit.Commit.Message}](https://github.com/{Organization}/{repositoryName}/commit/{commit.Sha})")) } }; var comment = Smart.Format(Message, objects); return comment; } } } |