Beispiel #1
0
 private async static Task HandleMasterCopy(RemoteFactory remoteFactory, string repoUrl, string masterGitRepoPath, string masterRepoGitDirPath, ILogger log)
 {
     if (masterRepoGitDirPath != null)
     {
         await HandleMasterCopyWithGitDirPath(remoteFactory, repoUrl, masterGitRepoPath, masterRepoGitDirPath, log);
     }
     else
     {
         await HandleMasterCopyWithDefaultGitDir(remoteFactory, repoUrl, masterGitRepoPath, masterRepoGitDirPath, log);
     }
 }
        public async Task UpdateLongestBuildPathAsync(CancellationToken cancellationToken)
        {
            using (_operations.BeginOperation($"Updating Longest Build Path table"))
            {
                IRemote barOnlyRemote = await RemoteFactory.GetBarOnlyRemoteAsync(Logger);

                List <Channel> channels = Context.Channels.Select(c => new Channel()
                {
                    Id = c.Id, Name = c.Name
                }).ToList();
                IReadOnlyList <string> frequencies = new[] { "everyWeek", "twiceDaily", "everyDay", "everyBuild", "none", };

                Logger.LogInformation($"Will update '{channels.Count}' channels");

                foreach (var channel in channels)
                {
                    var flowGraph = await barOnlyRemote.GetDependencyFlowGraphAsync(
                        channel.Id,
                        days : 30,
                        includeArcade : false,
                        includeBuildTimes : true,
                        includeDisabledSubscriptions : false,
                        includedFrequencies : frequencies);

                    // Get the nodes on the longest path and order them by path time so that the
                    // contributing repos are in the right order
                    List <DependencyFlowNode> longestBuildPathNodes = flowGraph.Nodes
                                                                      .Where(n => n.OnLongestBuildPath)
                                                                      .OrderByDescending(n => n.BestCasePathTime)
                                                                      .ToList();

                    if (longestBuildPathNodes.Any())
                    {
                        LongestBuildPath lbp = new LongestBuildPath()
                        {
                            ChannelId                = channel.Id,
                            BestCaseTimeInMinutes    = longestBuildPathNodes.Max(n => n.BestCasePathTime),
                            WorstCaseTimeInMinutes   = longestBuildPathNodes.Max(n => n.WorstCasePathTime),
                            ContributingRepositories = String.Join(';', longestBuildPathNodes.Select(n => $"{n.Repository}@{n.Branch}").ToArray()),
                            ReportDate               = DateTimeOffset.UtcNow,
                        };

                        Logger.LogInformation($"Will update {channel.Name} to best case time {lbp.BestCaseTimeInMinutes} and worst case time {lbp.WorstCaseTimeInMinutes}");
                        await Context.LongestBuildPaths.AddAsync(lbp);
                    }
                    else
                    {
                        Logger.LogInformation($"Will not update {channel.Name} longest build path because no nodes have {nameof(DependencyFlowNode.OnLongestBuildPath)} flag set. Total node count = {flowGraph.Nodes.Count}");
                    }
                }

                await Context.SaveChangesAsync();
            }
        }
Beispiel #3
0
        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);
            }
        }
