Beispiel #1
0
        /// <summary>
        /// Retrieve information about a particular subscription's history.
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                DarcSettings darcSettings = LocalSettings.GetDarcSettings(_options, Logger);
                // No need to set up a git type or PAT here.
                Remote remote = new Remote(darcSettings, Logger);

                // Given the ID, grab the subscription history items.
                var history = await remote.GetSubscriptionHistoryAsync(_options.SubscriptionId);

                foreach (var historyItem in history)
                {
                    Console.WriteLine($"{historyItem.Timestamp.Value.LocalDateTime}: ({(historyItem.Success.Value ? "Success" : "Failure")}) - {historyItem.Action}");
                    if (!historyItem.Success.Value)
                    {
                        Console.WriteLine($"  Error Message: {historyItem.ErrorMessage}");
                        Console.WriteLine($"  Retry Command: darc retry-subscription-update --id {_options.SubscriptionId} --update {historyItem.Timestamp.Value.ToUnixTimeSeconds()}");
                    }
                }

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to retrieve subscription history");
                return(Constants.ErrorCode);
            }
        }
Beispiel #2
0
        /// <summary>
        /// Deletes a channel by name
        /// </summary>
        /// <returns></returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                DarcSettings darcSettings = LocalSettings.GetDarcSettings(_options, Logger);
                // No need to set up a git type or PAT here.
                Remote remote = new Remote(darcSettings, Logger);

                // Get the ID of the channel with the specified name.
                Channel existingChannel = (await remote.GetChannelsAsync()).Where(channel => channel.Name.Equals(_options.Name, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

                if (existingChannel == null)
                {
                    Logger.LogError($"Could not find channel with name '{_options.Name}'");
                    return(Constants.ErrorCode);
                }

                await remote.DeleteChannelAsync(existingChannel.Id.Value);

                Console.WriteLine($"Successfully deleted channel '{existingChannel.Name}'.");

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to delete channel.");
                return(Constants.ErrorCode);
            }
        }
        public override async Task <int> ExecuteAsync()
        {
            DarcSettings darcSettings = LocalSettings.GetDarcSettings(_options, Logger);
            // No need to set up a git type or PAT here.
            Remote remote = new Remote(darcSettings, Logger);

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

                Console.WriteLine($"Successfully deleted subscription with id '{_options.Id}'");
                return(Constants.SuccessCode);
            }
            catch (ApiErrorException 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);
            }
        }
