예제 #1
0
        /// <summary>
        ///     Verify that a branch exists in the specified repo and contains a version details file.
        ///     If it does not, optionally prompt the user to confirm that they wish to continue.
        /// </summary>
        /// <param name="remote">Remote</param>
        /// <param name="repo">Repository that the branch should be in</param>
        /// <param name="branch">Branch to check the existence of</param>
        /// <param name="prompt">Prompt the user to verify that they want to continue</param>
        /// <returns>True if the branch exists, prompting is not desired, or if the user confirms that they want to continue. False otherwise.</returns>
        public static async Task <bool> VerifyAndConfirmBranchExistsAsync(IRemote remote, string repo, string branch, bool prompt)
        {
            const string regexPrefix = "-regex:";

            // IRemote doesn't currently provide a way for enumerating all branches in a repo, and the purpose of supporting regex is to allow new ones to match
            // So in this case we'll just prompt
            if (branch.StartsWith(regexPrefix, StringComparison.InvariantCultureIgnoreCase))
            {
                Console.WriteLine($"Warning: Regular expression '{branch.Substring(regexPrefix.Length)}' will be used to match on branches in '{repo}'.");
                Console.WriteLine("To ensure dependency updates (where desired), please verify all branches matching this pattern contain an eng/Version.Details.xml file.");
                return(!prompt || PromptForYesNo("Continue?"));
            }

            try
            {
                branch = GitHelpers.NormalizeBranchName(branch);

                await remote.GetDependenciesAsync(repo, branch);
            }
            catch (DependencyFileNotFoundException)
            {
                Console.WriteLine($"Warning: Could not find an eng/Version.Details.xml at '{repo}@{branch}'. Dependency updates may not happen as expected.");
                if (prompt)
                {
                    return(PromptForYesNo("Continue?"));
                }
            }

            return(true);
        }
예제 #2
0
        private static async Task CloneRemoteRepoAndDependencies(RemoteFactory remoteFactory, string reposFolder, string repoUri, string commit, bool includeToolset, ILogger logger)
        {
            IRemote rootRepoRemote = await remoteFactory.GetRemoteAsync(repoUri, logger);

            IEnumerable <DependencyDetail> rootDependencies = await rootRepoRemote.GetDependenciesAsync(repoUri, commit);

            rootDependencies = FilterToolsetDependencies(rootDependencies, includeToolset);

            if (!rootDependencies.Any())
            {
                string repoPath = GetRepoDirectory(reposFolder, repoUri, commit);
                if (Directory.Exists(repoPath))
                {
                    logger.LogDebug($"Repo path {repoPath} already exists, assuming we cloned already and skipping");
                }
                else
                {
                    logger.LogInformation($"Remote repo {repoUri}@{commit} has no dependencies.  Cloning shallowly into {repoPath}");
                    IRemote repoRemote = await remoteFactory.GetRemoteAsync(repoUri, logger);

                    repoRemote.Clone(repoUri, commit, repoPath);
                }
                return;
            }

            DependencyGraphBuildOptions graphBuildOptions = new DependencyGraphBuildOptions()
            {
                IncludeToolset = includeToolset,
                LookupBuilds   = true,
                NodeDiff       = NodeDiff.None
            };

            logger.LogDebug($"Building depdendency graph for {repoUri}@{commit} with {rootDependencies.Count()} dependencies");
            DependencyGraph graph = await DependencyGraph.BuildRemoteDependencyGraphAsync(
                remoteFactory,
                rootDependencies,
                repoUri,
                commit,
                graphBuildOptions,
                logger);

            foreach (DependencyGraphNode repo in graph.Nodes)
            {
                string repoPath = GetRepoDirectory(reposFolder, repo.Repository, repo.Commit);
                if (Directory.Exists(repoPath))
                {
                    logger.LogDebug($"Repo path {repoPath} already exists, assuming we cloned already and skipping");
                }
                else
                {
                    logger.LogInformation($"Cloning {repo.Repository}@{repo.Commit} into {repoPath}");
                    IRemote repoRemote = await remoteFactory.GetRemoteAsync(repo.Repository, logger);

                    repoRemote.Clone(repo.Repository, repo.Commit, repoPath);
                }
            }
        }
        /// <summary>
        ///     Evaluate the metric.
        /// </summary>
        /// <param name="remoteFactory">Remote factory</param>
        /// <returns>True if the metric passed, false otherwise</returns>
        public override async Task EvaluateAsync()
        {
            IRemote remote = await RemoteFactory.GetRemoteAsync(Repository, Logger);

            Logger.LogInformation("Evaluating subscription health metrics for {repo}@{branch}", Repository, Branch);

            // Get subscriptions that target this repo/branch
            Subscriptions = (await remote.GetSubscriptionsAsync(targetRepo: Repository))
                            .Where(s => s.TargetBranch.Equals(Branch, StringComparison.OrdinalIgnoreCase)).ToList();

            // Get the dependencies of the repository/branch. Skip pinned and subscriptions tied to another
            // dependency (coherent parent), as well as those not selected by the dependency selector.
            try
            {
                Dependencies = (await remote.GetDependenciesAsync(Repository, Branch))
                               .Where(d => !d.Pinned && string.IsNullOrEmpty(d.CoherentParentDependencyName))
                               .Where(d => DependencySelector(d))
                               .ToList();
            }
            catch (DependencyFileNotFoundException)
            {
                // When the dependency file is not found, then we're good as long as this repo is not
                // targeted by any subscriptions
                if (Subscriptions.Any())
                {
                    MissingVersionDetailsFile = true;
                    Result = HealthResult.Failed;
                    return;
                }
                else
                {
                    Result = HealthResult.Passed;
                    return;
                }
            }

            Dictionary <string, Subscription> latestAssets = await GetLatestAssetsAndComputeConflicts(remote);

            ComputeSubscriptionUse(latestAssets);

            // Determine the result. A conflict or missing subscription is an error.
            // A non-flowing subscription or unused subscription is a warning
            if (DependenciesMissingSubscriptions.Any() ||
                ConflictingSubscriptions.Any())
            {
                Result = HealthResult.Failed;
            }
            else if (UnusedSubscriptions.Any() || DependenciesThatDoNotFlow.Any())
            {
                Result = HealthResult.Warning;
            }
            else
            {
                Result = HealthResult.Passed;
            }
        }