Beispiel #4
0
        /// <summary>
        /// Implements the default channel enable/disable operation
        /// </summary>
        /// <param name="options"></param>
        public override async Task <int> ExecuteAsync()
        {
            if ((_options.Enable && _options.Disable) ||
                (!_options.Enable && !_options.Disable))
            {
                Console.WriteLine("Please specify either --enable or --disable");
                return(Constants.ErrorCode);
            }

            IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

            try
            {
                DefaultChannel resolvedChannel = await ResolveSingleChannel();

                if (resolvedChannel == null)
                {
                    return(Constants.ErrorCode);
                }

                bool enabled;
                if (_options.Enable)
                {
                    if (resolvedChannel.Enabled)
                    {
                        Console.WriteLine($"Default channel association is already enabled");
                        return(Constants.ErrorCode);
                    }
                    enabled = true;
                }
                else
                {
                    if (!resolvedChannel.Enabled)
                    {
                        Console.WriteLine($"Default channel association is already disabled");
                        return(Constants.ErrorCode);
                    }
                    enabled = false;
                }

                await remote.UpdateDefaultChannelAsync(resolvedChannel.Id, enabled : enabled);

                Console.WriteLine($"Default channel association has been {(enabled ? "enabled" : "disabled")}.");

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed enable/disable default channel association.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #5
0
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

                IEnumerable <RepositoryBranch> allRepositories = await remote.GetRepositoriesAsync();

                IEnumerable <RepositoryBranch> filteredRepositories = allRepositories.Where(repositories =>
                                                                                            (string.IsNullOrEmpty(_options.Repo) || repositories.Repository.Contains(_options.Repo, StringComparison.OrdinalIgnoreCase)) &&
                                                                                            (string.IsNullOrEmpty(_options.Branch) || repositories.Branch.Contains(_options.Branch, StringComparison.OrdinalIgnoreCase)));

                // List only those repos and branches that are targeted by a batchable subscription (active) unless the user
                // passes --all.
                if (!_options.All)
                {
                    HashSet <string> batchableTargets = (await remote.GetSubscriptionsAsync())
                                                        .Where(s => s.Policy.Batchable)
                                                        .Select <Subscription, string>(s => $"{s.TargetRepository}{s.TargetBranch}")
                                                        .ToHashSet(StringComparer.OrdinalIgnoreCase);
                    var targetedRepositories = filteredRepositories.Where(r => batchableTargets.Contains($"{r.Repository}{r.Branch}"));

                    // If the number of repositories we're about to print is less than what we could have printed, then print a
                    // message.
                    int difference = filteredRepositories.Count() - targetedRepositories.Count();
                    if (difference != 0)
                    {
                        Console.WriteLine($"Filtered {difference} policies for branches not targeted by an active batchable subscription. To include, pass --all.{Environment.NewLine}");
                    }

                    filteredRepositories = targetedRepositories;
                }

                foreach (var repository in filteredRepositories)
                {
                    Console.WriteLine($"{repository.Repository} @ {repository.Branch}");
                    Console.Write(UxHelpers.GetMergePoliciesDescription(repository.MergePolicies));
                }

                return(Constants.SuccessCode);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to retrieve repositories");
                return(Constants.ErrorCode);
            }
        }
        /// <summary>
        ///     Resolve channel based on the input options. If no channel could be resolved
        ///     based on the input options, returns null.
        /// </summary>
        /// <returns>Default channel or null</returns>
        protected async Task <DefaultChannel> ResolveSingleChannel()
        {
            IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

            IEnumerable <DefaultChannel> potentialDefaultChannels = await remote.GetDefaultChannelsAsync();

            // User should have supplied id or a combo of the channel name, repo, and branch.
            if (_options.Id != -1)
            {
                DefaultChannel defaultChannel = potentialDefaultChannels.SingleOrDefault(d => d.Id == _options.Id);
                if (defaultChannel == null)
                {
                    Console.WriteLine($"Could not find a default channel with id {_options.Id}");
                }
                return(defaultChannel);
            }
            else if (string.IsNullOrEmpty(_options.Repository) ||
                     string.IsNullOrEmpty(_options.Channel) ||
                     string.IsNullOrEmpty(_options.Branch))
            {
                Console.WriteLine("Please specify either the default channel id with --id or a combination of --channel, --branch and --repo");
                return(null);
            }

            // Otherwise, filter based on the other inputs. If more than one resolves, then print the possible
            // matches and return null
            var matchingChannels = potentialDefaultChannels.Where(d =>
            {
                return((string.IsNullOrEmpty(_options.Repository) || d.Repository.Contains(_options.Repository, StringComparison.OrdinalIgnoreCase)) &&
                       (string.IsNullOrEmpty(_options.Channel) || d.Channel.Name.Contains(_options.Channel, StringComparison.OrdinalIgnoreCase)) &&
                       (string.IsNullOrEmpty(_options.Branch) || d.Branch.Contains(GitHelpers.NormalizeBranchName(_options.Branch), StringComparison.OrdinalIgnoreCase)));
            });

            if (!matchingChannels.Any())
            {
                Console.WriteLine($"No channels found matching the specified criteria.");
                return(null);
            }
            else if (matchingChannels.Count() != 1)
            {
                Console.WriteLine($"More than one channel matching the specified criteria. Please change your options to be more specific.");
                foreach (DefaultChannel defaultChannel in matchingChannels)
                {
                    Console.WriteLine($"    {UxHelpers.GetDefaultChannelDescriptionString(defaultChannel)}");
                }
                return(null);
            }
            else
            {
                return(matchingChannels.Single());
            }
        }
        /// <summary>
        ///     Deletes a build from a channel.
        /// </summary>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

                // Find the build to give someone info
                Build build = await remote.GetBuildAsync(_options.Id);

                if (build == null)
                {
                    Console.WriteLine($"Could not find a build with id '{_options.Id}'");
                    return(Constants.ErrorCode);
                }

                Channel targetChannel = await UxHelpers.ResolveSingleChannel(remote, _options.Channel);

                if (targetChannel == null)
                {
                    return(Constants.ErrorCode);
                }

                if (!build.Channels.Any(c => c.Id == targetChannel.Id))
                {
                    Console.WriteLine($"Build '{build.Id}' is not assigned to channel '{targetChannel.Name}'");
                    return(Constants.SuccessCode);
                }

                Console.WriteLine($"Deleting the following build from channel '{targetChannel.Name}':");
                Console.WriteLine();
                Console.Write(UxHelpers.GetTextBuildDescription(build));

                await remote.DeleteBuildFromChannelAsync(_options.Id, targetChannel.Id);

                // Let the user know they can trigger subscriptions if they'd like.
                Console.WriteLine("Subscriptions can be triggered to revert to the previous state using the following command:");
                Console.WriteLine($"darc trigger-subscriptions --source-repo {build.GitHubRepository ?? build.AzureDevOpsRepository} --channel {targetChannel.Name}");

                return(Constants.SuccessCode);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, $"Error: Failed to delete build '{_options.Id}' from channel '{_options.Channel}'.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #8