Beispiel #4
0
        /// <summary>
        /// Retrieve the settings from the combination of the command line
        /// options and the user's darc settings file.
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <returns>Darc settings for use in remote commands</returns>
        /// <remarks>The command line takes precedence over the darc settings file.</remarks>
        public static DarcSettings GetDarcSettings(CommandLineOptions options, ILogger logger)
        {
            DarcSettings darcSettings = new DarcSettings();

            darcSettings.GitType = GitRepoType.None;

            try
            {
                LocalSettings localSettings = LoadSettingsFile();
                darcSettings.BuildAssetRegistryBaseUri  = localSettings.BuildAssetRegistryBaseUri;
                darcSettings.BuildAssetRegistryPassword = localSettings.BuildAssetRegistryPassword;
            }
            catch (FileNotFoundException)
            {
                // Doesn't have a settings file, which is not an error
            }
            catch (Exception e)
            {
                logger.LogWarning(e, $"Failed to load the darc settings file, may be corrupted");
            }

            // Override if non-empty on command line
            darcSettings.BuildAssetRegistryBaseUri = OverrideIfSet(darcSettings.BuildAssetRegistryBaseUri,
                                                                   options.BuildAssetRegistryBaseUri);
            darcSettings.BuildAssetRegistryPassword = OverrideIfSet(darcSettings.BuildAssetRegistryPassword,
                                                                    options.BuildAssetRegistryPassword);

            // Currently the darc settings only has one PAT type which is interpreted differently based
            // on the git type (Azure DevOps vs. GitHub).  For now, leave this setting empty until
            // we know what we are talking to.

            return(darcSettings);
        }
        /// <summary>
        /// Retry a specified subscription update.
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                DarcSettings darcSettings = LocalSettings.GetDarcSettings(_options, Logger);
                // No need to set up a git type or PAT here.
                Remote remote = new Remote(darcSettings, Logger);

                Console.WriteLine("Attempting to retry update...");
                // Attempt to retry the update.
                // TODO: Would be great if the controller returned the update result here.
                await remote.RetrySubscriptionUpdateAsync(_options.SubscriptionId, _options.UpdateId);

                Console.WriteLine("Update retry queued, please check subscription history in a few moments.");
                return(Constants.SuccessCode);
            }
            catch (ApiErrorException e) when(e.Response.StatusCode == HttpStatusCode.NotFound)
            {
                Console.WriteLine($"Subscription with id '{_options.SubscriptionId}' or subscription update with id '{_options.UpdateId}' was not found.");
                return(Constants.ErrorCode);
            }
            catch (ApiErrorException e) when(e.Response.StatusCode == HttpStatusCode.NotAcceptable)
            {
                Console.WriteLine($"Update '{_options.UpdateId}' did not fail and cannot be retried");
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to retry subscription update.");
                return(Constants.ErrorCode);
            }
        }
        /// <summary>
        /// Adds a new channel with the specified name.
        /// </summary>
        /// <returns>Process exit code.</returns>
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                DarcSettings darcSettings = LocalSettings.GetDarcSettings(_options, Logger);
                // No need to set up a git type or PAT here.
                Remote remote = new Remote(darcSettings, 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);

                Console.WriteLine($"Successfully created new channel with name '{_options.Name}'.");

                return(Constants.SuccessCode);
            }
            catch (ApiErrorException e) when(e.Response.StatusCode == 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);
            }
        }
        /// <summary>
        /// Create the graph or graphs dependending on the amount of base dependencies
        /// passed in as an input.
        /// </summary>
        /// <param name="dependencies">Input dependencies.</param>
        /// <param name="darcSettings">The Darc settings.</param>
        /// <returns>Collection of graph nodes.</returns>
        private async Task <List <DependencyGraph> > CreateGraphAsync(
            IEnumerable <DependencyDetail> dependencies,
            DarcSettings darcSettings)
        {
            List <DependencyGraph> graph = new List <DependencyGraph>();

            if (string.IsNullOrEmpty(_options.AssetName))
            {
                await Task.WhenAll(dependencies.Select(
                                       dependency => AddNodeToGraphAsync(
                                           darcSettings,
                                           dependency,
                                           graph)));
            }
            else
            {
                DependencyDetail dependency = dependencies.FirstOrDefault();

                if (dependency == null)
                {
                    Logger.LogError($"Dependency '{_options.AssetName}' was not found.");
                    return(null);
                }

                await AddNodeToGraphAsync(darcSettings, dependency, graph);
            }

            return(graph);
        }
