Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for creating pull requests as drafts #131

Merged
merged 4 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ public async Task WhenThereAreMultiplePullRequestsInAStack_OpensAllPullRequests(

inputProvider.Select(Questions.SelectStack, Arg.Any<string[]>()).Returns("Stack1");

var prForBranch3 = new GitHubPullRequest(1, "PR Title for branch-3", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri());
var prForBranch3 = new GitHubPullRequest(1, "PR Title for branch-3", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri(), false);
gitHubOperations
.GetPullRequest("branch-3")
.Returns(prForBranch3);

var prForBranch5 = new GitHubPullRequest(2, "PR Title for branch-5", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri());
var prForBranch5 = new GitHubPullRequest(2, "PR Title for branch-5", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri(), false);
gitHubOperations
.GetPullRequest("branch-5")
.Returns(prForBranch5);
Expand Down Expand Up @@ -85,12 +85,12 @@ public async Task WhenThereAreSomePullRequestsInAStack_OpensAllPullRequests()

inputProvider.Select(Questions.SelectStack, Arg.Any<string[]>()).Returns("Stack1");

var prForBranch3 = new GitHubPullRequest(1, "PR Title for branch-3", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri());
var prForBranch3 = new GitHubPullRequest(1, "PR Title for branch-3", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri(), false);
gitHubOperations
.GetPullRequest("branch-3")
.Returns(prForBranch3);

var prForBranch5 = new GitHubPullRequest(2, "PR Title for branch-5", string.Empty, GitHubPullRequestStates.Closed, Some.HttpsUri());
var prForBranch5 = new GitHubPullRequest(2, "PR Title for branch-5", string.Empty, GitHubPullRequestStates.Closed, Some.HttpsUri(), false);

// Act
await handler.Handle(OpenPullRequestsCommandInputs.Empty);
Expand Down Expand Up @@ -123,12 +123,12 @@ public async Task WhenStackNameIsProvided_OpensAllPullRequestsForTheStack()
]);
stackConfig.Load().Returns(stacks);

var prForBranch3 = new GitHubPullRequest(1, "PR Title for branch-3", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri());
var prForBranch3 = new GitHubPullRequest(1, "PR Title for branch-3", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri(), false);
gitHubOperations
.GetPullRequest("branch-3")
.Returns(prForBranch3);

var prForBranch5 = new GitHubPullRequest(2, "PR Title for branch-5", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri());
var prForBranch5 = new GitHubPullRequest(2, "PR Title for branch-5", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri(), false);
gitHubOperations
.GetPullRequest("branch-5")
.Returns(prForBranch5);
Expand Down Expand Up @@ -163,12 +163,12 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_OpensAllPullRequ
]);
stackConfig.Load().Returns(stacks);

var prForBranch3 = new GitHubPullRequest(1, "PR Title for branch-3", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri());
var prForBranch3 = new GitHubPullRequest(1, "PR Title for branch-3", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri(), false);
gitHubOperations
.GetPullRequest("branch-3")
.Returns(prForBranch3);

var prForBranch5 = new GitHubPullRequest(2, "PR Title for branch-5", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri());
var prForBranch5 = new GitHubPullRequest(2, "PR Title for branch-5", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri(), false);
gitHubOperations
.GetPullRequest("branch-5")
.Returns(prForBranch5);
Expand Down Expand Up @@ -204,12 +204,12 @@ public async Task WhenStackNameIsProvided_ButItStackDoesNotExist_Throws()
]);
stackConfig.Load().Returns(stacks);

var prForBranch3 = new GitHubPullRequest(1, "PR Title for branch-3", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri());
var prForBranch3 = new GitHubPullRequest(1, "PR Title for branch-3", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri(), false);
gitHubOperations
.GetPullRequest("branch-3")
.Returns(prForBranch3);

var prForBranch5 = new GitHubPullRequest(2, "PR Title for branch-5", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri());
var prForBranch5 = new GitHubPullRequest(2, "PR Title for branch-5", string.Empty, GitHubPullRequestStates.Open, Some.HttpsUri(), false);
gitHubOperations
.GetPullRequest("branch-5")
.Returns(prForBranch5);
Expand Down
14 changes: 7 additions & 7 deletions src/Stack.Tests/Commands/Stack/StackStatusCommandHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneHasAPullRequests_Retur
.GetStatusOfRemoteBranch("branch-5", "branch-3")
.Returns((1, 0));

var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri());
var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false);

gitHubOperations
.GetPullRequest("branch-3")
Expand Down Expand Up @@ -117,7 +117,7 @@ public async Task WhenStackNameIsProvided_DoesNotAskForStack_ReturnsStatus()
.GetStatusOfRemoteBranch("branch-5", "branch-3")
.Returns((1, 0));

var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri());
var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false);

gitHubOperations
.GetPullRequest("branch-3")
Expand Down Expand Up @@ -187,7 +187,7 @@ public async Task WhenAllStacksAreRequested_ReturnsStatusOfEachStack()
.GetStatusOfRemoteBranch("branch-4", "branch-2")
.Returns((3, 1));