0
        private async static Task HandleMasterCopy(RemoteFactory remoteFactory, string repoUrl, string masterGitRepoPath, string masterRepoGitDirPath, ILogger log)
        {
            if (masterRepoGitDirPath != null)
            {
                await HandleMasterCopyWithGitDirPath(remoteFactory, repoUrl, masterGitRepoPath, masterRepoGitDirPath, log);
            }
            else
            {
                await HandleMasterCopyWithDefaultGitDir(remoteFactory, repoUrl, masterGitRepoPath, masterRepoGitDirPath, log);
            }
            Local local = new Local(log, masterGitRepoPath);

            local.AddRemoteIfMissing(masterGitRepoPath, repoUrl);
        }
Beispiel #9
0
        private static async Task HandleMasterCopyAndCreateGitDir(RemoteFactory remoteFactory, string repoUrl, string masterGitRepoPath, string masterRepoGitDirPath, string gitDirRedirect, ILogger log)
        {
            log.LogDebug($"Master .gitdir {masterRepoGitDirPath} does not exist");

            // The master folder also doesn't exist.  Just clone and set everything up for the first time.
            if (!Directory.Exists(masterGitRepoPath))
            {
                log.LogInformation($"Cloning master copy of {repoUrl} into {masterGitRepoPath} with .gitdir path {masterRepoGitDirPath}");
                IRemote repoRemote = await remoteFactory.GetRemoteAsync(repoUrl, log);

                repoRemote.Clone(repoUrl, null, masterGitRepoPath, masterRepoGitDirPath);
            }
            // The master folder already exists.  We are probably resuming with a different --git-dir-parent setting, or the .gitdir parent was cleaned.
            else
            {
                string masterRepoPossibleGitDirPath = Path.Combine(masterGitRepoPath, ".git");
                // The master folder has a full .gitdir.  Relocate it to the .gitdir parent directory and update to redirect to that.
                if (Directory.Exists(masterRepoPossibleGitDirPath))
                {
                    log.LogDebug($".gitdir {masterRepoPossibleGitDirPath} exists in {masterGitRepoPath}");

                    // Check if the .gitdir is already where we expect it to be first.
                    if (Path.GetFullPath(masterRepoPossibleGitDirPath) != Path.GetFullPath(masterRepoGitDirPath))
                    {
                        log.LogDebug($"Moving .gitdir {masterRepoPossibleGitDirPath} to expected location {masterRepoGitDirPath}");
                        Directory.Move(masterRepoPossibleGitDirPath, masterRepoGitDirPath);
                        File.WriteAllText(masterRepoPossibleGitDirPath, gitDirRedirect);
                    }
                }
                // The master folder has a .gitdir redirect.  Relocate its .gitdir to where we expect and update the redirect.
                else if (File.Exists(masterRepoPossibleGitDirPath))
                {
                    log.LogDebug($"Master repo {masterGitRepoPath} has a .gitdir redirect");

                    string relocatedGitDirPath = File.ReadAllText(masterRepoPossibleGitDirPath).Substring(GitDirRedirectPrefix.Length);
                    if (Path.GetFullPath(relocatedGitDirPath) != Path.GetFullPath(masterRepoGitDirPath))
                    {
                        log.LogDebug($"Existing .gitdir redirect of {relocatedGitDirPath} does not match expected {masterRepoGitDirPath}, moving .gitdir and updating redirect");
                        Directory.Move(relocatedGitDirPath, masterRepoGitDirPath);
                        File.WriteAllText(masterRepoPossibleGitDirPath, gitDirRedirect);
                    }
                }
                // This repo is orphaned.  Since it's supposed to be our master copy, adopt it.
                else
                {
                    log.LogDebug($"Master repo {masterGitRepoPath} is orphaned, adding .gitdir redirect");
                    File.WriteAllText(masterRepoPossibleGitDirPath, gitDirRedirect);
                }
            }
        }