Beispiel #8
0
        /// <summary>
        /// Retrieve the settings from the combination of the command line
        /// options and the user's darc settings file.
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <returns>Darc settings for use in remote commands</returns>
        /// <remarks>The command line takes precedence over the darc settings file.</remarks>
        public static DarcSettings GetDarcSettings(CommandLineOptions options, ILogger logger, string repoUri = null)
        {
            LocalSettings localSettings = null;
            DarcSettings  darcSettings  = new DarcSettings();

            darcSettings.GitType = GitRepoType.None;

            try
            {
                localSettings = LoadSettingsFile(options);

                if (localSettings != null)
                {
                    darcSettings.BuildAssetRegistryBaseUri  = localSettings.BuildAssetRegistryBaseUri;
                    darcSettings.BuildAssetRegistryPassword = localSettings.BuildAssetRegistryPassword;
                }
                else
                {
                    darcSettings.BuildAssetRegistryBaseUri  = _defaultBuildAssetRegistryBaseUri;
                    darcSettings.BuildAssetRegistryPassword = options.BuildAssetRegistryPassword;
                }

                if (!string.IsNullOrEmpty(repoUri))
                {
                    if (repoUri.Contains("github"))
                    {
                        darcSettings.GitType             = GitRepoType.GitHub;
                        darcSettings.PersonalAccessToken = localSettings.GitHubToken;
                    }
                    else if (repoUri.Contains("dev.azure.com"))
                    {
                        darcSettings.GitType             = GitRepoType.AzureDevOps;
                        darcSettings.PersonalAccessToken = localSettings.AzureDevOpsToken;
                    }
                    else
                    {
                        logger.LogWarning($"Unknown repository '{repoUri}'");
                    }
                }
            }
            catch (Exception e)
            {
                logger.LogWarning(e, $"Failed to load the darc settings file, may be corrupted");
            }

            // Override if non-empty on command line
            darcSettings.BuildAssetRegistryBaseUri = OverrideIfSet(darcSettings.BuildAssetRegistryBaseUri,
                                                                   options.BuildAssetRegistryBaseUri);
            darcSettings.BuildAssetRegistryPassword = OverrideIfSet(darcSettings.BuildAssetRegistryPassword,
                                                                    options.BuildAssetRegistryPassword);

            // Currently the darc settings only has one PAT type which is interpreted differently based
            // on the git type (Azure DevOps vs. GitHub).  For now, leave this setting empty until
            // we know what we are talking to.

            return(darcSettings);
        }
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                Local local = new Local(LocalHelpers.GetGitDir(Logger), Logger);
                IEnumerable <DependencyDetail> dependencies = await local.GetDependenciesAsync(
                    _options.AssetName);

                DarcSettings darcSettings = null;

                if (_options.Remote)
                {
                    if (string.IsNullOrEmpty(_options.RepoUri))
                    {
                        Logger.LogError("If '--remote' is set '--repo-uri' is required.");

                        return(Constants.ErrorCode);
                    }

                    darcSettings = LocalSettings.GetDarcSettings(
                        _options,
                        Logger,
                        _options.RepoUri);
                    Remote remote = new Remote(darcSettings, Logger);
                    dependencies = await remote.GetDependenciesAsync(
                        _options.RepoUri,
                        _options.Branch,
                        _options.AssetName);
                }

                List <DependencyGraph> graph = await CreateGraphAsync(dependencies, darcSettings);

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

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

                return(Constants.SuccessCode);
            }
            catch (Exception exc)
            {
                Logger.LogError(exc, "Something failed while getting the dependency graph.");

                return(Constants.ErrorCode);
            }
        }
        private async Task AddNodeToGraphAsync(
            DarcSettings darcSettings,
            DependencyDetail dependency,
            List <DependencyGraph> graph)
        {
            DependencyGraph dependencyGraph = await DependencyGraph.GetDependencyGraphAsync(
                darcSettings,
                dependency,
                _options.Remote,
                Logger,
                _options.ReposFolder,
                _options.RemotesMap);

            graph.Add(dependencyGraph);
        }
Beispiel #11
0
        public async Task <IRemote> CreateAsync(string repoUrl, long installationId)
        {
            var settings = new DarcSettings();

            if (repoUrl.Contains("github.com"))
            {
                settings.GitType             = GitRepoType.GitHub;
                settings.PersonalAccessToken = await GitHubTokenProvider.GetTokenForInstallation(installationId);
            }
            else
            {
                settings.GitType             = GitRepoType.Vsts;
                settings.PersonalAccessToken = ""; // TODO: get this
            }

            return(new Remote(settings, LoggerFactory.CreateLogger <Remote>()));
        }
