public override async Task <int> ExecuteAsync() { try { IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger); IEnumerable <Subscription> subscriptions = await _options.FilterSubscriptions(remote); if (!subscriptions.Any()) { Console.WriteLine("No subscriptions found matching the specified criteria."); return(Constants.ErrorCode); } // Based on the current output schema, sort by source repo, target repo, target branch, etc. // Concat the input strings as a simple sorting mechanism. foreach (var subscription in subscriptions.OrderBy(subscription => $"{subscription.SourceRepository}{subscription.Channel}{subscription.TargetRepository}{subscription.TargetBranch}")) { Console.WriteLine($"{subscription.SourceRepository} ({subscription.Channel.Name}) ==> '{subscription.TargetRepository}' ('{subscription.TargetBranch}')"); Console.WriteLine($" - Id: {subscription.Id}"); Console.WriteLine($" - Update Frequency: {subscription.Policy.UpdateFrequency}"); Console.WriteLine($" - Enabled: {subscription.Enabled}"); Console.WriteLine($" - Batchable: {subscription.Policy.Batchable}"); // If batchable, the merge policies come from the repository IEnumerable <MergePolicy> mergePolicies = subscription.Policy.MergePolicies; if (subscription.Policy.Batchable == true) { mergePolicies = await remote.GetRepositoryMergePoliciesAsync(subscription.TargetRepository, subscription.TargetBranch); } Console.Write(UxHelpers.GetMergePoliciesDescription(mergePolicies, " ")); // Currently the API only returns the last applied build for requests to specific subscriptions. // This will be fixed, but for now, don't print the last applied build otherwise. if (subscription.LastAppliedBuild != null) { Console.WriteLine($" - Last Build: {subscription.LastAppliedBuild.AzureDevOpsBuildNumber} ({subscription.LastAppliedBuild.Commit})"); } } return(Constants.SuccessCode); } catch (AuthenticationException e) { Console.WriteLine(e.Message); return(Constants.ErrorCode); } catch (Exception e) { Logger.LogError(e, "Error: Failed to retrieve subscriptions"); return(Constants.ErrorCode); } }
public override async Task <int> ExecuteAsync() { try { IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger); IEnumerable <Subscription> subscriptions = await _options.FilterSubscriptions(remote); if (!subscriptions.Any()) { Console.WriteLine("No subscriptions found matching the specified criteria."); return(Constants.ErrorCode); } // Based on the current output schema, sort by source repo, target repo, target branch, etc. // Concat the input strings as a simple sorting mechanism. foreach (var subscription in subscriptions.OrderBy(subscription => $"{subscription.SourceRepository}{subscription.Channel}{subscription.TargetRepository}{subscription.TargetBranch}")) { // If batchable, the merge policies come from the repository IEnumerable <MergePolicy> mergePolicies = subscription.Policy.MergePolicies; if (subscription.Policy.Batchable == true) { mergePolicies = await remote.GetRepositoryMergePoliciesAsync(subscription.TargetRepository, subscription.TargetBranch); } string subscriptionInfo = UxHelpers.GetTextSubscriptionDescription(subscription, mergePolicies); Console.Write(subscriptionInfo); } return(Constants.SuccessCode); } catch (AuthenticationException e) { Console.WriteLine(e.Message); return(Constants.ErrorCode); } catch (Exception e) { Logger.LogError(e, "Error: Failed to retrieve subscriptions"); return(Constants.ErrorCode); } }
public override async Task <int> ExecuteAsync() { IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger); if (_options.IgnoreChecks.Count() > 0 && !_options.AllChecksSuccessfulMergePolicy) { Console.WriteLine($"--ignore-checks must be combined with --all-checks-passed"); return(Constants.ErrorCode); } // Parse the merge policies List <MergePolicy> mergePolicies = new List <MergePolicy>(); if (_options.AllChecksSuccessfulMergePolicy) { mergePolicies.Add( new MergePolicy { Name = MergePolicyConstants.AllCheckSuccessfulMergePolicyName, Properties = ImmutableDictionary.Create <string, JToken>() .Add(MergePolicyConstants.IgnoreChecksMergePolicyPropertyName, JToken.FromObject(_options.IgnoreChecks)) }); } if (_options.NoRequestedChangesMergePolicy) { mergePolicies.Add( new MergePolicy { Name = MergePolicyConstants.NoRequestedChangesMergePolicyName, Properties = ImmutableDictionary.Create <string, JToken>() }); } if (_options.StandardAutoMergePolicies) { mergePolicies.Add( new MergePolicy { Name = MergePolicyConstants.StandardMergePolicyName, Properties = ImmutableDictionary.Create <string, JToken>() }); } string repository = _options.Repository; string branch = _options.Branch; // If in quiet (non-interactive mode), ensure that all options were passed, then // just call the remote API if (_options.Quiet) { if (string.IsNullOrEmpty(repository) || string.IsNullOrEmpty(branch)) { Logger.LogError($"Missing input parameters for merge policies. Please see command help or remove --quiet/-q for interactive mode"); return(Constants.ErrorCode); } } else { // Look up existing merge policies if the repository and branch were specified, and the user didn't // specify policies on the command line. In this case, they typically want to update if (!mergePolicies.Any() && !string.IsNullOrEmpty(repository) && !string.IsNullOrEmpty(branch)) { mergePolicies = (await remote.GetRepositoryMergePoliciesAsync(repository, branch)).ToList(); } // Help the user along with a form. We'll use the API to gather suggested values // from existing subscriptions based on the input parameters. SetRepositoryMergePoliciesPopUp initEditorPopUp = new SetRepositoryMergePoliciesPopUp("set-policies/set-policies-todo", Logger, repository, branch, mergePolicies, Constants.AvailableMergePolicyYamlHelp); UxManager uxManager = new UxManager(_options.GitLocation, Logger); int exitCode = uxManager.PopUp(initEditorPopUp); if (exitCode != Constants.SuccessCode) { return(exitCode); } repository = initEditorPopUp.Repository; branch = initEditorPopUp.Branch; mergePolicies = initEditorPopUp.MergePolicies; } IRemote verifyRemote = RemoteFactory.GetRemote(_options, repository, Logger); IEnumerable <RepositoryBranch> targetRepository = await verifyRemote.GetRepositoriesAsync(repository); if (targetRepository == null || !targetRepository.Any()) { Console.WriteLine($"The target repository '{repository}' doesn't have a Maestro installation. Aborting merge policy creation."); return(Constants.ErrorCode); } if (!(await UxHelpers.VerifyAndConfirmBranchExistsAsync(verifyRemote, repository, branch, !_options.Quiet))) { Console.WriteLine("Aborting merge policy creation."); return(Constants.ErrorCode); } try { await remote.SetRepositoryMergePoliciesAsync( repository, branch, mergePolicies); Console.WriteLine($"Successfully updated merge policies for {repository}@{branch}."); return(Constants.SuccessCode); } catch (AuthenticationException e) { Console.WriteLine(e.Message); return(Constants.ErrorCode); } catch (RestApiException e) when(e.Response.Status == (int)System.Net.HttpStatusCode.BadRequest) { Logger.LogError($"Failed to set repository auto merge policies: {e.Response.Content}"); return(Constants.ErrorCode); } catch (Exception e) { Logger.LogError(e, $"Failed to set merge policies."); return(Constants.ErrorCode); } }
/// <summary> /// Implements the 'add-subscription' operation /// </summary> /// <param name="options"></param> public override async Task <int> ExecuteAsync() { IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger); if (_options.IgnoreChecks.Count() > 0 && !_options.AllChecksSuccessfulMergePolicy) { Console.WriteLine($"--ignore-checks must be combined with --all-checks-passed"); return(Constants.ErrorCode); } // Parse the merge policies List <MergePolicy> mergePolicies = new List <MergePolicy>(); if (_options.NoExtraCommitsMergePolicy) { mergePolicies.Add( new MergePolicy { Name = MergePolicyConstants.NoExtraCommitsMergePolicyName }); } if (_options.AllChecksSuccessfulMergePolicy) { mergePolicies.Add( new MergePolicy { Name = MergePolicyConstants.AllCheckSuccessfulMergePolicyName, Properties = ImmutableDictionary.Create <string, JToken>() .Add(MergePolicyConstants.IgnoreChecksMergePolicyPropertyName, JToken.FromObject(_options.IgnoreChecks)) }); } if (_options.NoRequestedChangesMergePolicy) { mergePolicies.Add( new MergePolicy { Name = MergePolicyConstants.NoRequestedChangesMergePolicyName, Properties = ImmutableDictionary.Create <string, JToken>() }); } if (_options.StandardAutoMergePolicies) { mergePolicies.Add( new MergePolicy { Name = MergePolicyConstants.StandardMergePolicyName, Properties = ImmutableDictionary.Create <string, JToken>() }); } if (_options.Batchable && mergePolicies.Count > 0) { Console.WriteLine("Batchable subscriptions cannot be combined with merge policies. " + "Merge policies are specified at a repository+branch level."); return(Constants.ErrorCode); } string channel = _options.Channel; string sourceRepository = _options.SourceRepository; string targetRepository = _options.TargetRepository; string targetBranch = GitHelpers.NormalizeBranchName(_options.TargetBranch); string updateFrequency = _options.UpdateFrequency; bool batchable = _options.Batchable; // If in quiet (non-interactive mode), ensure that all options were passed, then // just call the remote API if (_options.Quiet && !_options.ReadStandardIn) { if (string.IsNullOrEmpty(channel) || string.IsNullOrEmpty(sourceRepository) || string.IsNullOrEmpty(targetRepository) || string.IsNullOrEmpty(targetBranch) || string.IsNullOrEmpty(updateFrequency) || !Constants.AvailableFrequencies.Contains(updateFrequency, StringComparer.OrdinalIgnoreCase)) { Logger.LogError($"Missing input parameters for the subscription. Please see command help or remove --quiet/-q for interactive mode"); return(Constants.ErrorCode); } } else { // Grab existing subscriptions to get suggested values. // TODO: When this becomes paged, set a max number of results to avoid // pulling too much. var suggestedRepos = remote.GetSubscriptionsAsync(); var suggestedChannels = remote.GetChannelsAsync(); // Help the user along with a form. We'll use the API to gather suggested values // from existing subscriptions based on the input parameters. AddSubscriptionPopUp addSubscriptionPopup = new AddSubscriptionPopUp("add-subscription/add-subscription-todo", Logger, channel, sourceRepository, targetRepository, targetBranch, updateFrequency, batchable, mergePolicies, (await suggestedChannels).Select(suggestedChannel => suggestedChannel.Name), (await suggestedRepos).SelectMany(subscription => new List <string> { subscription.SourceRepository, subscription.TargetRepository }).ToHashSet(), Constants.AvailableFrequencies, Constants.AvailableMergePolicyYamlHelp); UxManager uxManager = new UxManager(_options.GitLocation, Logger); int exitCode = _options.ReadStandardIn ? uxManager.ReadFromStdIn(addSubscriptionPopup) : uxManager.PopUp(addSubscriptionPopup); if (exitCode != Constants.SuccessCode) { return(exitCode); } channel = addSubscriptionPopup.Channel; sourceRepository = addSubscriptionPopup.SourceRepository; targetRepository = addSubscriptionPopup.TargetRepository; targetBranch = addSubscriptionPopup.TargetBranch; updateFrequency = addSubscriptionPopup.UpdateFrequency; mergePolicies = addSubscriptionPopup.MergePolicies; batchable = addSubscriptionPopup.Batchable; } try { // If we are about to add a batchable subscription and the merge policies are empty for the // target repo/branch, warn the user. if (batchable) { var existingMergePolicies = await remote.GetRepositoryMergePoliciesAsync(targetRepository, targetBranch); if (!existingMergePolicies.Any()) { Console.WriteLine("Warning: Batchable subscription doesn't have any repository merge policies. " + "PRs will not be auto-merged."); Console.WriteLine($"Please use 'darc set-repository-policies --repo {targetRepository} --branch {targetBranch}' " + $"to set policies.{Environment.NewLine}"); } } // Verify the target IRemote targetVerifyRemote = RemoteFactory.GetRemote(_options, targetRepository, Logger); if (!(await UxHelpers.VerifyAndConfirmBranchExistsAsync(targetVerifyRemote, targetRepository, targetBranch, !_options.Quiet))) { Console.WriteLine("Aborting subscription creation."); return(Constants.ErrorCode); } // Verify the source. IRemote sourceVerifyRemote = RemoteFactory.GetRemote(_options, sourceRepository, Logger); if (!(await UxHelpers.VerifyAndConfirmRepositoryExistsAsync(sourceVerifyRemote, sourceRepository, !_options.Quiet))) { Console.WriteLine("Aborting subscription creation."); return(Constants.ErrorCode); } var newSubscription = await remote.CreateSubscriptionAsync(channel, sourceRepository, targetRepository, targetBranch, updateFrequency, batchable, mergePolicies); Console.WriteLine($"Successfully created new subscription with id '{newSubscription.Id}'."); // Prompt the user to trigger the subscription unless they have explicitly disallowed it if (!_options.NoTriggerOnCreate) { bool triggerAutomatically = _options.TriggerOnCreate || UxHelpers.PromptForYesNo("Trigger this subscription immediately?"); if (triggerAutomatically) { await remote.TriggerSubscriptionAsync(newSubscription.Id.ToString()); Console.WriteLine($"Subscription '{newSubscription.Id}' triggered."); } } return(Constants.SuccessCode); } catch (AuthenticationException e) { Console.WriteLine(e.Message); return(Constants.ErrorCode); } catch (RestApiException e) when(e.Response.Status == (int)System.Net.HttpStatusCode.BadRequest) { // Could have been some kind of validation error (e.g. channel doesn't exist) Logger.LogError($"Failed to create subscription: {e.Response.Content}"); return(Constants.ErrorCode); } catch (Exception e) { Logger.LogError(e, $"Failed to create subscription."); return(Constants.ErrorCode); } }
public override async Task <int> ExecuteAsync() { try { IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger); var subscriptions = (await remote.GetSubscriptionsAsync()).Where(subscription => { return(_options.SubcriptionFilter(subscription)); }); if (subscriptions.Count() == 0) { Console.WriteLine("No subscriptions found matching the specified criteria."); return(Constants.ErrorCode); } // Based on the current output schema, sort by source repo, target repo, target branch, etc. // Concat the input strings as a simple sorting mechanism. foreach (var subscription in subscriptions.OrderBy(subscription => $"{subscription.SourceRepository}{subscription.Channel}{subscription.TargetRepository}{subscription.TargetBranch}")) { Console.WriteLine($"{subscription.SourceRepository} ({subscription.Channel.Name}) ==> '{subscription.TargetRepository}' ('{subscription.TargetBranch}')"); Console.WriteLine($" - Id: {subscription.Id}"); Console.WriteLine($" - Update Frequency: {subscription.Policy.UpdateFrequency}"); Console.WriteLine($" - Enabled: {subscription.Enabled}"); Console.WriteLine($" - Batchable: {subscription.Policy.Batchable}"); // If batchable, the merge policies come from the repository IEnumerable <MergePolicy> mergePolicies = subscription.Policy.MergePolicies; if (subscription.Policy.Batchable == true) { mergePolicies = await remote.GetRepositoryMergePoliciesAsync(subscription.TargetRepository, subscription.TargetBranch); } if (mergePolicies.Any()) { Console.WriteLine($" - Merge Policies:"); foreach (MergePolicy mergePolicy in mergePolicies) { Console.WriteLine($" {mergePolicy.Name}"); if (mergePolicy.Properties != null) { foreach (var mergePolicyProperty in mergePolicy.Properties) { // The merge policy property is a key value pair. For formatting, turn it into a string. // It's often a JToken, so handle appropriately // 1. If the number of lines in the string is 1, write on same line as key // 2. If the number of lines in the string is more than one, start on new // line and indent. string valueString = mergePolicyProperty.Value.ToString(); string[] valueLines = valueString.Split(System.Environment.NewLine); string keyString = $" {mergePolicyProperty.Key} = "; Console.Write(keyString); if (valueLines.Length == 1) { Console.WriteLine(valueString); } else { string indentString = new string(' ', keyString.Length); Console.WriteLine(); foreach (string line in valueLines) { Console.WriteLine($"{indentString}{line}"); } } } } } } else { Console.WriteLine($" - Merge Policies: []"); } // Currently the API only returns the last applied build for requests to specific subscriptions. // This will be fixed, but for now, don't print the last applied build otherwise. if (subscription.LastAppliedBuild != null) { Console.WriteLine($" - Last Build: {subscription.LastAppliedBuild.AzureDevOpsBuildNumber} ({subscription.LastAppliedBuild.Commit})"); } } return(Constants.SuccessCode); } catch (Exception e) { Logger.LogError(e, "Error: Failed to retrieve subscriptions"); return(Constants.ErrorCode); } }