Beispiel #10
0
        /// <summary>
        /// Adds a new channel with the specified name.
        /// </summary>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

                // If the user tried to mark as internal, indicate that this is currently
                // unsupported.
                if (_options.Internal)
                {
                    Logger.LogError("Cannot currently mark channels as internal.");
                    return(Constants.ErrorCode);
                }

                Channel newChannelInfo = await remote.CreateChannelAsync(_options.Name, _options.Classification);

                switch (_options.OutputFormat)
                {
                case DarcOutputType.json:
                    Console.WriteLine(JsonConvert.SerializeObject(
                                          new
                    {
                        id             = newChannelInfo.Id,
                        name           = newChannelInfo.Name,
                        classification = newChannelInfo.Classification
                    },
                                          Formatting.Indented));
                    break;

                case DarcOutputType.text:
                    Console.WriteLine($"Successfully created new channel with name '{_options.Name}' and id {newChannelInfo.Id}.");
                    break;

                default:
                    throw new NotImplementedException($"Output type {_options.OutputFormat} not supported by add-channel");
                }

                return(Constants.SuccessCode);
            }
            catch (RestApiException e) when(e.Response.Status == (int)HttpStatusCode.Conflict)
            {
                Logger.LogError($"An existing channel with name '{_options.Name}' already exists");
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to create new channel.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #11
0
        /// <summary>
        ///     Assigns a build to a channel.
        /// </summary>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

                // Find the build to give someone info
                Build build = await remote.GetBuildAsync(_options.Id);

                if (build == null)
                {
                    Console.WriteLine($"Could not find a build with id '{_options.Id}'");
                    return(Constants.ErrorCode);
                }

                Channel targetChannel = await UxHelpers.ResolveSingleChannel(remote, _options.Channel);

                if (targetChannel == null)
                {
                    return(Constants.ErrorCode);
                }

                if (build.Channels.Any(c => c.Id == targetChannel.Id))
                {
                    Console.WriteLine($"Build '{build.Id}' has already been assigned to '{targetChannel.Name}'");
                    return(Constants.SuccessCode);
                }

                Console.WriteLine($"Assigning the following build to channel '{targetChannel.Name}':");
                Console.WriteLine();
                OutputHelpers.PrintBuild(build);

                await remote.AssignBuildToChannel(_options.Id, targetChannel.Id);

                // Be helpful. Let the user know what will happen.
                string buildRepo = build.GitHubRepository ?? build.AzureDevOpsRepository;
                List <Subscription> applicableSubscriptions = (await remote.GetSubscriptionsAsync(
                                                                   sourceRepo: buildRepo, channelId: targetChannel.Id)).ToList();

                PrintSubscriptionInfo(applicableSubscriptions);

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, $"Error: Failed to assign build '{_options.Id}' to channel '{_options.Channel}'.");
                return(Constants.ErrorCode);
            }
        }
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                RemoteFactory remoteFactory = new RemoteFactory(_options);
                var           barOnlyRemote = await remoteFactory.GetBarOnlyRemoteAsync(Logger);

                List <DefaultChannel> defaultChannels = (await barOnlyRemote.GetDefaultChannelsAsync()).ToList();
                defaultChannels.Add(
                    new DefaultChannel(0, "https://github.com/dotnet/arcade", true)
                {
                    Branch  = "refs/heads/master",
                    Channel = await barOnlyRemote.GetChannelAsync(".NET Tools - Latest")
                }
                    );
                List <Subscription> subscriptions = (await barOnlyRemote.GetSubscriptionsAsync()).ToList();

                // Build, then prune out what we don't want to see if the user specified
                // channels.
                DependencyFlowGraph flowGraph = DependencyFlowGraph.Build(defaultChannels, subscriptions);

                Channel targetChannel = null;
                if (!string.IsNullOrEmpty(_options.Channel))
                {
                    // Resolve the channel.
                    targetChannel = await UxHelpers.ResolveSingleChannel(barOnlyRemote, _options.Channel);

                    if (targetChannel == null)
                    {
                        return(Constants.ErrorCode);
                    }
                }

                if (targetChannel != null)
                {
                    flowGraph.PruneGraph(node => IsInterestingNode(targetChannel, node), edge => IsInterestingEdge(edge));
                }

                await LogGraphViz(targetChannel, flowGraph);

                return(Constants.SuccessCode);
            }
            catch (Exception exc)
            {
                Logger.LogError(exc, "Something failed while getting the dependency graph.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #13
0
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

                await remote.AddDefaultChannelAsync(_options.Repository, _options.Branch, _options.Channel);

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to add a new default channel association.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #14
0
        private void Load(string librariesDirectory, string targetDirectory, State state = null)
        {
            if (librariesDirectory != targetDirectory)
            {
                DirectoryCopyAndOverwrite(librariesDirectory, targetDirectory);
            }
            EmptyStagingFolder();
            ConfigurationManager.RefreshSection("appSettings");
            RefreshAdditionalConfigurationSections();
            _nextDomain = AppDomain.CreateDomain("Ad2", null, _info);
            RemoteFactory factory = _nextDomain.CreateInstanceAndUnwrap("Connector", "Connector.RemoteFactory") as RemoteFactory;

            _nextService = factory.Create(targetDirectory, _dllName, _className);
            _folders.Swap();
            _nextService.SetState(state);
            _nextService.Start();
        }
Beispiel #15
0
        private static async Task HandleMasterCopyWithGitDirPath(RemoteFactory remoteFactory, string repoUrl, string masterGitRepoPath, string masterRepoGitDirPath, ILogger log)
        {
            string gitDirRedirect = GetGitDirRedirectString(masterRepoGitDirPath);

            log.LogDebug($"Starting master copy for {repoUrl} in {masterGitRepoPath} with .gitdir {masterRepoGitDirPath}");

            // the .gitdir exists already.  We are resuming with the same --git-dir-parent setting.
            if (Directory.Exists(masterRepoGitDirPath))
            {
                HandleMasterCopyWithExistingGitDir(masterGitRepoPath, masterRepoGitDirPath, log, gitDirRedirect);
            }
            // The .gitdir does not exist.  This could be a new clone or resuming with a different --git-dir-parent setting.
            else
            {
                await HandleMasterCopyAndCreateGitDir(remoteFactory, repoUrl, masterGitRepoPath, masterRepoGitDirPath, gitDirRedirect, log);
            }
        }
Beispiel #16
0
        public override async Task EvaluateAsync()
        {
            // Build the repository graph starting at the repo + branch and then look for cycles.
            // Build without build lookups, toolsets, etc. to minimize time. Toolset dependencies also break
            // product dependency cycles, so that eliminates some analysis

            DependencyGraphBuildOptions options = new DependencyGraphBuildOptions
            {
                EarlyBuildBreak   = EarlyBreakOn.NoEarlyBreak,
                IncludeToolset    = false,
                LookupBuilds      = false,
                NodeDiff          = NodeDiff.None,
                ComputeCyclePaths = true
            };

            // Evaluate and find out what the latest is on the branch
            var remote = await RemoteFactory.GetRemoteAsync(Repository, Logger);

            var commit = await remote.GetLatestCommitAsync(Repository, Branch);

            if (commit == null)
            {
                // If there were no commits, then there can be no cycles. This would be typical of newly
                // created branches.
                Result = HealthResult.Passed;
                Cycles = new List <List <string> >();
                return;
            }

            DependencyGraph graph =
                await DependencyGraph.BuildRemoteDependencyGraphAsync(RemoteFactory, Repository, commit, options, Logger);

            // Check to see whether there are any cycles.
            // Boil down the cycles into just the repositories involved
            Cycles = graph.Cycles.Select(cycle => cycle.Select(graphNode => graphNode.Repository));

            if (Cycles.Any())
            {
                Result = HealthResult.Failed;
            }
            else
            {
                Result = HealthResult.Passed;
            }
        }
Beispiel #17
0
        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);
            }
        }
        /// <summary>
        /// Retrieve information about the default association between builds of a specific branch/repo
        /// and a channel.
        /// </summary>
        /// <returns></returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

                IEnumerable <DefaultChannel> defaultChannels = (await remote.GetDefaultChannelsAsync())
                                                               .Where(defaultChannel =>
                {
                    return((string.IsNullOrEmpty(_options.SourceRepository) ||
                            defaultChannel.Repository.Contains(_options.SourceRepository, StringComparison.OrdinalIgnoreCase)) &&
                           (string.IsNullOrEmpty(_options.Branch) ||
                            defaultChannel.Branch.Contains(_options.Branch, StringComparison.OrdinalIgnoreCase)) &&
                           (string.IsNullOrEmpty(_options.Channel) ||
                            defaultChannel.Channel.Name.Contains(_options.Channel, StringComparison.OrdinalIgnoreCase)));
                })
                                                               .OrderBy(df => df.Repository);

                if (defaultChannels.Count() == 0)
                {
                    Console.WriteLine("No matching channels were found.");
                }

                // Write out a simple list of each channel's name
                foreach (DefaultChannel defaultChannel in defaultChannels)
                {
                    Console.WriteLine(UxHelpers.GetDefaultChannelDescriptionString(defaultChannel));
                }

                return(Constants.SuccessCode);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to retrieve default channel information.");
                return(Constants.ErrorCode);
            }
        }
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                RemoteFactory remoteFactory = new RemoteFactory(_options);
                var           barOnlyRemote = await remoteFactory.GetBarOnlyRemoteAsync(Logger);

                Channel targetChannel = null;
                if (!string.IsNullOrEmpty(_options.Channel))
                {
                    // Resolve the channel.
                    targetChannel = await UxHelpers.ResolveSingleChannel(barOnlyRemote, _options.Channel);

                    if (targetChannel == null)
                    {
                        return(Constants.ErrorCode);
                    }
                }

                var flowGraph = await barOnlyRemote.GetDependencyFlowGraph(
                    targetChannel?.Id ?? 0,
                    _options.Days,
                    includeArcade : true,
                    includeBuildTimes : _options.IncludeBuildTimes,
                    includeDisabledSubscriptions : _options.IncludeDisabledSubscriptions,
                    includedFrequencies : _options.IncludedFrequencies?.ToList());

                await LogGraphVizAsync(targetChannel, flowGraph, _options.IncludeBuildTimes);

                return(Constants.SuccessCode);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                return(Constants.ErrorCode);
            }
            catch (Exception exc)
            {
                Logger.LogError(exc, "Something failed while getting the dependency graph.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #20
0
        /// <summary>
        /// Retrieve Goal in minutes for Definition in a Channel.
        /// </summary>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote   = RemoteFactory.GetBarOnlyRemote(_options, Logger);
                Goal    goalInfo = await remote.GetGoalAsync(_options.Channel, _options.DefinitionId);

                Console.Write(goalInfo.Minutes);
                return(Constants.SuccessCode);
            }
            catch (RestApiException e) when(e.Response.Status == (int)HttpStatusCode.NotFound)
            {
                Logger.LogError(e, $"Cannot find Channel '{_options.Channel}'.");
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, $"Unable to create goal for Channel : '{_options.Channel}' and DefinitionId : '{_options.DefinitionId}'.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #21
0
        /// <summary>
        /// Retrieve information about channels
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

                var allChannels = await remote.GetChannelsAsync();

                // Write out a simple list of each channel's name
                foreach (var channel in allChannels)
                {
                    Console.WriteLine(channel.Name);
                }

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to retrieve channels");
                return(Constants.ErrorCode);
            }
        }