Beispiel #12
0
        /// <summary>
        ///     Get a remote for a specific repo.
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <param name="repoUrl">Repository url</param>
        /// <param name="logger">Logger</param>
        /// <returns>New remote</returns>
        public static IRemote GetRemote(CommandLineOptions options, string repoUrl, ILogger logger)
        {
            DarcSettings darcSettings = LocalSettings.GetDarcSettings(options, logger, repoUrl);

            if (darcSettings.GitType != GitRepoType.None &&
                string.IsNullOrEmpty(darcSettings.GitRepoPersonalAccessToken))
            {
                throw new DarcException($"No personal access token was provided for repo type '{darcSettings.GitType}'");
            }

            // If a temporary repository root was not provided, use the environment
            // provided temp directory.
            string temporaryRepositoryRoot = darcSettings.TemporaryRepositoryRoot;

            if (string.IsNullOrEmpty(temporaryRepositoryRoot))
            {
                temporaryRepositoryRoot = Path.GetTempPath();
            }

            IGitRepo gitClient = null;

            if (darcSettings.GitType == GitRepoType.GitHub)
            {
                gitClient = new GitHubClient(options.GitLocation, darcSettings.GitRepoPersonalAccessToken,
                                             logger,
                                             temporaryRepositoryRoot,
                                             // Caching not in use for Darc local client.
                                             null);
            }
            else if (darcSettings.GitType == GitRepoType.AzureDevOps)
            {
                gitClient = new AzureDevOpsClient(options.GitLocation, darcSettings.GitRepoPersonalAccessToken,
                                                  logger,
                                                  temporaryRepositoryRoot);
            }

            IBarClient barClient = null;

            if (!string.IsNullOrEmpty(darcSettings.BuildAssetRegistryPassword))
            {
                barClient = new MaestroApiBarClient(darcSettings.BuildAssetRegistryPassword,
                                                    darcSettings.BuildAssetRegistryBaseUri);
            }
            return(new Remote(gitClient, barClient, logger));
        }
Beispiel #13
0
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                DarcSettings darcSettings = LocalSettings.GetDarcSettings(_options, Logger);
                // No need to set up a git type or PAT here.
                Remote remote = new Remote(darcSettings, Logger);

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

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed remove the default channel association.");
                return(Constants.ErrorCode);
            }
        }
        public async Task <IRemote> CreateAsync(string repoUrl, long installationId)
        {
            var settings = new DarcSettings();

            if (repoUrl.Contains("github.com"))
            {
                if (installationId == default)
                {
                    throw new SubscriptionException($"No installation is avaliable for repository '{repoUrl}'");
                }

                settings.GitType             = GitRepoType.GitHub;
                settings.PersonalAccessToken = await GitHubTokenProvider.GetTokenForInstallation(installationId);
            }
            else
            {
                settings.GitType             = GitRepoType.AzureDevOps;
                settings.PersonalAccessToken = ""; // TODO: get this
            }

            return(new Remote(settings, LoggerFactory.CreateLogger <Remote>()));
        }
        /// <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
            {
                DarcSettings darcSettings = LocalSettings.GetDarcSettings(_options, Logger);
                // No need to set up a git type or PAT here.
                Remote remote = new Remote(darcSettings, Logger);

                var 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)));
                });

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

                // Write out a simple list of each channel's name
                foreach (var defaultChannel in defaultChannels)
                {
                    Console.WriteLine($"{defaultChannel.Repository} @ {defaultChannel.Branch} -> {defaultChannel.Channel.Name}");
                }

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to retrieve default channel information.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #16
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
            {
                DarcSettings darcSettings = LocalCommands.GetSettings(_options, Logger);
                // No need to set up a git type or PAT here.
                Remote remote = new Remote(darcSettings, 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);
            }
        }
        /// <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.PersonalAccessToken = localSettings != null && !string.IsNullOrEmpty(localSettings.GitHubToken) ?
                                                   localSettings.GitHubToken :
                                                   _options.GitHubPat;

                Remote remote = new Remote(darcSettings, Logger);
                Local  local  = new Local(LocalHelpers.GetGitDir(Logger), 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> dependencies = await local.GetDependenciesAsync(_options.Name);

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

                if (!string.IsNullOrEmpty(_options.Name) && !string.IsNullOrEmpty(_options.Version))
                {
                    DependencyDetail dependency = dependencies.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, dependencies));
                    }
                    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
                {
                    // Start channel query.
                    var channel = remote.GetChannelAsync(_options.Channel);

                    // Limit the number of BAR queries by grabbing the repo URIs and making a hash set.
                    var repositoryUrisForQuery = dependencies.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)
                    {
                        var latestBuild = remote.GetLatestBuildAsync(repoToQuery, channelInfo.Id.Value);
                        getLatestBuildTaskDictionary.TryAdd(repoToQuery, latestBuild);
                    }

                    // Now walk dependencies again and attempt the update
                    foreach (DependencyDetail dependency in dependencies)
                    {
                        Build build;
                        try
                        {
                            build = await getLatestBuildTaskDictionary[dependency.RepoUri];
                        }
                        catch (ApiErrorException e) when(e.Response.StatusCode == System.Net.HttpStatusCode.NotFound)
                        {
                            Logger.LogTrace($"No build of '{dependency.RepoUri}' found on channel '{_options.Channel}'.");
                            continue;
                        }

                        Asset buildAsset = build.Assets.Where(asset => asset.Name.Equals(dependency.Name, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
                        if (buildAsset == null)
                        {
                            Logger.LogTrace($"Dependency '{dependency.Name}' not found in latest build of '{dependency.RepoUri}' on '{_options.Channel}', skipping.");
                            continue;
                        }

                        if (buildAsset.Version == dependency.Version &&
                            buildAsset.Name == dependency.Name &&
                            build.Repository == dependency.RepoUri &&
                            build.Commit == dependency.Commit)
                        {
                            // No changes
                            someUpToDate = true;
                            continue;
                        }

                        DependencyDetail updatedDependency = new DependencyDetail
                        {
                            // TODO: Not needed, but not currently provided in Build info. Will be available on next rollout.
                            Branch = null,
                            Commit = build.Commit,
                            // If casing changes, ensure that the dependency name gets updated.
                            Name    = buildAsset.Name,
                            RepoUri = build.Repository,
                            Version = buildAsset.Version
                        };

                        dependenciesToUpdate.Add(updatedDependency);

                        // Print out what we are going to do.
                        Console.WriteLine($"Updating '{dependency.Name}': '{dependency.Version}' => '{updatedDependency.Version}'" +
                                          $" (from build '{build.BuildNumber}' of '{build.Repository}')");
                        // Notify on casing changes.
                        if (buildAsset.Name != dependency.Name)
                        {
                            Console.WriteLine($"  Dependency name normalized to '{updatedDependency.Name}'");
                        }

                        dependenciesToUpdate.Add(updatedDependency);
                    }
                }

                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, remote);

                Console.WriteLine(finalMessage);

                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, $"Error: Failed to update dependencies to channel {_options.Channel}");
                return(Constants.ErrorCode);
            }
        }
