diff --git a/GithubIssueTagger/GitHubClientBinder.cs b/GithubIssueTagger/GitHubClientBinder.cs index 84b02447..4083260c 100644 --- a/GithubIssueTagger/GitHubClientBinder.cs +++ b/GithubIssueTagger/GitHubClientBinder.cs @@ -41,6 +41,12 @@ protected override GitHubPat GetBoundValue(BindingContext bindingContext) return pat; } + pat = GetGitHubCliToken(); + if (!string.IsNullOrEmpty(pat)) + { + return pat; + } + Dictionary? credentials = GetGitCredentials(new Uri("https://github.com/NuGet/Home")); if (credentials?.TryGetValue("password", out pat) == true && !string.IsNullOrEmpty(pat)) { @@ -50,6 +56,42 @@ protected override GitHubPat GetBoundValue(BindingContext bindingContext) return null; } + // Attempt to get a token from the GitHub CLI + private static string? GetGitHubCliToken() + { + try + { + ProcessStartInfo processStartInfo = new() + { + FileName = "gh", + Arguments = "auth token", + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + }; + + Process? process = Process.Start(processStartInfo); + if (process == null) + { + return null; + } + + string output = process.StandardOutput.ReadToEnd().Trim(); + process.WaitForExit(); + + if (process.ExitCode == 0 && !string.IsNullOrEmpty(output)) + { + return output; + } + } + catch + { + // gh CLI not available + } + + return null; + } + // Implement https://git-scm.com/docs/git-credential#_typical_use_of_git_credential private static Dictionary? GetGitCredentials(Uri uri) { diff --git a/GithubIssueTagger/GithubIssueTagger.csproj b/GithubIssueTagger/GithubIssueTagger.csproj index 6a314eb9..e3f36184 100644 --- a/GithubIssueTagger/GithubIssueTagger.csproj +++ b/GithubIssueTagger/GithubIssueTagger.csproj @@ -41,4 +41,15 @@ + + + + + + + + diff --git a/GithubIssueTagger/IEnumerableExtensions.cs b/GithubIssueTagger/IEnumerableExtensions.cs index abb123b3..5587195e 100644 --- a/GithubIssueTagger/IEnumerableExtensions.cs +++ b/GithubIssueTagger/IEnumerableExtensions.cs @@ -17,5 +17,17 @@ internal static class IEnumerableExtensions return default; } } + + internal static T? MaxOrDefault(this IEnumerable enumerable) where T : IComparable + { + try + { + return enumerable.Max(); + } + catch (InvalidOperationException) + { + return default; + } + } } } diff --git a/GithubIssueTagger/Reports/IceBox/GetIssues.graphql b/GithubIssueTagger/Reports/IceBox/GetIssues.graphql index 2806d058..1fe5fe8a 100644 --- a/GithubIssueTagger/Reports/IceBox/GetIssues.graphql +++ b/GithubIssueTagger/Reports/IceBox/GetIssues.graphql @@ -15,7 +15,8 @@ id, number, title, - timelineItems(itemTypes: [LABELED_EVENT], last: $timelineCount) { + url, + timelineItems(itemTypes: [LABELED_EVENT, UNLABELED_EVENT], last: $timelineCount) { totalCount, pageInfo { hasNextPage, @@ -29,6 +30,13 @@ id }, createdAt + }, + ... on UnlabeledEvent { + label { + name, + id + }, + createdAt } } }, diff --git a/GithubIssueTagger/Reports/IceBox/GetIssuesResult.cs b/GithubIssueTagger/Reports/IceBox/GetIssuesResult.cs index d8a87784..0954443b 100644 --- a/GithubIssueTagger/Reports/IceBox/GetIssuesResult.cs +++ b/GithubIssueTagger/Reports/IceBox/GetIssuesResult.cs @@ -28,11 +28,12 @@ public RepositoryModel(Connection issues) internal class IssuesModel { - public IssuesModel(string id, int? number, string title, Connection timelineItems, Connection reactions, Connection