Beispiel #22
0
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetRemote(_options, _options.Repository, Logger);

                if (!(await UxHelpers.VerifyAndConfirmBranchExistsAsync(remote, _options.Repository, _options.Branch, !_options.NoConfirmation)))
                {
                    Console.WriteLine("Aborting default channel creation.");
                    return(Constants.ErrorCode);
                }

                await remote.AddDefaultChannelAsync(_options.Repository, _options.Branch, _options.Channel);

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to add a new default channel association.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #23
0
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

                DefaultChannel resolvedChannel = await ResolveSingleChannel();

                if (resolvedChannel == null)
                {
                    return(Constants.ErrorCode);
                }

                await remote.DeleteDefaultChannelAsync(resolvedChannel.Id);

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed remove the default channel association.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #24
0
 private void Subscribe()
 {
     try
     {
         clientFactory = RemoteFactory.CreateClientFactory(new Subscriber(this));
         client        = clientFactory.CreateChannel();
         var communicationObject = client as ICommunicationObject;
         if (communicationObject != null)
         {
             log.Debug("register channel state change listeners");
             communicationObject.Closed  += Subscription_Closed;
             communicationObject.Faulted += Subscription_Faulted;
         }
         client.Subscribe();
         _isUnfaulted = true;
         log.InfoFormat("Successfully subscribed to [{0}]", RemoteFactory.PipeLocation);
     }
     catch (EndpointNotFoundException)
     {
         log.Debug("Service is not ready to receive subscriptions");
         // Subscription_Faulted callback will be triggered
     }
 }