var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri());
var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false);

gitHubOperations
.GetPullRequest("branch-3")
Expand Down Expand Up @@ -263,7 +263,7 @@ public async Task WhenAllStacksAreRequested_WithStacksInMultipleRepositories_Ret
.GetStatusOfRemoteBranch("branch-4", "branch-2")
.Returns((3, 1));

var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri());
var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false);

gitHubOperations
.GetPullRequest("branch-3")
Expand Down Expand Up @@ -360,7 +360,7 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneNoLongerExistsOnTheRem
.GetStatusOfRemoteBranch("branch-5", "branch-1")
.Returns((1, 0));

var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri());
var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false);

gitHubOperations
.GetPullRequest("branch-5")
Expand Down Expand Up @@ -421,7 +421,7 @@ public async Task WhenMultipleBranchesExistInAStack_AndOneNoLongerExistsOnTheRem
.GetStatusOfRemoteBranch("branch-5", "branch-1")
.Returns((1, 0));

var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri());
var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false);

gitHubOperations
.GetPullRequest("branch-5")
Expand Down Expand Up @@ -484,7 +484,7 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_ReturnsStatus()
.GetStatusOfRemoteBranch("branch-5", "branch-3")
.Returns((1, 0));

var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri());
var pr = new GitHubPullRequest(1, "PR title", "PR body", GitHubPullRequestStates.Open, Some.HttpsUri(), false);

gitHubOperations
.GetPullRequest("branch-3")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public async Task WhenABranchInTheStackExistsOnTheRemote_ButThePullRequestIsMerg
var branchesThatExistInRemote = new List<string>(["branch-1", "branch-2", "branch-3"]);

gitOperations.DoesRemoteBranchExist(Arg.Is<string>(b => branchesThatExistInRemote.Contains(b))).Returns(true);
gitHubOperations.GetPullRequest("branch-2").Returns(new GitHubPullRequest(1, Some.Name(), Some.Name(), GitHubPullRequestStates.Merged, Some.HttpsUri()));
gitHubOperations.GetPullRequest("branch-2").Returns(new GitHubPullRequest(1, Some.Name(), Some.Name(), GitHubPullRequestStates.Merged, Some.HttpsUri(), false));

// Act
await handler.Handle(new UpdateStackCommandInputs(null, false));
Expand Down
1 change: 1 addition & 0 deletions src/Stack/Commands/Helpers/Questions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ public static class Questions
public static string PullRequestTitle(string sourceBranch, string targetBranch) => $"Title for pull request from {sourceBranch.Branch()} -> {targetBranch.Branch()}:";
public const string PullRequestStackDescription = "Stack description for pull request:";
public const string OpenPullRequests = "Open the pull requests in the browser?";
public const string CreatePullRequestsAsDrafts = "Create pull requests as drafts?";
}
33 changes: 27 additions & 6 deletions src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ public class CreatePullRequestsCommandSettings : DryRunCommandSettingsBase
[Description("The name of the stack to create pull requests for.")]
[CommandOption("-n|--name")]
public string? Name { get; init; }

[Description("Create pull requests as drafts.")]
[CommandOption("--draft")]
public bool? Draft { get; init; }
}

public class CreatePullRequestsCommand : AsyncCommand<CreatePullRequestsCommandSettings>
Expand All @@ -30,15 +34,15 @@ public override async Task<int> ExecuteAsync(CommandContext context, CreatePullR
new GitHubOperations(console, settings.GetGitHubOperationSettings()),
new StackConfig());

await handler.Handle(new CreatePullRequestsCommandInputs(settings.Name));
await handler.Handle(new CreatePullRequestsCommandInputs(settings.Name, settings.Draft));

return 0;
}
}

public record CreatePullRequestsCommandInputs(string? StackName)
public record CreatePullRequestsCommandInputs(string? StackName, bool? Draft)
{
public static CreatePullRequestsCommandInputs Empty => new((string?)null);
public static CreatePullRequestsCommandInputs Empty => new(null, null);
}