Beispiel #18
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.CoherencyOnly)
                    {
                        if (string.IsNullOrEmpty(_options.Channel))
                        {
                            Console.WriteLine($"Please supply either a channel name (--channel), a packages folder (--packages-folder) " +
                                              $"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;
                            }
                            IEnumerable <AssetData> assetData = build.Assets.Select(
                                a => new AssetData(a.NonShipping)
                            {
                                Name    = a.Name,
                                Version = a.Version
                            });

                            // Now determine what needs to be updated.
                            List <DependencyUpdate> updates = await barOnlyRemote.GetRequiredNonCoherencyUpdatesAsync(
                                repoUri, build.Commit, assetData, currentDependencies);

                            foreach (DependencyUpdate update in updates)
                            {
                                DependencyDetail from = update.From;
                                DependencyDetail to   = update.To;
                                // Print out what we are going to do.
                                Console.WriteLine($"Updating '{from.Name}': '{from.Version}' => '{to.Version}'" +
                                                  $" (from build '{build.AzureDevOpsBuildNumber}' of '{repoUri}')");

                                // Final list of dependencies to update
                                dependenciesToUpdate.Add(to);
                                // Replace in the current dependencies list so the correct data is fed into the coherency pass.
                                currentDependencies.Remove(from);
                                currentDependencies.Add(to);
                            }
                        }
                    }

                    Console.WriteLine("Checking for coherency updates...");

                    // Now run a coherency update based on the current set of dependencies updated
                    // from the previous pass.
                    List <DependencyUpdate> coherencyUpdates =
                        await barOnlyRemote.GetRequiredCoherencyUpdatesAsync(currentDependencies, remoteFactory);

                    foreach (DependencyUpdate dependencyUpdate in coherencyUpdates)
                    {
                        DependencyDetail from            = dependencyUpdate.From;
                        DependencyDetail to              = dependencyUpdate.To;
                        DependencyDetail coherencyParent = currentDependencies.First(d =>
                                                                                     d.Name.Equals(from.CoherentParentDependencyName, StringComparison.OrdinalIgnoreCase));
                        // Print out what we are going to do.
                        Console.WriteLine($"Updating '{from.Name}': '{from.Version}' => '{to.Version}' " +
                                          $"to ensure coherency with {from.CoherentParentDependencyName}@{coherencyParent.Version}");

                        // Final list of dependencies to update
                        dependenciesToUpdate.Add(to);
                    }
                }

                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 (Exception e)
            {
                Logger.LogError(e, $"Error: Failed to update dependencies to channel {_options.Channel}");
                return(Constants.ErrorCode);
            }
        }
        /// <summary>
        /// Retrieve the settings from the combination of the command line
        /// options and the user's darc settings file.
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <returns>Darc settings for use in remote commands</returns>
        /// <remarks>The command line takes precedence over the darc settings file.</remarks>
        public static DarcSettings GetDarcSettings(CommandLineOptions options, ILogger logger, string repoUri = null)
        {
            LocalSettings localSettings = null;
            DarcSettings  darcSettings  = new DarcSettings();

            darcSettings.GitType = GitRepoType.None;

            try
            {
                localSettings = LoadSettingsFile(options);

                if (localSettings != null)
                {
                    darcSettings.BuildAssetRegistryBaseUri  = localSettings.BuildAssetRegistryBaseUri;
                    darcSettings.BuildAssetRegistryPassword = localSettings.BuildAssetRegistryPassword;
                }
                else
                {
                    darcSettings.BuildAssetRegistryBaseUri  = _defaultBuildAssetRegistryBaseUri;
                    darcSettings.BuildAssetRegistryPassword = options.BuildAssetRegistryPassword;
                }

                if (!string.IsNullOrEmpty(repoUri))
                {
                    if (Uri.TryCreate(repoUri, UriKind.Absolute, out Uri parsedUri))
                    {
                        if (parsedUri.Host == "github.com")
                        {
                            darcSettings.GitType = GitRepoType.GitHub;
                            darcSettings.GitRepoPersonalAccessToken =
                                localSettings != null ?
                                (string.IsNullOrEmpty(localSettings.GitHubToken) ? options.GitHubPat : localSettings.GitHubToken) :
                                options.GitHubPat;
                        }
                        else if (parsedUri.Host == "dev.azure.com" || parsedUri.Host.EndsWith("visualstudio.com"))
                        {
                            darcSettings.GitType = GitRepoType.AzureDevOps;
                            darcSettings.GitRepoPersonalAccessToken =
                                localSettings != null ?
                                (string.IsNullOrEmpty(localSettings.AzureDevOpsToken) ? options.AzureDevOpsPat : localSettings.AzureDevOpsToken) :
                                options.AzureDevOpsPat;
                        }
                    }

                    if (darcSettings.GitType == GitRepoType.None)
                    {
                        throw new DarcException($"Unknown repository '{repoUri}', repo type set to 'None'.");
                    }
                }
            }
            catch (Exception e)
            {
                logger.LogWarning(e, $"Failed to load the darc settings file, may be corrupted");
            }

            // Override if non-empty on command line
            darcSettings.BuildAssetRegistryBaseUri = OverrideIfSet(darcSettings.BuildAssetRegistryBaseUri,
                                                                   options.BuildAssetRegistryBaseUri);
            darcSettings.BuildAssetRegistryPassword = OverrideIfSet(darcSettings.BuildAssetRegistryPassword,
                                                                    options.BuildAssetRegistryPassword);

            // Currently the darc settings only has one PAT type which is interpreted differently based
            // on the git type (Azure DevOps vs. GitHub).  For now, leave this setting empty until
            // we know what we are talking to.

            return(darcSettings);
        }