Beispiel #25
0
        private static async Task HandleMasterCopyWithDefaultGitDir(RemoteFactory remoteFactory, string repoUrl, string masterGitRepoPath, string masterRepoGitDirPath, ILogger log)
        {
            log.LogDebug($"Starting master copy for {repoUrl} in {masterGitRepoPath} with default .gitdir");

            // The master folder doesn't exist.  Just clone and set everything up for the first time.
            if (!Directory.Exists(masterGitRepoPath))
            {
                log.LogInformation($"Cloning master copy of {repoUrl} into {masterGitRepoPath}");
                IRemote repoRemote = await remoteFactory.GetRemoteAsync(repoUrl, log);

                repoRemote.Clone(repoUrl, null, masterGitRepoPath, masterRepoGitDirPath);
            }
            // The master folder already exists.  We are probably resuming with a different --git-dir-parent setting, or the .gitdir parent was cleaned.
            else
            {
                log.LogDebug($"Checking for existing .gitdir in {masterGitRepoPath}");
                string masterRepoPossibleGitDirPath = Path.Combine(masterGitRepoPath, ".git");
                // This repo is not in good shape for us.  It needs to be deleted and recreated.
                if (!Directory.Exists(masterRepoPossibleGitDirPath))
                {
                    throw new InvalidOperationException($"Repo {masterGitRepoPath} does not have a .git folder, but no git-dir-parent was specified.  Please fix the repo or specify a git-dir-parent.");
                }
            }
        }
Beispiel #26
0
        public override async Task <int> ExecuteAsync()
        {
            IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

            try
            {
                Subscription deletedSubscription = await remote.DeleteSubscriptionAsync(_options.Id);

                Console.WriteLine($"Successfully deleted subscription with id '{_options.Id}'");
                return(Constants.SuccessCode);
            }
            catch (RestApiException e) when(e.Response.StatusCode == HttpStatusCode.NotFound)
            {
                // Not found is fine to ignore.  If we get this, it will be an aggregate exception with an inner API exception
                // that has a response message code of NotFound.  Return success.
                Console.WriteLine($"Subscription with id '{_options.Id}' does not exist.");
                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, $"Failed to delete subscription with id '{_options.Id}'");
                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);
            }
        }