예제 #4
0
        /// <summary>
        ///     Verify that a branch exists in the specified repo and contains a version details file.
        ///     If it does not, optionally prompt the user to confirm that they wish to continue.
        /// </summary>
        /// <param name="remote">Remote</param>
        /// <param name="repo">Repository that the branch should be in</param>
        /// <param name="branch">Branch to check the existence of</param>
        /// <param name="prompt">Prompt the user to verify that they want to continue</param>
        /// <returns>True if the branch exists, prompting is not desired, or if the user confirms that they want to continue. False otherwise.</returns>
        public static async Task <bool> VerifyAndConfirmBranchExistsAsync(IRemote remote, string repo, string branch, bool prompt)
        {
            try
            {
                var dependencies = await remote.GetDependenciesAsync(repo, branch);
            }
            catch (DependencyFileNotFoundException)
            {
                Console.WriteLine($"Warning: Could not find an eng/Version.Details.xml at '{repo}@{branch}'. Dependency updates may not happen as expected.");
                if (prompt)
                {
                    return(PromptForYesNo("Continue?"));
                }
            }

            return(true);
        }
예제 #5
0
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IEnumerable <DependencyDetail> rootDependencies = null;
                DependencyGraph graph;
                RemoteFactory   remoteFactory = new RemoteFactory(_options);

                if (!_options.Local)
                {
                    NodeDiff diffOption = NodeDiff.None;
                    // Check node diff options
                    switch (_options.DeltaFrom.ToLowerInvariant())
                    {
                    case "none":
                        break;

                    case "newest-in-channel":
                        diffOption = NodeDiff.LatestInChannel;
                        break;

                    case "newest-in-graph":
                        diffOption = NodeDiff.LatestInGraph;
                        break;

                    default:
                        Console.WriteLine("Unknown --delta-from option, please see help.");
                        return(Constants.ErrorCode);
                    }

                    // If the repo uri and version are set, then call the graph
                    // build operation based on those.  Both should be set in this case.
                    // If they are not set, then gather the initial set based on the local repository,
                    // and then call the graph build with that root set.

                    if (!string.IsNullOrEmpty(_options.RepoUri))
                    {
                        if (string.IsNullOrEmpty(_options.Version))
                        {
                            Console.WriteLine("If --repo is set, --version should be supplied");
                            return(Constants.ErrorCode);
                        }

                        Console.WriteLine($"Getting root dependencies from {_options.RepoUri}@{_options.Version}...");

                        // Grab root dependency set. The graph build can do this, but
                        // if an original asset name is passed, then this will do the initial filtering.
                        IRemote rootRepoRemote = await remoteFactory.GetRemoteAsync(_options.RepoUri, Logger);

                        rootDependencies = await rootRepoRemote.GetDependenciesAsync(
                            _options.RepoUri,
                            _options.Version,
                            _options.AssetName);
                    }
                    else
                    {
                        if (!string.IsNullOrEmpty(_options.Version))
                        {
                            Console.WriteLine("If --version is supplied, then --repo is required");
                            return(Constants.ErrorCode);
                        }

                        Console.WriteLine($"Getting root dependencies from local repository...");

                        // Grab root dependency set from local repo
                        Local local = new Local(Logger);
                        rootDependencies = await local.GetDependenciesAsync(
                            _options.AssetName);
                    }

                    Console.WriteLine($"Building repository dependency graph...");

                    rootDependencies = FilterToolsetDependencies(rootDependencies);

                    if (!rootDependencies.Any())
                    {
                        Console.WriteLine($"No root dependencies found, exiting.");
                        return(Constants.ErrorCode);
                    }

                    DependencyGraphBuildOptions graphBuildOptions = new DependencyGraphBuildOptions()
                    {
                        IncludeToolset = _options.IncludeToolset,
                        LookupBuilds   = diffOption != NodeDiff.None || !_options.SkipBuildLookup,
                        NodeDiff       = diffOption
                    };

                    // Build graph
                    graph = await DependencyGraph.BuildRemoteDependencyGraphAsync(
                        remoteFactory,
                        rootDependencies,
                        _options.RepoUri ?? LocalHelpers.GetRootDir(Logger),
                        _options.Version ?? LocalHelpers.GetGitCommit(Logger),
                        graphBuildOptions,
                        Logger);
                }
                else
                {
                    Console.WriteLine($"Getting root dependencies from local repository...");

                    Local local = new Local(Logger);
                    rootDependencies = await local.GetDependenciesAsync(
                        _options.AssetName);

                    rootDependencies = FilterToolsetDependencies(rootDependencies);

                    if (!rootDependencies.Any())
                    {
                        Console.WriteLine($"No root dependencies found, exiting.");
                        return(Constants.ErrorCode);
                    }

                    Console.WriteLine($"Building repository dependency graph from local information...");

                    DependencyGraphBuildOptions graphBuildOptions = new DependencyGraphBuildOptions()
                    {
                        IncludeToolset = _options.IncludeToolset,
                        LookupBuilds   = false,
                        NodeDiff       = NodeDiff.None
                    };

                    // Build graph using only local resources
                    graph = await DependencyGraph.BuildLocalDependencyGraphAsync(
                        rootDependencies,
                        graphBuildOptions,
                        Logger,
                        LocalHelpers.GetRootDir(Logger),
                        LocalHelpers.GetGitCommit(Logger),
                        _options.ReposFolder,
                        _options.RemotesMap);
                }

                if (_options.Flat)
                {
                    await LogFlatDependencyGraph(graph);
                }
                else
                {
                    await LogDependencyGraph(graph);
                }

                if (!string.IsNullOrEmpty(_options.GraphVizOutputFile))
                {
                    await LogGraphViz(graph);
                }

                return(Constants.SuccessCode);
            }
            catch (Exception exc)
            {
                Logger.LogError(exc, "Something failed while getting the dependency graph.");
                return(Constants.ErrorCode);
            }
        }