Beispiel #20
0
        /// <summary>
        /// Implements the 'add-subscription' operation
        /// </summary>
        /// <param name="options"></param>
        public override async Task <int> ExecuteAsync()
        {
            DarcSettings darcSettings = LocalCommands.GetSettings(_options, Logger);
            // No need to set up a git type or PAT here.
            Remote remote = new Remote(darcSettings, Logger);

            if (_options.IgnoreChecks.Count() > 0 && !_options.AllChecksSuccessfulMergePolicy)
            {
                Logger.LogError($"--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("NoExtraCommits", null));
            }
            if (_options.AllChecksSuccessfulMergePolicy)
            {
                mergePolicies.Add(new MergePolicy("AllChecksSuccessful", new Dictionary <string, object>
                {
                    { "ignoreChecks", _options.IgnoreChecks }
                }));
            }
            if (_options.RequireChecksMergePolicy.Count() > 0)
            {
                mergePolicies.Add(new MergePolicy("RequireChecks", new Dictionary <string, object>
                {
                    { "checks", _options.RequireChecksMergePolicy }
                }));
            }

            string channel          = _options.Channel;
            string sourceRepository = _options.SourceRepository;
            string targetRepository = _options.TargetRepository;
            string targetBranch     = _options.TargetBranch;
            string updateFrequency  = _options.UpdateFrequency;

            // If in quiet (non-interactive mode), ensure that all options were passed, then
            // just call the remote API
            if (_options.Quiet)
            {
                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 initEditorPopUp =
                    new AddSubscriptionPopUp("add-subscription/add-subscription-todo",
                                             Logger,
                                             channel,
                                             sourceRepository,
                                             targetRepository,
                                             targetBranch,
                                             updateFrequency,
                                             mergePolicies,
                                             (await suggestedChannels).Select(suggestedChannel => suggestedChannel.Name),
                                             (await suggestedRepos).SelectMany(subscription => new List <string> {
                    subscription.SourceRepository, subscription.TargetRepository
                }),
                                             Constants.AvailableFrequencies,
                                             Constants.AvailableMergePolicyYamlHelp);

                UxManager uxManager = new UxManager(Logger);
                int       exitCode  = uxManager.PopUp(initEditorPopUp);
                if (exitCode != Constants.SuccessCode)
                {
                    return(exitCode);
                }
                channel          = initEditorPopUp.Channel;
                sourceRepository = initEditorPopUp.SourceRepository;
                targetRepository = initEditorPopUp.TargetRepository;
                targetBranch     = initEditorPopUp.TargetBranch;
                updateFrequency  = initEditorPopUp.UpdateFrequency;
                mergePolicies    = initEditorPopUp.MergePolicies;
            }

            try
            {
                var newSubscription = await remote.CreateSubscriptionAsync(channel,
                                                                           sourceRepository,
                                                                           targetRepository,
                                                                           targetBranch,
                                                                           updateFrequency,
                                                                           mergePolicies);

                Console.WriteLine($"Successfully created new subscription with id '{newSubscription.Id}'.");
                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, $"Failed to create subscription.");
                return(Constants.ErrorCode);
            }
        }