Beispiel #28
0
        /// <summary>
        /// Update local dependencies based on a specific channel.
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                DarcSettings darcSettings = darcSettings = LocalSettings.GetDarcSettings(_options, Logger);

                // TODO: PAT only used for pulling the Arcade eng/common dir,
                // so hardcoded to GitHub PAT right now. Must be more generic in the future.
                darcSettings.GitType = GitRepoType.GitHub;
                LocalSettings localSettings = LocalSettings.LoadSettingsFile(_options);

                darcSettings.GitRepoPersonalAccessToken = localSettings != null && !string.IsNullOrEmpty(localSettings.GitHubToken) ?
                                                          localSettings.GitHubToken :
                                                          _options.GitHubPat;

                IRemoteFactory remoteFactory = new RemoteFactory(_options);
                IRemote        barOnlyRemote = await remoteFactory.GetBarOnlyRemoteAsync(Logger);

                Local local = new Local(Logger);
                List <DependencyDetail> dependenciesToUpdate = new List <DependencyDetail>();
                bool   someUpToDate = false;
                string finalMessage = $"Local dependencies updated from channel '{_options.Channel}'.";

                // First we need to figure out what to query for. Load Version.Details.xml and
                // find all repository uris, optionally restricted by the input dependency parameter.
                IEnumerable <DependencyDetail> localDependencies = await local.GetDependenciesAsync(_options.Name, false);

                // If the source repository was specified, filter away any local dependencies not from that
                // source repository.
                if (!string.IsNullOrEmpty(_options.SourceRepository))
                {
                    localDependencies = localDependencies.Where(
                        dependency => dependency.RepoUri.Contains(_options.SourceRepository, StringComparison.OrdinalIgnoreCase));
                }

                if (!localDependencies.Any())
                {
                    Console.WriteLine("Found no dependencies to update.");
                    return(Constants.ErrorCode);
                }

                List <DependencyDetail> currentDependencies = localDependencies.ToList();

                if (!string.IsNullOrEmpty(_options.Name) && !string.IsNullOrEmpty(_options.Version))
                {
                    DependencyDetail dependency = currentDependencies.First();
                    dependency.Version = _options.Version;
                    dependenciesToUpdate.Add(dependency);

                    Console.WriteLine($"Updating '{dependency.Name}': '{dependency.Version}' => '{_options.Version}'");

                    finalMessage = $"Local dependency {_options.Name} updated to version '{_options.Version}'.";
                }
                else if (!string.IsNullOrEmpty(_options.PackagesFolder))
                {
                    try
                    {
                        dependenciesToUpdate.AddRange(GetDependenciesFromPackagesFolder(_options.PackagesFolder, currentDependencies));
                    }
                    catch (DarcException exc)
                    {
                        Logger.LogError(exc, $"Error: Failed to update dependencies based on folder '{_options.PackagesFolder}'");
                        return(Constants.ErrorCode);
                    }

                    finalMessage = $"Local dependencies updated based on packages folder {_options.PackagesFolder}.";
                }
                else if (_options.BARBuildId > 0)
                {
                    try
                    {
                        if (!_options.CoherencyOnly)
                        {
                            Console.WriteLine($"Looking up build with BAR id {_options.BARBuildId}");
                            var specificBuild = await barOnlyRemote.GetBuildAsync(_options.BARBuildId);

                            int nonCoherencyResult = await NonCoherencyUpdatesForBuildAsync(specificBuild, barOnlyRemote, currentDependencies, dependenciesToUpdate)
                                                     .ConfigureAwait(false);

                            if (nonCoherencyResult != Constants.SuccessCode)
                            {
                                Console.WriteLine("Error: Failed to update non-coherent parent tied dependencies.");
                                return(nonCoherencyResult);
                            }

                            string sourceRepo   = specificBuild.GitHubRepository ?? specificBuild.AzureDevOpsRepository;
                            string sourceBranch = specificBuild.GitHubBranch ?? specificBuild.AzureDevOpsBranch;

                            finalMessage = $"Local dependencies updated based on build with BAR id {_options.BARBuildId} " +
                                           $"({specificBuild.AzureDevOpsBuildNumber} from {sourceRepo}@{sourceBranch})";
                        }

                        int coherencyResult = await CoherencyUpdatesAsync(barOnlyRemote, remoteFactory, currentDependencies, dependenciesToUpdate)
                                              .ConfigureAwait(false);

                        if (coherencyResult != Constants.SuccessCode)
                        {
                            Console.WriteLine("Error: Failed to update coherent parent tied dependencies.");
                            return(coherencyResult);
                        }

                        finalMessage = string.IsNullOrEmpty(finalMessage) ? "Local dependencies successfully updated." : finalMessage;
                    }
                    catch (RestApiException e) when(e.Response.Status == 404)
                    {
                        Console.WriteLine($"Could not find build with BAR id '{_options.BARBuildId}'.");
                        return(Constants.ErrorCode);
                    }
                }
                else
                {
                    if (!_options.CoherencyOnly)
                    {
                        if (string.IsNullOrEmpty(_options.Channel))
                        {
                            Console.WriteLine($"Please supply either a channel name (--channel), a packages folder (--packages-folder) " +
                                              "a BAR build id (--id), or a specific dependency name and version (--name and --version).");
                            return(Constants.ErrorCode);
                        }

                        // Start channel query.
                        Task <Channel> channel = barOnlyRemote.GetChannelAsync(_options.Channel);

                        // Limit the number of BAR queries by grabbing the repo URIs and making a hash set.
                        // We gather the latest build for any dependencies that aren't marked with coherent parent
                        // dependencies, as those will be updated based on additional queries.
                        HashSet <string> repositoryUrisForQuery = currentDependencies
                                                                  .Where(dependency => string.IsNullOrEmpty(dependency.CoherentParentDependencyName))
                                                                  .Select(dependency => dependency.RepoUri)
                                                                  .ToHashSet();

                        ConcurrentDictionary <string, Task <Build> > getLatestBuildTaskDictionary = new ConcurrentDictionary <string, Task <Build> >();

                        Channel channelInfo = await channel;
                        if (channelInfo == null)
                        {
                            Console.WriteLine($"Could not find a channel named '{_options.Channel}'.");
                            return(Constants.ErrorCode);
                        }

                        foreach (string repoToQuery in repositoryUrisForQuery)
                        {
                            Console.WriteLine($"Looking up latest build of {repoToQuery} on {_options.Channel}");
                            var latestBuild = barOnlyRemote.GetLatestBuildAsync(repoToQuery, channelInfo.Id);
                            getLatestBuildTaskDictionary.TryAdd(repoToQuery, latestBuild);
                        }

                        // For each build, first go through and determine the required updates,
                        // updating the "live" dependency information as we go.
                        // Then run a second pass where we update any assets based on coherency information.
                        foreach (KeyValuePair <string, Task <Build> > buildKvPair in getLatestBuildTaskDictionary)
                        {
                            string repoUri = buildKvPair.Key;
                            Build  build   = await buildKvPair.Value;

                            if (build == null)
                            {
                                Logger.LogTrace($"No build of '{repoUri}' found on channel '{_options.Channel}'.");
                                continue;
                            }

                            int nonCoherencyResult = await NonCoherencyUpdatesForBuildAsync(build, barOnlyRemote, currentDependencies, dependenciesToUpdate)
                                                     .ConfigureAwait(false);

                            if (nonCoherencyResult != Constants.SuccessCode)
                            {
                                Console.WriteLine("Error: Failed to update non-coherent parent tied dependencies.");
                                return(nonCoherencyResult);
                            }
                        }
                    }

                    int coherencyResult = await CoherencyUpdatesAsync(barOnlyRemote, remoteFactory, currentDependencies, dependenciesToUpdate)
                                          .ConfigureAwait(false);

                    if (coherencyResult != Constants.SuccessCode)
                    {
                        Console.WriteLine("Error: Failed to update coherent parent tied dependencies.");
                        return(coherencyResult);
                    }
                }

                if (!dependenciesToUpdate.Any())
                {
                    // If we found some dependencies already up to date,
                    // then we consider this a success. Otherwise, we didn't even
                    // find matching dependencies so we should let the user know.
                    if (someUpToDate)
                    {
                        Console.WriteLine($"All dependencies are up to date.");
                        return(Constants.SuccessCode);
                    }
                    else
                    {
                        Console.WriteLine($"Found no dependencies to update.");
                        return(Constants.ErrorCode);
                    }
                }

                if (_options.DryRun)
                {
                    return(Constants.SuccessCode);
                }

                // Now call the local updater to run the update.
                await local.UpdateDependenciesAsync(dependenciesToUpdate, remoteFactory);

                Console.WriteLine(finalMessage);

                return(Constants.SuccessCode);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to update dependencies.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #29
