diff --git a/README.md b/README.md index 6a1e02b3..36802432 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -n, --name The name of the stack. Must be unique within the repository. -s, --source-branch The source branch to use for the new stack. Defaults to the default branch for the repository. -b, --branch The name of the branch to create within the stack. @@ -191,7 +192,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. - --json Output results as JSON. + --json Write output and log messages as JSON. Log messages will be written to stderr. -?, -h, --help Show help and usage information ``` @@ -207,7 +208,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. - --json Output results as JSON. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. --all Show status of all stacks. --full Show full status including pull requests. @@ -226,6 +227,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. -y, --yes Confirm the command without prompting. -?, -h, --help Show help and usage information @@ -243,6 +245,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. -n, --name The new name for the stack. -?, -h, --help Show help and usage information @@ -262,6 +265,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. --rebase Use rebase when updating the stack. Overrides any setting in Git configuration. --merge Use merge when updating the stack. Overrides any setting in Git configuration. @@ -280,6 +284,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -b, --branch The name of the branch. -?, -h, --help Show help and usage information ``` @@ -296,6 +301,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. -y, --yes Confirm the command without prompting. -?, -h, --help Show help and usage information @@ -313,6 +319,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. -b, --branch The name of the branch. -p, --parent The name of the parent branch to put the branch under. @@ -331,6 +338,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. -b, --branch The name of the branch. -p, --parent The name of the parent branch to put the branch under. @@ -349,6 +357,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. -b, --branch The name of the branch. -y, --yes Confirm the command without prompting. @@ -371,6 +380,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. -?, -h, --help Show help and usage information ``` @@ -387,6 +397,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. --max-batch-size The maximum number of branches to process at once. [default: 5] --force-with-lease Force push changes with lease. @@ -405,6 +416,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. --max-batch-size The maximum number of branches to process at once. [default: 5] --rebase Use rebase when updating the stack. Overrides any setting in Git configuration. @@ -428,6 +440,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. -?, -h, --help Show help and usage information ``` @@ -444,6 +457,7 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -s, --stack The name of the stack. -?, -h, --help Show help and usage information ``` @@ -462,5 +476,6 @@ Options: --working-dir The path to the directory containing the git repository. Defaults to the current directory. --debug Show debug output. --verbose Show verbose output. + --json Write output and log messages as JSON. Log messages will be written to stderr. -?, -h, --help Show help and usage information ``` diff --git a/src/Stack.Tests/Commands/PullRequests/CreatePullRequestsCommandHandlerTests.cs b/src/Stack.Tests/Commands/PullRequests/CreatePullRequestsCommandHandlerTests.cs index d324239d..049d73bd 100644 --- a/src/Stack.Tests/Commands/PullRequests/CreatePullRequestsCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/PullRequests/CreatePullRequestsCommandHandlerTests.cs @@ -39,7 +39,8 @@ public async Task WhenNoPullRequestsExistForAStackWithMultipleBranches_CreatesPu .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); var fileOperations = new FileOperations(); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); @@ -52,7 +53,7 @@ public async Task WhenNoPullRequestsExistForAStackWithMultipleBranches_CreatesPu [branch1] = new GitBranchStatus(branch1, $"origin/{branch1}", true, false, 0, 0, new Commit(Some.Sha(), "msg")), [branch2] = new GitBranchStatus(branch2, $"origin/{branch2}", true, false, 0, 0, new Commit(Some.Sha(), "msg")) }); - var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, fileOperations, stackConfig); + var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, fileOperations, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); inputProvider @@ -109,7 +110,8 @@ public async Task WhenCreatingPullRequestsForAStackWithMultipleBranches_EachPull .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); var fileOperations = new FileOperations(); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); @@ -122,7 +124,7 @@ public async Task WhenCreatingPullRequestsForAStackWithMultipleBranches_EachPull [branch1] = new GitBranchStatus(branch1, $"origin/{branch1}", true, false, 0, 0, new Commit(Some.Sha(), "msg")), [branch2] = new GitBranchStatus(branch2, $"origin/{branch2}", true, false, 0, 0, new Commit(Some.Sha(), "msg")) }); - var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, fileOperations, stackConfig); + var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, fileOperations, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); @@ -170,7 +172,8 @@ public async Task WhenAPullRequestExistForABranch_AndNoneForAnotherBranch_Create .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); var fileOperations = new FileOperations(); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); @@ -183,7 +186,7 @@ public async Task WhenAPullRequestExistForABranch_AndNoneForAnotherBranch_Create [branch1] = new GitBranchStatus(branch1, $"origin/{branch1}", true, false, 0, 0, new Commit(Some.Sha(), "msg")), [branch2] = new GitBranchStatus(branch2, $"origin/{branch2}", true, false, 0, 0, new Commit(Some.Sha(), "msg")) }); - var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, fileOperations, stackConfig); + var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, fileOperations, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); inputProvider @@ -228,7 +231,8 @@ public async Task WhenStackNameIsProvided_PullRequestsAreCreatedForThatStack() .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); var fileOperations = new FileOperations(); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); @@ -241,7 +245,7 @@ public async Task WhenStackNameIsProvided_PullRequestsAreCreatedForThatStack() [branch1] = new GitBranchStatus(branch1, $"origin/{branch1}", true, false, 0, 0, new Commit(Some.Sha(), "msg")), [branch2] = new GitBranchStatus(branch2, $"origin/{branch2}", true, false, 0, 0, new Commit(Some.Sha(), "msg")) }); - var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, fileOperations, stackConfig); + var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, fileOperations, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); @@ -288,7 +292,8 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_PullRequestsAreC .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); var fileOperations = new FileOperations(); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); @@ -301,7 +306,7 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName_PullRequestsAreC [branch1] = new GitBranchStatus(branch1, $"origin/{branch1}", true, false, 0, 0, new Commit(Some.Sha(), "msg")), [branch2] = new GitBranchStatus(branch2, $"origin/{branch2}", true, false, 0, 0, new Commit(Some.Sha(), "msg")) }); - var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, fileOperations, stackConfig); + var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, fileOperations, stackConfig); inputProvider @@ -352,7 +357,8 @@ public async Task WhenStackNameIsProvided_ButTheStackDoesNotExist_Throws() .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); var fileOperations = new FileOperations(); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); @@ -365,7 +371,7 @@ public async Task WhenStackNameIsProvided_ButTheStackDoesNotExist_Throws() [branch1] = new GitBranchStatus(branch1, $"origin/{branch1}", true, false, 0, 0, new Commit(Some.Sha(), "msg")), [branch2] = new GitBranchStatus(branch2, $"origin/{branch2}", true, false, 0, 0, new Commit(Some.Sha(), "msg")) }); - var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, fileOperations, stackConfig); + var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, fileOperations, stackConfig); // Act and assert @@ -405,7 +411,8 @@ public async Task WhenAPullRequestExistForABranch_AndHasBeenMerged_AndNoneForAno .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); var fileOperations = new FileOperations(); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); @@ -418,7 +425,7 @@ public async Task WhenAPullRequestExistForABranch_AndHasBeenMerged_AndNoneForAno [branch1] = new GitBranchStatus(branch1, $"origin/{branch1}", true, false, 0, 0, new Commit(Some.Sha(), "msg")), [branch2] = new GitBranchStatus(branch2, $"origin/{branch2}", true, false, 0, 0, new Commit(Some.Sha(), "msg")) }); - var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, fileOperations, stackConfig); + var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, fileOperations, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); @@ -469,7 +476,8 @@ public async Task WhenAPullRequestTemplateExistsInTheRepo_ItIsUsedAsTheBodyOfANe .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); var fileOperations = new FileOperations(); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); @@ -483,7 +491,7 @@ public async Task WhenAPullRequestTemplateExistsInTheRepo_ItIsUsedAsTheBodyOfANe [branch1] = new GitBranchStatus(branch1, $"origin/{branch1}", true, false, 0, 0, new Commit(Some.Sha(), "msg")), [branch2] = new GitBranchStatus(branch2, $"origin/{branch2}", true, false, 0, 0, new Commit(Some.Sha(), "msg")) }); - var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, fileOperations, stackConfig); + var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, fileOperations, stackConfig); Directory.CreateDirectory(Path.Join(tempRepo.DirectoryPath, ".github")); File.WriteAllText(Path.Join(tempRepo.DirectoryPath, ".github", "PULL_REQUEST_TEMPLATE.md"), "This is the PR template"); @@ -535,7 +543,8 @@ public async Task WhenAPullRequestTemplateDoesNotExistInTheRepo_TheStackPrListMa .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); var fileOperations = new FileOperations(); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); @@ -545,7 +554,7 @@ public async Task WhenAPullRequestTemplateDoesNotExistInTheRepo_TheStackPrListMa [sourceBranch] = new GitBranchStatus(sourceBranch, $"origin/{sourceBranch}", true, false, 0, 0, new Commit(Some.Sha(), "msg")), [branch1] = new GitBranchStatus(branch1, $"origin/{branch1}", true, false, 0, 0, new Commit(Some.Sha(), "msg")) }); - var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, fileOperations, stackConfig); + var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, fileOperations, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); @@ -597,7 +606,8 @@ public async Task WhenAskedWhetherToCreateAPullRequestAsADraft_AndTheAnswerIsYes .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); var fileOperations = new FileOperations(); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); @@ -610,7 +620,7 @@ public async Task WhenAskedWhetherToCreateAPullRequestAsADraft_AndTheAnswerIsYes [branch1] = new GitBranchStatus(branch1, $"origin/{branch1}", true, false, 0, 0, new Commit(Some.Sha(), "msg")), [branch2] = new GitBranchStatus(branch2, $"origin/{branch2}", true, false, 0, 0, new Commit(Some.Sha(), "msg")) }); - var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, fileOperations, stackConfig); + var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, fileOperations, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); @@ -652,7 +662,8 @@ public async Task WhenOnlySelectingSomeBranchesToCreatePullRequestsFor_OnlyThose .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); var fileOperations = new FileOperations(); var gitClient = Substitute.For(); gitClient.GetRemoteUri().Returns(remoteUri); @@ -665,7 +676,7 @@ public async Task WhenOnlySelectingSomeBranchesToCreatePullRequestsFor_OnlyThose [branch1] = new GitBranchStatus(branch1, $"origin/{branch1}", true, false, 0, 0, new Commit(Some.Sha(), "msg")), [branch2] = new GitBranchStatus(branch2, $"origin/{branch2}", true, false, 0, 0, new Commit(Some.Sha(), "msg")) }); - var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, fileOperations, stackConfig); + var handler = new CreatePullRequestsCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, fileOperations, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); diff --git a/src/Stack.Tests/Commands/Remote/SyncStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Remote/SyncStackCommandHandlerTests.cs index 82ed1106..a0ad22ca 100644 --- a/src/Stack.Tests/Commands/Remote/SyncStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Remote/SyncStackCommandHandlerTests.cs @@ -39,8 +39,9 @@ public async Task WhenNameIsProvided_DoesNotAskForName_SyncsCorrectStack() var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -92,8 +93,9 @@ public async Task WhenNameIsProvided_ButStackDoesNotExist_Throws() var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -131,8 +133,9 @@ public async Task WhenOnASpecificBranchInTheStack_TheSameBranchIsSetAsCurrentAft var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); // We are on a specific branch in the stack gitClient.GetRemoteUri().Returns(remoteUri); @@ -181,8 +184,9 @@ public async Task WhenOnlyASingleStackExists_DoesNotAskForStackName_SyncsStack() var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -234,8 +238,9 @@ public async Task WhenRebaseIsProvided_SyncsStackUsingRebase() var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -285,8 +290,9 @@ public async Task WhenMergeIsProvided_SyncsStackUsingMerge() var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -335,8 +341,9 @@ public async Task WhenNotSpecifyingRebaseOrMerge_AndUpdateSettingIsRebase_SyncsS var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -386,8 +393,9 @@ public async Task WhenNotSpecifyingRebaseOrMerge_AndUpdateSettingIsMerge_SyncsSt var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -437,8 +445,9 @@ public async Task WhenGitConfigValueIsSetToMerge_ButRebaseIsSpecified_SyncsStack var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -488,8 +497,9 @@ public async Task WhenGitConfigValueIsSetToRebase_ButMergeIsSpecified_SyncsStack var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -539,8 +549,9 @@ public async Task WhenNotSpecifyingRebaseOrMerge_AndNoUpdateSettingExists_AndMer var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -591,8 +602,9 @@ public async Task WhenNotSpecifyingRebaseOrMerge_AndNoUpdateSettingsExists_AndRe var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -628,8 +640,9 @@ public async Task WhenBothRebaseAndMergeAreSpecified_AnErrorIsThrown() var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); // Act and assert await handler @@ -664,8 +677,9 @@ public async Task WhenConfirmOptionIsProvided_DoesNotAskForConfirmation() var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); @@ -716,8 +730,9 @@ public async Task WhenNoPushOptionIsProvided_DoesNotPushChangesToRemote() var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); var stackActions = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new SyncStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig, stackActions); + var displayProvider = new TestDisplayProvider(testOutputHelper); + var outputProvider = Substitute.For(); + var handler = new SyncStackCommandHandler(inputProvider, logger, outputProvider, displayProvider, gitClient, gitHubClient, stackConfig, stackActions); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(branch1); diff --git a/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs index 9cd86e9a..a1057c57 100644 --- a/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/CleanupStackCommandHandlerTests.cs @@ -36,10 +36,9 @@ public async Task WhenBranchExistsLocally_ButHasNotBeenPushedToTheRemote_BranchI .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); - var handler = new CleanupStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new CleanupStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); @@ -86,10 +85,9 @@ public async Task WhenBranchExistsLocally_AndHasBeenDeletedFromTheRemote_BranchI .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); - var handler = new CleanupStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new CleanupStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); @@ -136,10 +134,9 @@ public async Task WhenBranchExistsLocally_AndInRemote_BranchIsNotDeletedLocally( .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); - var handler = new CleanupStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new CleanupStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); @@ -189,8 +186,7 @@ public async Task WhenConfirmationIsFalse_DoesNotDeleteAnyBranches() var logger = XUnitLogger.CreateLogger(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new CleanupStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new CleanupStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); @@ -239,8 +235,7 @@ public async Task WhenStackNameIsProvided_ItIsNotAskedFor() var logger = XUnitLogger.CreateLogger(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new CleanupStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new CleanupStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); @@ -288,8 +283,7 @@ public async Task WhenStackNameIsProvided_ButStackDoesNotExist_Throws() var logger = XUnitLogger.CreateLogger(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new CleanupStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new CleanupStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); @@ -325,8 +319,7 @@ public async Task WhenOnlyASingleStackExists_StackIsSelectedAutomatically() var logger = XUnitLogger.CreateLogger(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new CleanupStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new CleanupStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); @@ -374,8 +367,7 @@ public async Task WhenConfirmIsProvided_DoesNotAskForConfirmation_DeletesBranche var logger = XUnitLogger.CreateLogger(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new CleanupStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new CleanupStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); @@ -425,8 +417,7 @@ public async Task WhenChildBranchExistsLocally_AndHasBeenDeletedFromTheRemote_Br var logger = XUnitLogger.CreateLogger(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); - var console = new TestDisplayProvider(testOutputHelper); - var handler = new CleanupStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new CleanupStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); gitClient.GetRemoteUri().Returns(remoteUri); gitClient.GetCurrentBranch().Returns(sourceBranch); diff --git a/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs b/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs index 8750a3b4..28e5da56 100644 --- a/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs +++ b/src/Stack.Tests/Commands/Stack/DeleteStackCommandHandlerTests.cs @@ -33,7 +33,6 @@ public async Task WhenNoInputsAreProvided_AsksForName_AndConfirmation_AndDeletes var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); @@ -41,7 +40,7 @@ public async Task WhenNoInputsAreProvided_AsksForName_AndConfirmation_AndDeletes gitClient.GetCurrentBranch().Returns(sourceBranch); gitClient.GetBranchStatuses(Arg.Any()).Returns(ci => CreateBranchStatuses(ci.Arg(), sourceBranch)); - var handler = new DeleteStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new DeleteStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); inputProvider.Confirm(Questions.ConfirmDeleteStack, Arg.Any()).Returns(true); @@ -75,7 +74,6 @@ public async Task WhenConfirmationIsFalse_DoesNotDeleteStack() .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); @@ -83,7 +81,7 @@ public async Task WhenConfirmationIsFalse_DoesNotDeleteStack() gitClient.GetCurrentBranch().Returns(sourceBranch); gitClient.GetBranchStatuses(Arg.Any()).Returns(ci => CreateBranchStatuses(ci.Arg(), sourceBranch)); - var handler = new DeleteStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new DeleteStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); inputProvider.Confirm(Questions.ConfirmDeleteStack, Arg.Any()).Returns(false); @@ -118,7 +116,6 @@ public async Task WhenNameIsProvided_AsksForConfirmation_AndDeletesStack() .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); @@ -126,7 +123,7 @@ public async Task WhenNameIsProvided_AsksForConfirmation_AndDeletesStack() gitClient.GetCurrentBranch().Returns(sourceBranch); gitClient.GetBranchStatuses(Arg.Any()).Returns(ci => CreateBranchStatuses(ci.Arg(), sourceBranch)); - var handler = new DeleteStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new DeleteStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); inputProvider.Confirm(Questions.ConfirmDeleteStack, Arg.Any()).Returns(true); @@ -161,7 +158,6 @@ public async Task WhenStackDoesNotExist_Throws() .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); @@ -169,7 +165,7 @@ public async Task WhenStackDoesNotExist_Throws() gitClient.GetCurrentBranch().Returns(sourceBranch); gitClient.GetBranchStatuses(Arg.Any()).Returns(ci => CreateBranchStatuses(ci.Arg(), sourceBranch)); - var handler = new DeleteStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new DeleteStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); inputProvider.Confirm(Questions.ConfirmDeleteStack, Arg.Any()).Returns(true); @@ -203,7 +199,6 @@ public async Task WhenThereAreLocalBranchesThatAreDeletedInTheRemote_AsksToClean .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); @@ -228,7 +223,7 @@ public async Task WhenThereAreLocalBranchesThatAreDeletedInTheRemote_AsksToClean return dict; }); - var handler = new DeleteStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new DeleteStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); inputProvider.Confirm(Questions.ConfirmDeleteStack, Arg.Any()).Returns(true); @@ -261,7 +256,6 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName() .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); @@ -269,7 +263,7 @@ public async Task WhenOnlyOneStackExists_DoesNotAskForStackName() gitClient.GetCurrentBranch().Returns(sourceBranch); gitClient.GetBranchStatuses(Arg.Any()).Returns(ci => CreateBranchStatuses(ci.Arg(), sourceBranch)); - var handler = new DeleteStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new DeleteStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); inputProvider.Confirm(Questions.ConfirmDeleteStack, Arg.Any()).Returns(true); @@ -301,7 +295,6 @@ public async Task WhenConfirmIsProvided_DoesNotAskForConfirmation_DeletesStack() .Build(); var inputProvider = Substitute.For(); var logger = XUnitLogger.CreateLogger(testOutputHelper); - var console = new TestDisplayProvider(testOutputHelper); var gitClient = Substitute.For(); var gitHubClient = Substitute.For(); @@ -309,7 +302,7 @@ public async Task WhenConfirmIsProvided_DoesNotAskForConfirmation_DeletesStack() gitClient.GetCurrentBranch().Returns(sourceBranch); gitClient.GetBranchStatuses(Arg.Any()).Returns(ci => CreateBranchStatuses(ci.Arg(), sourceBranch)); - var handler = new DeleteStackCommandHandler(inputProvider, logger, console, gitClient, gitHubClient, stackConfig); + var handler = new DeleteStackCommandHandler(inputProvider, logger, gitClient, gitHubClient, stackConfig); inputProvider.Select(Questions.SelectStack, Arg.Any(), Arg.Any()).Returns("Stack1"); diff --git a/src/Stack.Tests/Helpers/TestDisplayProvider.cs b/src/Stack.Tests/Helpers/TestDisplayProvider.cs index 3109d113..661a0f22 100644 --- a/src/Stack.Tests/Helpers/TestDisplayProvider.cs +++ b/src/Stack.Tests/Helpers/TestDisplayProvider.cs @@ -46,4 +46,10 @@ public async Task DisplayHeader(string header, CancellationToken cancellationTok await Task.CompletedTask; testOutputHelper.WriteLine($"HEADER: {header}"); } + + public async Task DisplaySuccess(string message, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + testOutputHelper.WriteLine($"SUCCESS: {message}"); + } } diff --git a/src/Stack/Commands/Branch/AddBranchCommand.cs b/src/Stack/Commands/Branch/AddBranchCommand.cs index f5df0d79..19047763 100644 --- a/src/Stack/Commands/Branch/AddBranchCommand.cs +++ b/src/Stack/Commands/Branch/AddBranchCommand.cs @@ -13,12 +13,12 @@ public class AddBranchCommand : Command private readonly AddBranchCommandHandler handler; public AddBranchCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + AddBranchCommandHandler handler, CliExecutionContext executionContext, - AddBranchCommandHandler handler) - : base("add", "Add an existing branch to a stack.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("add", "Add an existing branch to a stack.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); diff --git a/src/Stack/Commands/Branch/NewBranchCommand.cs b/src/Stack/Commands/Branch/NewBranchCommand.cs index b8af6098..5d820eeb 100644 --- a/src/Stack/Commands/Branch/NewBranchCommand.cs +++ b/src/Stack/Commands/Branch/NewBranchCommand.cs @@ -15,12 +15,12 @@ public class NewBranchCommand : Command private readonly NewBranchCommandHandler handler; public NewBranchCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + NewBranchCommandHandler handler, CliExecutionContext executionContext, - NewBranchCommandHandler handler) - : base("new", "Create a new branch in a stack.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("new", "Create a new branch in a stack.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); diff --git a/src/Stack/Commands/Branch/RemoveBranchCommand.cs b/src/Stack/Commands/Branch/RemoveBranchCommand.cs index 169e99ac..64112a74 100644 --- a/src/Stack/Commands/Branch/RemoveBranchCommand.cs +++ b/src/Stack/Commands/Branch/RemoveBranchCommand.cs @@ -25,12 +25,12 @@ public class RemoveBranchCommand : Command private readonly RemoveBranchCommandHandler handler; public RemoveBranchCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + RemoveBranchCommandHandler handler, CliExecutionContext executionContext, - RemoveBranchCommandHandler handler) - : base("remove", "Remove a branch from a stack.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("remove", "Remove a branch from a stack.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); diff --git a/src/Stack/Commands/Config/OpenConfigCommand.cs b/src/Stack/Commands/Config/OpenConfigCommand.cs index 6179a371..fe39fb9f 100644 --- a/src/Stack/Commands/Config/OpenConfigCommand.cs +++ b/src/Stack/Commands/Config/OpenConfigCommand.cs @@ -12,12 +12,12 @@ public class OpenConfigCommand : Command readonly IStackConfig stackConfig; public OpenConfigCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + IStackConfig stackConfig, CliExecutionContext executionContext, - IStackConfig stackConfig) - : base("open", "Open the configuration file in the default editor.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("open", "Open the configuration file in the default editor.", executionContext, inputProvider, outputProvider, logger) { this.stackConfig = stackConfig; } diff --git a/src/Stack/Commands/Helpers/StackActions.cs b/src/Stack/Commands/Helpers/StackActions.cs index 01d4fbe3..12feda13 100644 --- a/src/Stack/Commands/Helpers/StackActions.cs +++ b/src/Stack/Commands/Helpers/StackActions.cs @@ -116,7 +116,6 @@ public async Task UpdateStack(Config.Stack stack, UpdateStrategy strategy, Cance stack, currentBranch, logger, - displayProvider, gitClient, gitHubClient, true); diff --git a/src/Stack/Commands/Helpers/StackHelpers.cs b/src/Stack/Commands/Helpers/StackHelpers.cs index 08b36d9a..7c567cab 100644 --- a/src/Stack/Commands/Helpers/StackHelpers.cs +++ b/src/Stack/Commands/Helpers/StackHelpers.cs @@ -105,7 +105,6 @@ public static List GetStackStatus( List stacks, string currentBranch, ILogger logger, - IDisplayProvider displayProvider, IGitClient gitClient, IGitHubClient gitHubClient, bool includePullRequestStatus = true) @@ -218,7 +217,6 @@ public static StackStatus GetStackStatus( Config.Stack stack, string currentBranch, ILogger logger, - IDisplayProvider displayProvider, IGitClient gitClient, IGitHubClient gitHubClient, bool includePullRequestStatus = true) @@ -227,7 +225,6 @@ public static StackStatus GetStackStatus( [stack], currentBranch, logger, - displayProvider, gitClient, gitHubClient, includePullRequestStatus); @@ -237,19 +234,18 @@ public static StackStatus GetStackStatus( public static async Task OutputStackStatus( List statuses, - IDisplayProvider displayProvider, + IOutputProvider outputProvider, CancellationToken cancellationToken) { foreach (var status in statuses) { - await OutputStackStatus(status, displayProvider, cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); + await OutputStackStatus(status, outputProvider, cancellationToken); } } public static async Task OutputStackStatus( StackStatus status, - IDisplayProvider displayProvider, + IOutputProvider outputProvider, CancellationToken cancellationToken, Func? getBranchPullRequestDisplay = null) { @@ -261,8 +257,10 @@ public static async Task OutputStackStatus( items.Add(GetBranchAndPullRequestStatusOutput(branch, getBranchPullRequestDisplay)); } - await displayProvider.DisplayMessage(status.Name.Stack(), cancellationToken); - await displayProvider.DisplayTree(header, [.. items], cancellationToken: cancellationToken); + var tree = RenderingHelpers.RenderTree(header, items); + + await outputProvider.WriteMessage(status.Name.Stack(), cancellationToken); + await outputProvider.WriteLine(tree, cancellationToken); } public static TreeItem GetBranchAndPullRequestStatusOutput( @@ -427,48 +425,48 @@ public static string GetBranchStatusOutput(BranchDetail branch) public static async Task OutputBranchAndStackActions( StackStatus status, - IDisplayProvider displayProvider, + IOutputProvider outputProvider, CancellationToken cancellationToken) { var allBranches = status.GetAllBranches(); if (allBranches.All(branch => branch.CouldBeCleanedUp)) { - await displayProvider.DisplayMessage("All branches exist locally but are either not in the remote repository or the pull request associated with the branch is no longer open. This stack might be able to be deleted.", cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); - await displayProvider.DisplayMessage($"Run {$"stack delete --stack \"{status.Name}\"".Example()} to delete the stack if it's no longer needed.", cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); + await outputProvider.WriteMessage("All branches exist locally but are either not in the remote repository or the pull request associated with the branch is no longer open. This stack might be able to be deleted.", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); + await outputProvider.WriteMessage($"Run {$"stack delete --stack \"{status.Name}\"".Example()} to delete the stack if it's no longer needed.", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); } else if (allBranches.Any(branch => branch.CouldBeCleanedUp)) { - await displayProvider.DisplayMessage("Some branches exist locally but are either not in the remote repository or the pull request associated with the branch is no longer open.", cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); - await displayProvider.DisplayMessage($"Run {$"stack cleanup --stack \"{status.Name}\"".Example()} to clean up the stack if it's no longer needed.", cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); + await outputProvider.WriteMessage("Some branches exist locally but are either not in the remote repository or the pull request associated with the branch is no longer open.", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); + await outputProvider.WriteMessage($"Run {$"stack cleanup --stack \"{status.Name}\"".Example()} to clean up the stack if it's no longer needed.", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); } else if (allBranches.All(branch => !branch.Exists)) { - await displayProvider.DisplayMessage("No branches exist locally. This stack might be able to be deleted.", cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); - await displayProvider.DisplayMessage($"Run {$"stack delete --stack \"{status.Name}\"".Example()} to delete the stack if it's no longer needed.", cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); + await outputProvider.WriteMessage("No branches exist locally. This stack might be able to be deleted.", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); + await outputProvider.WriteMessage($"Run {$"stack delete --stack \"{status.Name}\"".Example()} to delete the stack if it's no longer needed.", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); } if (allBranches.Any(branch => branch.Exists && (branch.RemoteTrackingBranch is null || branch.RemoteTrackingBranch.Ahead > 0))) { - await displayProvider.DisplayMessage("There are changes in local branches that have not been pushed to the remote repository.", cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); - await displayProvider.DisplayMessage($"Run {$"stack push --stack \"{status.Name}\"".Example()} to push the changes to the remote repository.", cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); + await outputProvider.WriteMessage("There are changes in local branches that have not been pushed to the remote repository.", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); + await outputProvider.WriteMessage($"Run {$"stack push --stack \"{status.Name}\"".Example()} to push the changes to the remote repository.", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); } if (allBranches.Any(branch => branch.Exists && branch.RemoteTrackingBranch is not null && branch.RemoteTrackingBranch.Behind > 0)) { - await displayProvider.DisplayMessage("There are changes in source branches that have not been applied to the stack.", cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); - await displayProvider.DisplayMessage($"Run {$"stack update --stack \"{status.Name}\"".Example()} to update the stack locally.", cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); - await displayProvider.DisplayMessage($"Run {$"stack sync --stack \"{status.Name}\"".Example()} to sync the stack with the remote repository.", cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); + await outputProvider.WriteMessage("There are changes in source branches that have not been applied to the stack.", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); + await outputProvider.WriteMessage($"Run {$"stack update --stack \"{status.Name}\"".Example()} to update the stack locally.", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); + await outputProvider.WriteMessage($"Run {$"stack sync --stack \"{status.Name}\"".Example()} to sync the stack with the remote repository.", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); } } @@ -520,10 +518,10 @@ public static async Task GetUpdateStrategy( return strategy; } - public static string[] GetBranchesNeedingCleanup(Config.Stack stack, ILogger logger, IDisplayProvider displayProvider, IGitClient gitClient, IGitHubClient gitHubClient) + public static string[] GetBranchesNeedingCleanup(Config.Stack stack, ILogger logger, IGitClient gitClient, IGitHubClient gitHubClient) { var currentBranch = gitClient.GetCurrentBranch(); - var stackStatus = GetStackStatus(stack, currentBranch, logger, displayProvider, gitClient, gitHubClient, true); + var stackStatus = GetStackStatus(stack, currentBranch, logger, gitClient, gitHubClient, true); return [.. stackStatus.GetAllBranches().Where(b => b.CouldBeCleanedUp).Select(b => b.Name)]; } diff --git a/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs b/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs index 0ef2d55e..1f67b6db 100644 --- a/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs +++ b/src/Stack/Commands/PullRequests/CreatePullRequestsCommand.cs @@ -15,12 +15,12 @@ public class CreatePullRequestsCommand : Command private readonly CreatePullRequestsCommandHandler handler; public CreatePullRequestsCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + CreatePullRequestsCommandHandler handler, CliExecutionContext executionContext, - CreatePullRequestsCommandHandler handler) - : base("create", "Create pull requests for a stack.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("create", "Create pull requests for a stack.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); @@ -43,6 +43,7 @@ public record CreatePullRequestsCommandInputs(string? Stack) public class CreatePullRequestsCommandHandler( IInputProvider inputProvider, ILogger logger, + IOutputProvider outputProvider, IDisplayProvider displayProvider, IGitClient gitClient, IGitHubClient gitHubClient, @@ -78,7 +79,6 @@ public override async Task Handle(CreatePullRequestsCommandInputs inputs, Cancel stack, currentBranch, logger, - displayProvider, gitClient, gitHubClient); @@ -104,9 +104,9 @@ public override async Task Handle(CreatePullRequestsCommandInputs inputs, Cancel } } - await StackHelpers.OutputStackStatus(status, displayProvider, cancellationToken); + await StackHelpers.OutputStackStatus(status, outputProvider, cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); if (pullRequestCreateActions.Count > 0) { @@ -124,22 +124,23 @@ public override async Task Handle(CreatePullRequestsCommandInputs inputs, Cancel logger.PullRequestSelected(action.Branch, action.BaseBranch); } - await displayProvider.DisplayNewLine(cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); var pullRequestInformation = await GetPullRequestInformation( inputProvider, logger, + outputProvider, displayProvider, gitClient, fileOperations, selectedPullRequestActions, cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); - await OutputUpdatedStackStatus(logger, displayProvider, stack, status, pullRequestInformation, cancellationToken); + await OutputUpdatedStackStatus(outputProvider, status, pullRequestInformation, cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); if (await inputProvider.Confirm(Questions.ConfirmCreatePullRequests, cancellationToken)) { @@ -199,16 +200,14 @@ private static List CreatePullRequests( } private static async Task OutputUpdatedStackStatus( - ILogger logger, - IDisplayProvider displayProvider, - Config.Stack stack, + IOutputProvider outputProvider, StackStatus status, List pullRequestInformation, CancellationToken cancellationToken) { await StackHelpers.OutputStackStatus( status, - displayProvider, + outputProvider, cancellationToken, (branch) => { @@ -226,6 +225,7 @@ await StackHelpers.OutputStackStatus( private static async Task> GetPullRequestInformation( IInputProvider inputProvider, ILogger logger, + IOutputProvider outputProvider, IDisplayProvider displayProvider, IGitClient gitClient, IFileOperations fileOperations, @@ -246,8 +246,8 @@ private static async Task> GetPullRequestInformatio foreach (var action in pullRequestCreateActions) { - await displayProvider.DisplayNewLine(cancellationToken); - await displayProvider.DisplayHeader($"New pull request from {action.Branch.Branch()} -> {action.BaseBranch.Branch()}", cancellationToken); + await outputProvider.WriteNewLine(cancellationToken); + await outputProvider.WriteHeader($"New pull request from {action.Branch.Branch()} -> {action.BaseBranch.Branch()}", cancellationToken); var title = inputProvider.Text(Questions.PullRequestTitle, cancellationToken).Result; var bodyFilePath = Path.Join(fileOperations.GetTempPath(), $"stack-pr-{Guid.NewGuid():N}.md"); diff --git a/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs b/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs index eb67fde9..b2d43166 100644 --- a/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs +++ b/src/Stack/Commands/PullRequests/OpenPullRequestsCommand.cs @@ -13,12 +13,12 @@ public class OpenPullRequestsCommand : Command private readonly OpenPullRequestsCommandHandler handler; public OpenPullRequestsCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + OpenPullRequestsCommandHandler handler, CliExecutionContext executionContext, - OpenPullRequestsCommandHandler handler) - : base("open", "Open pull requests for a stack in the default browser.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("open", "Open pull requests for a stack in the default browser.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); diff --git a/src/Stack/Commands/Remote/PullStackCommand.cs b/src/Stack/Commands/Remote/PullStackCommand.cs index 73cb6efa..f17e9b12 100644 --- a/src/Stack/Commands/Remote/PullStackCommand.cs +++ b/src/Stack/Commands/Remote/PullStackCommand.cs @@ -13,12 +13,12 @@ public class PullStackCommand : Command private readonly PullStackCommandHandler handler; public PullStackCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + PullStackCommandHandler handler, CliExecutionContext executionContext, - PullStackCommandHandler handler) - : base("pull", "Pull changes from the remote repository for a stack.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("pull", "Pull changes from the remote repository for a stack.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); diff --git a/src/Stack/Commands/Remote/PushStackCommand.cs b/src/Stack/Commands/Remote/PushStackCommand.cs index 621f63dd..21f256eb 100644 --- a/src/Stack/Commands/Remote/PushStackCommand.cs +++ b/src/Stack/Commands/Remote/PushStackCommand.cs @@ -18,12 +18,12 @@ public class PushStackCommand : Command private readonly PushStackCommandHandler handler; public PushStackCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + PushStackCommandHandler handler, CliExecutionContext executionContext, - PushStackCommandHandler handler) - : base("push", "Push changes to the remote repository for a stack.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("push", "Push changes to the remote repository for a stack.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); diff --git a/src/Stack/Commands/Remote/SyncStackCommand.cs b/src/Stack/Commands/Remote/SyncStackCommand.cs index 5c2d2df3..d91df50f 100644 --- a/src/Stack/Commands/Remote/SyncStackCommand.cs +++ b/src/Stack/Commands/Remote/SyncStackCommand.cs @@ -19,12 +19,12 @@ public class SyncStackCommand : Command private readonly SyncStackCommandHandler handler; public SyncStackCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + SyncStackCommandHandler handler, CliExecutionContext executionContext, - SyncStackCommandHandler handler) - : base("sync", "Sync a stack with the remote repository. Shortcut for `git fetch --prune`, `stack pull`, `stack update` and `stack push`.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("sync", "Sync a stack with the remote repository. Shortcut for `git fetch --prune`, `stack pull`, `stack update` and `stack push`.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); @@ -63,6 +63,7 @@ public record SyncStackCommandInputs( public class SyncStackCommandHandler( IInputProvider inputProvider, ILogger logger, + IOutputProvider outputProvider, IDisplayProvider displayProvider, IGitClient gitClient, IGitHubClient gitHubClient, @@ -103,21 +104,19 @@ await displayProvider.DisplayStatusWithSuccess("Fetching changes from remote rep if (!inputs.Confirm) { - await displayProvider.DisplayStatus("Checking stack status...", async (ct) => + var status = await displayProvider.DisplayStatus("Checking stack status...", async (ct) => { - var status = StackHelpers.GetStackStatus( + await Task.CompletedTask; + return StackHelpers.GetStackStatus( stack, currentBranch, logger, - displayProvider, gitClient, gitHubClient, true); - - await StackHelpers.OutputStackStatus(status, displayProvider, cancellationToken); }, cancellationToken); - await displayProvider.DisplayNewLine(cancellationToken); + await StackHelpers.OutputStackStatus(status, outputProvider, cancellationToken); if (!await inputProvider.Confirm(Questions.ConfirmSyncStack, cancellationToken)) { diff --git a/src/Stack/Commands/Stack/CleanupStackCommand.cs b/src/Stack/Commands/Stack/CleanupStackCommand.cs index 00a6b2c7..a4103734 100644 --- a/src/Stack/Commands/Stack/CleanupStackCommand.cs +++ b/src/Stack/Commands/Stack/CleanupStackCommand.cs @@ -13,12 +13,12 @@ public class CleanupStackCommand : Command private readonly CleanupStackCommandHandler handler; public CleanupStackCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + CleanupStackCommandHandler handler, CliExecutionContext executionContext, - CleanupStackCommandHandler handler) - : base("cleanup", "Clean up branches in a stack that are no longer needed.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("cleanup", "Clean up branches in a stack that are no longer needed.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); @@ -43,7 +43,6 @@ public record CleanupStackCommandInputs(string? Stack, bool Confirm) public class CleanupStackCommandHandler( IInputProvider inputProvider, ILogger logger, - IDisplayProvider displayProvider, IGitClient gitClient, IGitHubClient gitHubClient, IStackConfig stackConfig) @@ -66,7 +65,7 @@ public override async Task Handle(CleanupStackCommandInputs inputs, Cancellation throw new InvalidOperationException($"Stack '{inputs.Stack}' not found."); } - var branchesToCleanUp = StackHelpers.GetBranchesNeedingCleanup(stack, logger, displayProvider, gitClient, gitHubClient); + var branchesToCleanUp = StackHelpers.GetBranchesNeedingCleanup(stack, logger, gitClient, gitHubClient); if (branchesToCleanUp.Length == 0) { diff --git a/src/Stack/Commands/Stack/DeleteStackCommand.cs b/src/Stack/Commands/Stack/DeleteStackCommand.cs index 6db441b2..6d5bdb21 100644 --- a/src/Stack/Commands/Stack/DeleteStackCommand.cs +++ b/src/Stack/Commands/Stack/DeleteStackCommand.cs @@ -14,12 +14,12 @@ public class DeleteStackCommand : Command private readonly DeleteStackCommandHandler handler; public DeleteStackCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + DeleteStackCommandHandler handler, CliExecutionContext executionContext, - DeleteStackCommandHandler handler) - : base("delete", "Delete a stack.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("delete", "Delete a stack.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); @@ -46,7 +46,6 @@ public record DeleteStackCommandResponse(string? DeletedStackName); public class DeleteStackCommandHandler( IInputProvider inputProvider, ILogger logger, - IDisplayProvider displayProvider, IGitClient gitClient, IGitHubClient gitHubClient, IStackConfig stackConfig) @@ -71,7 +70,7 @@ public override async Task Handle(DeleteStackCommandInputs inputs, CancellationT if (inputs.Confirm || await inputProvider.Confirm(Questions.ConfirmDeleteStack, cancellationToken)) { - var branchesNeedingCleanup = StackHelpers.GetBranchesNeedingCleanup(stack, logger, displayProvider, gitClient, gitHubClient); + var branchesNeedingCleanup = StackHelpers.GetBranchesNeedingCleanup(stack, logger, gitClient, gitHubClient); if (branchesNeedingCleanup.Length > 0) { diff --git a/src/Stack/Commands/Stack/ListStacksCommand.cs b/src/Stack/Commands/Stack/ListStacksCommand.cs index a6a47cc8..76a2bffb 100644 --- a/src/Stack/Commands/Stack/ListStacksCommand.cs +++ b/src/Stack/Commands/Stack/ListStacksCommand.cs @@ -22,12 +22,12 @@ public class ListStacksCommand : CommandWithOutput private readonly ListStacksCommandHandler handler; public ListStacksCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + ListStacksCommandHandler handler, CliExecutionContext executionContext, - ListStacksCommandHandler handler) - : base("list", "List stacks.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("list", "List stacks.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; } @@ -47,7 +47,7 @@ protected override async Task WriteDefaultOutput(ListStacksCommandResponse respo foreach (var stack in response.Stacks) { - await DisplayProvider.DisplayMessage($"{stack.Name.Stack()} {$"({stack.SourceBranch})".Muted()} {stack.BranchCount} {(stack.BranchCount == 1 ? "branch" : "branches")}", cancellationToken); + await OutputProvider.WriteMessage($"{stack.Name.Stack()} {$"({stack.SourceBranch})".Muted()} {stack.BranchCount} {(stack.BranchCount == 1 ? "branch" : "branches")}", cancellationToken); } } diff --git a/src/Stack/Commands/Stack/NewStackCommand.cs b/src/Stack/Commands/Stack/NewStackCommand.cs index 31a525db..db92cee2 100644 --- a/src/Stack/Commands/Stack/NewStackCommand.cs +++ b/src/Stack/Commands/Stack/NewStackCommand.cs @@ -43,12 +43,12 @@ public class NewStackCommand : Command private readonly NewStackCommandHandler handler; public NewStackCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + NewStackCommandHandler handler, CliExecutionContext executionContext, - NewStackCommandHandler handler) - : base("new", "Create a new stack.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("new", "Create a new stack.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(StackName); diff --git a/src/Stack/Commands/Stack/RenameStackCommand.cs b/src/Stack/Commands/Stack/RenameStackCommand.cs index d9abf7d9..d54e2f2f 100644 --- a/src/Stack/Commands/Stack/RenameStackCommand.cs +++ b/src/Stack/Commands/Stack/RenameStackCommand.cs @@ -15,16 +15,16 @@ public class RenameStackCommand : Command Description = "The new name for the stack.", Required = false }; - + private readonly RenameStackCommandHandler handler; public RenameStackCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + RenameStackCommandHandler handler, CliExecutionContext executionContext, - RenameStackCommandHandler handler) - : base("rename", "Rename a stack.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("rename", "Rename a stack.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); @@ -80,8 +80,8 @@ public override async Task Handle(RenameStackCommandInputs inputs, CancellationT var newName = await inputProvider.Text(logger, Questions.StackName, inputs.Name, cancellationToken); // Validate that there's not another stack with the same name for the same remote - var existingStackWithSameName = stacksForRemote.FirstOrDefault(s => - s.Name.Equals(newName, StringComparison.OrdinalIgnoreCase) && + var existingStackWithSameName = stacksForRemote.FirstOrDefault(s => + s.Name.Equals(newName, StringComparison.OrdinalIgnoreCase) && !s.Name.Equals(stack.Name, StringComparison.OrdinalIgnoreCase)); if (existingStackWithSameName is not null) @@ -90,11 +90,11 @@ public override async Task Handle(RenameStackCommandInputs inputs, CancellationT } var renamedStack = stack.ChangeName(newName); - + // Update the stack in the collection var stackIndex = stackData.Stacks.IndexOf(stack); stackData.Stacks[stackIndex] = renamedStack; - + stackConfig.Save(stackData); logger.StackRenamed(stack.Name, newName); diff --git a/src/Stack/Commands/Stack/StackStatusCommand.cs b/src/Stack/Commands/Stack/StackStatusCommand.cs index b8f4cef0..f7c1b1be 100644 --- a/src/Stack/Commands/Stack/StackStatusCommand.cs +++ b/src/Stack/Commands/Stack/StackStatusCommand.cs @@ -96,12 +96,12 @@ public class StackStatusCommand : CommandWithOutput private readonly StackStatusCommandHandler handler; public StackStatusCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + StackStatusCommandHandler handler, CliExecutionContext executionContext, - StackStatusCommandHandler handler) - : base("status", "Show the status of the current stack or all stacks in the repository.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("status", "Show the status of the current stack or all stacks in the repository.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); @@ -121,12 +121,12 @@ protected override async Task ExecuteAndReturnRespon protected override async Task WriteDefaultOutput(StackStatusCommandResponse response, CancellationToken cancellationToken) { - await StackHelpers.OutputStackStatus(response.Stacks, DisplayProvider, cancellationToken); + await StackHelpers.OutputStackStatus(response.Stacks, OutputProvider, cancellationToken); if (response.Stacks.Count == 1) { var stack = response.Stacks.First(); - await StackHelpers.OutputBranchAndStackActions(stack, DisplayProvider, cancellationToken); + await StackHelpers.OutputBranchAndStackActions(stack, OutputProvider, cancellationToken); } } @@ -134,7 +134,7 @@ protected override async Task WriteJsonOutput(StackStatusCommandResponse respons { var output = response.Stacks.Select(MapToJsonOutput).ToList(); var json = JsonSerializer.Serialize(output, typeof(List), StackStatusCommandJsonSerializerContext.Default); - await StdOut.WriteLineAsync(json.AsMemory(), cancellationToken); + await OutputProvider.WriteLine(json, cancellationToken); } private static StackStatusCommandJsonOutput MapToJsonOutput(StackStatus stack) @@ -251,7 +251,6 @@ public override async Task Handle(StackStatusCommand stacksToCheckStatusFor, currentBranch, logger, - displayProvider, gitClient, gitHubClient, inputs.Full); diff --git a/src/Stack/Commands/Stack/StackSwitchCommand.cs b/src/Stack/Commands/Stack/StackSwitchCommand.cs index 161b455b..d8972d06 100644 --- a/src/Stack/Commands/Stack/StackSwitchCommand.cs +++ b/src/Stack/Commands/Stack/StackSwitchCommand.cs @@ -14,12 +14,12 @@ public class StackSwitchCommand : Command private readonly StackSwitchCommandHandler handler; public StackSwitchCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + StackSwitchCommandHandler handler, CliExecutionContext executionContext, - StackSwitchCommandHandler handler) - : base("switch", "Switch to a branch in a stack.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("switch", "Switch to a branch in a stack.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Branch); diff --git a/src/Stack/Commands/Stack/UpdateStackCommand.cs b/src/Stack/Commands/Stack/UpdateStackCommand.cs index 3a35c2a5..fe249a06 100644 --- a/src/Stack/Commands/Stack/UpdateStackCommand.cs +++ b/src/Stack/Commands/Stack/UpdateStackCommand.cs @@ -13,12 +13,12 @@ public class UpdateStackCommand : Command private readonly UpdateStackCommandHandler handler; public UpdateStackCommand( - ILogger logger, - IDisplayProvider displayProvider, - IInputProvider inputProvider, + UpdateStackCommandHandler handler, CliExecutionContext executionContext, - UpdateStackCommandHandler handler) - : base("update", "Update the branches in a stack.", logger, displayProvider, inputProvider, executionContext) + IInputProvider inputProvider, + IOutputProvider outputProvider, + ILogger logger) + : base("update", "Update the branches in a stack.", executionContext, inputProvider, outputProvider, logger) { this.handler = handler; Add(CommonOptions.Stack); diff --git a/src/Stack/Infrastructure/Commands/Command.cs b/src/Stack/Infrastructure/Commands/Command.cs index 801cd5d0..04d3092c 100644 --- a/src/Stack/Infrastructure/Commands/Command.cs +++ b/src/Stack/Infrastructure/Commands/Command.cs @@ -11,25 +11,26 @@ public abstract class Command : System.CommandLine.Command { protected ILogger Logger; protected IInputProvider InputProvider; - protected IDisplayProvider DisplayProvider; + protected IOutputProvider OutputProvider; protected string? WorkingDirectory; protected bool Verbose; public Command( string name, string? description, - ILogger logger, - IDisplayProvider displayProvider, + CliExecutionContext executionContext, IInputProvider inputProvider, - CliExecutionContext executionContext) : base(name, description) + IOutputProvider outputProvider, + ILogger logger) : base(name, description) { - Logger = logger; - DisplayProvider = displayProvider; InputProvider = inputProvider; + OutputProvider = outputProvider; + Logger = logger; Add(CommonOptions.WorkingDirectory); Add(CommonOptions.Debug); Add(CommonOptions.Verbose); + Add(CommonOptions.Json); SetAction(async (parseResult, cancellationToken) => { diff --git a/src/Stack/Infrastructure/Commands/CommandWithOutput.cs b/src/Stack/Infrastructure/Commands/CommandWithOutput.cs index 6fa27ed1..0bd80c0a 100644 --- a/src/Stack/Infrastructure/Commands/CommandWithOutput.cs +++ b/src/Stack/Infrastructure/Commands/CommandWithOutput.cs @@ -11,20 +11,14 @@ public abstract class CommandWithOutput : Command where TResponse : n protected CommandWithOutput( string name, string? description, - ILogger logger, - IDisplayProvider displayProvider, + CliExecutionContext executionContext, IInputProvider inputProvider, - CliExecutionContext executionContext) - : base(name, description, logger, displayProvider, inputProvider, executionContext) + IOutputProvider outputProvider, + ILogger logger) + : base(name, description, executionContext, inputProvider, outputProvider, logger) { - Add(CommonOptions.Json); } - readonly JsonSerializerOptions jsonSerializerOptions = new() - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - }; - protected override async Task Execute(ParseResult parseResult, CancellationToken cancellationToken) { var json = parseResult.GetValue(CommonOptions.Json); diff --git a/src/Stack/Infrastructure/CommonOptions.cs b/src/Stack/Infrastructure/CommonOptions.cs index 59339976..b38dbab5 100644 --- a/src/Stack/Infrastructure/CommonOptions.cs +++ b/src/Stack/Infrastructure/CommonOptions.cs @@ -22,7 +22,7 @@ public static class CommonOptions public static Option Json { get; } = new Option("--json") { - Description = "Output results as JSON.", + Description = "Write output and log messages as JSON. Log messages will be written to stderr.", Required = false }; diff --git a/src/Stack/Infrastructure/ConsoleDisplayProvider.cs b/src/Stack/Infrastructure/ConsoleDisplayProvider.cs index 10a5026e..f08c514d 100644 --- a/src/Stack/Infrastructure/ConsoleDisplayProvider.cs +++ b/src/Stack/Infrastructure/ConsoleDisplayProvider.cs @@ -1,29 +1,7 @@ -using Microsoft.Extensions.Logging; using Spectre.Console; -using Spectre.Console.Rendering; namespace Stack.Infrastructure; -public interface IDisplayProvider -{ - Task DisplayStatus(string message, Func action, CancellationToken cancellationToken = default); - Task DisplayStatus(string message, Func> action, CancellationToken cancellationToken = default); - Task DisplayTree(string header, IEnumerable> items, Func? itemFormatter = null, CancellationToken cancellationToken = default) where T : notnull; - Task DisplayMessage(string message, CancellationToken cancellationToken = default); - Task DisplaySuccess(string message, CancellationToken cancellationToken = default) - => DisplayMessage($"{Emoji.Known.CheckMark} {message}", cancellationToken); - Task DisplayStatusWithSuccess(string message, Func action, CancellationToken cancellationToken = default) - { - return DisplayStatus(message, async ct => - { - await action(ct); - await DisplaySuccess(message, ct); - }, cancellationToken); - } - Task DisplayHeader(string header, CancellationToken cancellationToken = default); - Task DisplayNewLine(CancellationToken cancellationToken = default); -} - public class ConsoleDisplayProvider(IAnsiConsole console) : IDisplayProvider { readonly AsyncLocal _currentStatusContext = new(); @@ -105,12 +83,6 @@ public async Task DisplayTree(string header, IEnumerable> items, console.Write(tree); } - public async Task DisplayMessage(string message, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - console.MarkupLine(message); - } - void AddChildTreeNodes(TreeNode parent, TreeItem item, Func? itemFormatter = null) where T : notnull { foreach (var child in item.Children) @@ -142,6 +114,12 @@ public async Task DisplayHeader(string header, CancellationToken cancellationTok rule.DoubleBorder(); console.Write(rule); } + + public async Task DisplaySuccess(string message, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + console.MarkupLine($"{Emoji.Known.CheckMark} {message}", cancellationToken); + } } public record TreeItem(T Value, List> Children); diff --git a/src/Stack/Infrastructure/ConsoleOutputProvider.cs b/src/Stack/Infrastructure/ConsoleOutputProvider.cs new file mode 100644 index 00000000..a5bf4c31 --- /dev/null +++ b/src/Stack/Infrastructure/ConsoleOutputProvider.cs @@ -0,0 +1,9 @@ +namespace Stack.Infrastructure; + +public class ConsoleOutputProvider : IOutputProvider +{ + public async Task WriteLine(string output, CancellationToken cancellationToken = default) + { + await Console.Out.WriteLineAsync(output.AsMemory(), cancellationToken); + } +} diff --git a/src/Stack/Infrastructure/HostApplicationBuilderExtensions.cs b/src/Stack/Infrastructure/HostApplicationBuilderExtensions.cs index b531d25e..4772a40f 100644 --- a/src/Stack/Infrastructure/HostApplicationBuilderExtensions.cs +++ b/src/Stack/Infrastructure/HostApplicationBuilderExtensions.cs @@ -9,6 +9,8 @@ using Stack.Infrastructure.Settings; using Stack.Commands; using Microsoft.Extensions.Logging; +using System.Security.Cryptography.X509Certificates; +using Microsoft.Extensions.Logging.Console; namespace Stack.Infrastructure; @@ -35,7 +37,19 @@ public static IHostApplicationBuilder ConfigureLogging(this IHostApplicationBuil builder.Logging.AddFilter("Microsoft", LogLevel.Warning); builder.Logging.ClearProviders(); - builder.Services.AddSingleton(); + + if (args.Contains(CommonOptions.Json.Name)) + { + builder.Logging.AddJsonConsole(); + builder.Services.Configure(options => + { + options.LogToStandardErrorThreshold = LogLevel.Trace; + }); + } + else + { + builder.Services.AddSingleton(); + } return builder; } @@ -54,7 +68,15 @@ private static void ConfigureServices(this IServiceCollection services, string[] Out = new AnsiConsoleOutput(stream), }); }); - services.AddSingleton(); + services.AddSingleton(); + if (args.Contains(CommonOptions.Json.Name)) + { + services.AddSingleton(); + } + else + { + services.AddSingleton(); + } services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Stack/Infrastructure/IDisplayProvider.cs b/src/Stack/Infrastructure/IDisplayProvider.cs new file mode 100644 index 00000000..daa912d2 --- /dev/null +++ b/src/Stack/Infrastructure/IDisplayProvider.cs @@ -0,0 +1,16 @@ +namespace Stack.Infrastructure; + +public interface IDisplayProvider +{ + Task DisplayStatus(string message, Func action, CancellationToken cancellationToken = default); + Task DisplayStatus(string message, Func> action, CancellationToken cancellationToken = default); + Task DisplaySuccess(string message, CancellationToken cancellationToken = default); + Task DisplayStatusWithSuccess(string message, Func action, CancellationToken cancellationToken = default) + { + return DisplayStatus(message, async ct => + { + await action(ct); + await DisplaySuccess(message, ct); + }, cancellationToken); + } +} diff --git a/src/Stack/Infrastructure/IOutputProvider.cs b/src/Stack/Infrastructure/IOutputProvider.cs new file mode 100644 index 00000000..278c4cb9 --- /dev/null +++ b/src/Stack/Infrastructure/IOutputProvider.cs @@ -0,0 +1,15 @@ +namespace Stack.Infrastructure; + +public interface IOutputProvider +{ + Task WriteLine(string output, CancellationToken cancellationToken); + + Task WriteMessage(string message, CancellationToken cancellationToken) + => WriteLine(RenderingHelpers.RenderMessage(message), cancellationToken); + + Task WriteNewLine(CancellationToken cancellationToken) + => WriteLine(string.Empty, cancellationToken); + + Task WriteHeader(string header, CancellationToken cancellationToken) + => WriteLine(RenderingHelpers.RenderHeader(header), cancellationToken); +} diff --git a/src/Stack/Infrastructure/LoggingDisplayProvider.cs b/src/Stack/Infrastructure/LoggingDisplayProvider.cs new file mode 100644 index 00000000..96b4f64f --- /dev/null +++ b/src/Stack/Infrastructure/LoggingDisplayProvider.cs @@ -0,0 +1,39 @@ +using Microsoft.Extensions.Logging; + +namespace Stack.Infrastructure; + +public class LoggingDisplayProvider(ILogger logger) : IDisplayProvider +{ + public async Task DisplayStatus(string message, Func action, CancellationToken cancellationToken = default) + { + logger.Status(message); + await action(cancellationToken); + } + + public async Task DisplayStatus(string message, Func> action, CancellationToken cancellationToken = default) + { + logger.Status(message); + return await action(cancellationToken); + } + + public async Task DisplaySuccess(string message, CancellationToken cancellationToken = default) + { + logger.Success(message); + await Task.CompletedTask; + } +} + +public static class KnownEvents +{ + public const int Status = 1; + public const int Success = 2; +} + +public static partial class LoggerExtensionMethods +{ + [LoggerMessage(KnownEvents.Status, LogLevel.Information, "{Message}")] + public static partial void Status(this ILogger logger, string message); + + [LoggerMessage(KnownEvents.Success, LogLevel.Information, "{Message}")] + public static partial void Success(this ILogger logger, string message); +} diff --git a/src/Stack/Infrastructure/RenderingHelpers.cs b/src/Stack/Infrastructure/RenderingHelpers.cs new file mode 100644 index 00000000..7d4769b0 --- /dev/null +++ b/src/Stack/Infrastructure/RenderingHelpers.cs @@ -0,0 +1,83 @@ +using Spectre.Console; + +namespace Stack.Infrastructure; + +public static class RenderingHelpers +{ + public static string RenderTree(string header, IEnumerable> items, Func? itemFormatter = null) + where T : notnull + { + using var writer = new StringWriter(); + var console = AnsiConsole.Create(new AnsiConsoleSettings + { + Ansi = AnsiSupport.Detect, + ColorSystem = ColorSystemSupport.Detect, + Out = new AnsiConsoleOutput(writer) + }); + var tree = new Tree(header); + + foreach (var item in items) + { + var formattedItem = itemFormatter?.Invoke(item.Value) ?? item.Value.ToString(); + if (formattedItem is null) + { + continue; + } + var treeNode = tree.AddNode(formattedItem); + AddChildTreeNodes(treeNode, item, itemFormatter); + } + + console.Write(tree); + + return writer.ToString(); + } + + public static string RenderHeader(string header) + { + using var writer = new StringWriter(); + var console = AnsiConsole.Create(new AnsiConsoleSettings + { + Ansi = AnsiSupport.Detect, + ColorSystem = ColorSystemSupport.Detect, + Out = new AnsiConsoleOutput(writer) + }); + + var rule = new Rule(header); + rule.LeftJustified(); + rule.DoubleBorder(); + console.Write(rule); + + return writer.ToString(); + } + + public static string RenderMessage(string message) + { + using var writer = new StringWriter(); + var console = AnsiConsole.Create(new AnsiConsoleSettings + { + Ansi = AnsiSupport.Detect, + ColorSystem = ColorSystemSupport.Detect, + Out = new AnsiConsoleOutput(writer) + }); + + console.Markup(message); + return writer.ToString(); + } + + static void AddChildTreeNodes(TreeNode parent, TreeItem item, Func? itemFormatter = null) where T : notnull + { + foreach (var child in item.Children) + { + var formattedItem = itemFormatter?.Invoke(child.Value) ?? child.Value.ToString(); + if (formattedItem is null) + { + continue; + } + var node = parent.AddNode(formattedItem); + if (child.Children.Count > 0) + { + AddChildTreeNodes(node, child); + } + } + } +}