Beispiel #21
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 #22
0
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                DarcSettings darcSettings = LocalSettings.GetDarcSettings(_options, Logger);

                // No need to set up a git type or PAT here.
                Remote remote = new Remote(darcSettings, Logger);

                var subscriptions = (await remote.GetSubscriptionsAsync()).Where(subscription =>
                {
                    return((string.IsNullOrEmpty(_options.TargetRepository) ||
                            subscription.TargetRepository.Contains(_options.TargetRepository, StringComparison.OrdinalIgnoreCase)) &&
                           (string.IsNullOrEmpty(_options.TargetBranch) ||
                            subscription.TargetBranch.Contains(_options.TargetBranch, StringComparison.OrdinalIgnoreCase)) &&
                           (string.IsNullOrEmpty(_options.SourceRepository) ||
                            subscription.SourceRepository.Contains(_options.SourceRepository, StringComparison.OrdinalIgnoreCase)) &&
                           (string.IsNullOrEmpty(_options.Channel) ||
                            subscription.Channel.Name.Contains(_options.Channel, StringComparison.OrdinalIgnoreCase)));
                });

                if (subscriptions.Count() == 0)
                {
                    Console.WriteLine("No subscriptions found matching the specified criteria.");
                }

                // 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($"  - Merge Policies:");
                    foreach (var mergePolicy in subscription.Policy.MergePolicies)
                    {
                        Console.WriteLine($"    {mergePolicy.Name}");
                        foreach (var mergePolicyProperty in mergePolicy.Properties)
                        {
                            // The merge policy property is a key value pair.  For formatting, turn it into a string.
                            // It's often a JToken, so handle appropriately
                            // 1. If the number of lines in the string is 1, write on same line as key
                            // 2. If the number of lines in the string is more than one, start on new
                            //    line and indent.
                            string   valueString = mergePolicyProperty.Value.ToString();
                            string[] valueLines  = valueString.Split(System.Environment.NewLine);
                            string   keyString   = $"      {mergePolicyProperty.Key} = ";
                            Console.Write(keyString);
                            if (valueLines.Length == 1)
                            {
                                Console.WriteLine(valueString);
                            }
                            else
                            {
                                string indentString = new string(' ', keyString.Length);
                                Console.WriteLine();
                                foreach (string line in valueLines)
                                {
                                    Console.WriteLine($"{indentString}{line}");
                                }
                            }
                        }
                    }
                    Console.WriteLine($"  - Last Build: {(subscription.LastAppliedBuild != null ? subscription.LastAppliedBuild.BuildNumber : "N/A")}");
                }
                return(Constants.SuccessCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to retrieve subscriptions");
                return(Constants.ErrorCode);
            }
        }
        /// <summary>
        /// Retrieve the settings from the combination of the command line
        /// options and the user's darc settings file.
        /// </summary>
        /// <param name="options">Command line options</param>
        /// <returns>Darc settings for use in remote commands</returns>
        /// <remarks>The command line takes precedence over the darc settings file.</remarks>
        public static DarcSettings GetDarcSettings(CommandLineOptions options, ILogger logger, string repoUri = null)
        {
            LocalSettings localSettings = null;
            DarcSettings  darcSettings  = new DarcSettings();

            darcSettings.GitType = GitRepoType.None;

            try
            {
                try
                {
                    localSettings = LoadSettingsFile();
                }
                catch (Exception exc) when(exc is DirectoryNotFoundException || exc is FileNotFoundException)
                {
                    if (string.IsNullOrEmpty(options.AzureDevOpsPat) &&
                        string.IsNullOrEmpty(options.GitHubPat) &&
                        string.IsNullOrEmpty(options.BuildAssetRegistryPassword))
                    {
                        throw new DarcException("Please make sure to run darc authenticate and set" +
                                                " 'bar_password' and 'github_token' or 'azure_devops_token' or append" +
                                                "'-p <bar_password>' [--github-pat <github_token> | " +
                                                "--azdev-pat <azure_devops_token>] to your command");
                    }
                }

                darcSettings.BuildAssetRegistryBaseUri  = localSettings.BuildAssetRegistryBaseUri;
                darcSettings.BuildAssetRegistryPassword = localSettings.BuildAssetRegistryPassword;

                if (!string.IsNullOrEmpty(repoUri))
                {
                    if (repoUri.Contains("github"))
                    {
                        darcSettings.GitType             = GitRepoType.GitHub;
                        darcSettings.PersonalAccessToken = localSettings.GitHubToken;
                    }
                    else if (repoUri.Contains("dev.azure.com"))
                    {
                        darcSettings.GitType             = GitRepoType.AzureDevOps;
                        darcSettings.PersonalAccessToken = localSettings.AzureDevOpsToken;
                    }
                    else
                    {
                        logger.LogWarning($"Unknown repository '{repoUri}'");
                    }
                }
            }
            catch (Exception e)
            {
                logger.LogWarning(e, $"Failed to load the darc settings file, may be corrupted");
            }

            // Override if non-empty on command line
            darcSettings.BuildAssetRegistryBaseUri = OverrideIfSet(darcSettings.BuildAssetRegistryBaseUri,
                                                                   options.BuildAssetRegistryBaseUri);
            darcSettings.BuildAssetRegistryPassword = OverrideIfSet(darcSettings.BuildAssetRegistryPassword,
                                                                    options.BuildAssetRegistryPassword);

            // Currently the darc settings only has one PAT type which is interpreted differently based
            // on the git type (Azure DevOps vs. GitHub).  For now, leave this setting empty until
            // we know what we are talking to.

            return(darcSettings);
        }