예제 #6
0
        private static async Task <IEnumerable <DependencyDetail> > GetDependenciesAsync(
            IRemoteFactory remoteFactory,
            bool remote,
            ILogger logger,
            string repoUri,
            string commit,
            bool includeToolset,
            IEnumerable <string> remotesMap,
            string reposFolder,
            string testPath = null)
        {
            try
            {
                IEnumerable <DependencyDetail> dependencies = null;

                if (!string.IsNullOrEmpty(testPath))
                {
                    testPath = Path.Combine(
                        testPath,
                        repoUri,
                        commit);

                    if (Directory.Exists(testPath))
                    {
                        Local local = new Local(logger, testPath);
                        dependencies = await local.GetDependenciesAsync();
                    }
                }
                else if (remote)
                {
                    IRemote remoteClient = await remoteFactory.GetRemoteAsync(repoUri, logger);

                    dependencies = await remoteClient.GetDependenciesAsync(
                        repoUri,
                        commit);
                }
                else
                {
                    string repoPath = GetRepoPath(repoUri, commit, remotesMap, reposFolder, logger);

                    if (!string.IsNullOrEmpty(repoPath))
                    {
                        Local  local        = new Local(logger);
                        string fileContents = LocalHelpers.GitShow(
                            repoPath,
                            commit,
                            VersionFiles.VersionDetailsXml,
                            logger);
                        dependencies = local.GetDependenciesFromFileContents(fileContents);
                    }
                }

                if (!includeToolset)
                {
                    dependencies = dependencies.Where(dependency => dependency.Type != DependencyType.Toolset);
                }
                return(dependencies);
            }
            catch (DependencyFileNotFoundException)
            {
                // This is not an error. Dependencies can be specified with explicit shas that
                // may not have eng/Version.Details.xml at that point.
                logger.LogWarning($"{repoUri}@{commit} does not have an eng/Version.Details.xml.");
                return(null);
            }
            catch (Exception exc)
            {
                logger.LogError(exc, $"Something failed while trying the fetch the " +
                                $"dependencies of repo '{repoUri}' at sha " +
                                $"'{commit}'");
                throw;
            }
        }