public record CreatePullRequestsCommandResponse();
Expand Down Expand Up @@ -117,7 +121,18 @@ public async Task<CreatePullRequestsCommandResponse> Handle(CreatePullRequestsCo

if (inputProvider.Confirm(Questions.ConfirmCreatePullRequests))
{
CreatePullRequests(outputProvider, gitOperations, gitHubOperations, status, pullRequestCreateActions);
var draft = inputs.Draft ?? false;

if (inputs.Draft is null)
{
draft = inputProvider.Confirm(Questions.CreatePullRequestsAsDrafts, false);
}
else if (draft)
{
outputProvider.Information("Creating pull requests as drafts.");
}

CreatePullRequests(outputProvider, gitOperations, gitHubOperations, status, pullRequestCreateActions, draft);

var pullRequestsInStack = status.Branches.Values
.Where(branch => branch.HasPullRequest)
Expand Down Expand Up @@ -204,7 +219,13 @@ private static void UpdatePullRequestStackDescriptions(IInputProvider inputProvi
}
}

private static void CreatePullRequests(IOutputProvider outputProvider, IGitOperations gitOperations, IGitHubOperations gitHubOperations, StackStatus status, List<GitHubPullRequestCreateAction> pullRequestCreateActions)
private static void CreatePullRequests(
IOutputProvider outputProvider,
IGitOperations gitOperations,
IGitHubOperations gitHubOperations,
StackStatus status,
List<GitHubPullRequestCreateAction> pullRequestCreateActions,
bool draft)
{
var pullRequestTemplatePath = Path.Join(gitOperations.GetRootOfRepository(), ".github", "pull_request_template.md");
var body = string.Empty;
Expand All @@ -220,7 +241,7 @@ private static void CreatePullRequests(IOutputProvider outputProvider, IGitOpera
{
var branchDetail = status.Branches[action.HeadBranch];
outputProvider.Information($"Creating pull request for branch {action.HeadBranch.Branch()} to {action.BaseBranch.Branch()}");
var pullRequest = gitHubOperations.CreatePullRequest(action.HeadBranch, action.BaseBranch, action.Title!, body);
var pullRequest = gitHubOperations.CreatePullRequest(action.HeadBranch, action.BaseBranch, action.Title!, body, draft);

if (pullRequest is not null)
{
Expand Down
20 changes: 15 additions & 5 deletions src/Stack/Git/GitHubOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ public static class GitHubPullRequestStates
public const string Merged = "MERGED";
}

public record GitHubPullRequest(int Number, string Title, string Body, string State, Uri Url);
public record GitHubPullRequest(int Number, string Title, string Body, string State, Uri Url, bool IsDraft);

public static class GitHubPullRequestExtensionMethods
{
public static Color GetPullRequestColor(this GitHubPullRequest pullRequest)
{
if (pullRequest.IsDraft)
return Color.Grey;

return pullRequest.State switch
{
GitHubPullRequestStates.Open => Color.Green,
Expand All @@ -40,7 +43,7 @@ public static string GetPullRequestDisplay(this GitHubPullRequest pullRequest)
public interface IGitHubOperations
{
GitHubPullRequest? GetPullRequest(string branch);
GitHubPullRequest? CreatePullRequest(string headBranch, string baseBranch, string title, string body);
GitHubPullRequest? CreatePullRequest(string headBranch, string baseBranch, string title, string body, bool draft);
void EditPullRequest(int number, string body);
void OpenPullRequest(GitHubPullRequest pullRequest);
}
Expand All @@ -49,16 +52,23 @@ public class GitHubOperations(IAnsiConsole console, GitHubOperationSettings sett
{
public GitHubPullRequest? GetPullRequest(string branch)
{
var output = ExecuteGitHubCommandAndReturnOutput($"pr list --json title,number,body,state,url --head {branch} --state all");
var output = ExecuteGitHubCommandAndReturnOutput($"pr list --json title,number,body,state,url,isDraft --head {branch} --state all");
var pullRequests = System.Text.Json.JsonSerializer.Deserialize<List<GitHubPullRequest>>(output,
new System.Text.Json.JsonSerializerOptions(System.Text.Json.JsonSerializerDefaults.Web))!;

return pullRequests.FirstOrDefault();
}

public GitHubPullRequest? CreatePullRequest(string headBranch, string baseBranch, string title, string body)
public GitHubPullRequest? CreatePullRequest(string headBranch, string baseBranch, string title, string body, bool draft)
{
ExecuteGitHubCommand($"pr create --title \"{title}\" --body \"{body}\" --base {baseBranch} --head {headBranch}");
var command = $"pr create --title \"{title}\" --body \"{body}\" --base {baseBranch} --head {headBranch}";

if (draft)
{
command += " --draft";
}

ExecuteGitHubCommand(command);

if (settings.DryRun)
{
Expand Down
12 changes: 10 additions & 2 deletions src/Stack/Infrastructure/ConsoleInputProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public interface IInputProvider
string Select(string prompt, string[] choices);
T Select<T>(string prompt, T[] choices, Func<T, string>? converter = null) where T : notnull;
T SelectGrouped<T>(string prompt, ChoiceGroup<T>[] choices, Func<T, string>? converter = null) where T : notnull;
bool Confirm(string prompt);
bool Confirm(string prompt, bool defaultValue = true);
}

public class ConsoleInputProvider(IAnsiConsole console) : IInputProvider
Expand Down Expand Up @@ -65,7 +65,15 @@ public T SelectGrouped<T>(string prompt, ChoiceGroup<T>[] groups, Func<T, string
return console.Prompt(select);
}

public bool Confirm(string prompt) => console.Prompt(new ConfirmationPrompt(prompt));
public bool Confirm(string prompt, bool defaultValue = true)
{
var confirmationPrompt = new ConfirmationPrompt(prompt)
{
DefaultValue = defaultValue
};

return console.Prompt(confirmationPrompt);
}
}


Loading