0
        /// <summary>
        ///     Get a specific build of a repository
        /// </summary>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

                List <Build> matchingBuilds = null;
                if (_options.Id != 0)
                {
                    if (!string.IsNullOrEmpty(_options.BuildUri) ||
                        !string.IsNullOrEmpty(_options.Repo) ||
                        !string.IsNullOrEmpty(_options.Commit))
                    {
                        Console.WriteLine("--id should not be used with other options.");
                        return(Constants.ErrorCode);
                    }

                    matchingBuilds = new List <Build>()
                    {
                        await remote.GetBuildAsync(_options.Id)
                    };
                }
                else if (!string.IsNullOrEmpty(_options.BuildUri))
                {
                    if (_options.Id != 0 || !string.IsNullOrEmpty(_options.Repo) || !string.IsNullOrEmpty(_options.Commit))
                    {
                        Console.WriteLine("--uri should not be used with other options.");

                        return(Constants.ErrorCode);
                    }

                    Console.WriteLine("This option is not yet supported.");
                    return(Constants.ErrorCode);
                }
                else if (!string.IsNullOrEmpty(_options.Repo) || !string.IsNullOrEmpty(_options.Commit))
                {
                    if (string.IsNullOrEmpty(_options.Repo) != string.IsNullOrEmpty(_options.Commit))
                    {
                        Console.WriteLine("--repo and --commit should be used together.");
                        return(Constants.ErrorCode);
                    }
                    else if (_options.Id != 0 || !string.IsNullOrEmpty(_options.BuildUri))
                    {
                        Console.WriteLine("--repo and --commit should not be used with other options.");
                        return(Constants.ErrorCode);
                    }

                    matchingBuilds = (await remote.GetBuildsAsync(_options.Repo, _options.Commit)).ToList();
                }
                else
                {
                    Console.WriteLine("Please specify --id, --uri, or --repo and --commit to lookup a build.");
                    return(Constants.ErrorCode);
                }

                // Print the build info.
                if (!matchingBuilds.Any())
                {
                    Console.WriteLine($"Could not any builds matching the given criteria.");
                    return(Constants.ErrorCode);
                }

                switch (_options.OutputFormat)
                {
                case DarcOutputType.text:
                    foreach (Build build in matchingBuilds)
                    {
                        Console.Write(UxHelpers.GetTextBuildDescription(build));
                    }
                    break;

                case DarcOutputType.json:
                    Console.WriteLine(JsonConvert.SerializeObject(
                                          matchingBuilds.Select(build => UxHelpers.GetJsonBuildDescription(build)), Formatting.Indented));
                    break;

                default:
                    throw new NotImplementedException($"Output format type {_options.OutputFormat} not yet supported for get-build.");
                }

                return(Constants.SuccessCode);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to retrieve build information.");
                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);
            }
        }