Ejemplo n.º 1
0
 public NuGetFeedsHealthCheck(
     [NotNull] IHttpClientFactory httpClient,
     [NotNull] ILogger logger)
 {
     _httpClient          = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
     _logger              = logger ?? throw new ArgumentNullException(nameof(logger));
     _nuGetDownloadClient = new NuGetDownloadClient();
 }
Ejemplo n.º 2
0
        public static Task <App> CreateAsync(
            string[] args,
            ILogger logger        = default,
            HttpClient httpClient = default,
            bool disposeNested    = true,
            CancellationToken cancellationToken = default)
        {
            if (args == null)
            {
                throw new ArgumentNullException(nameof(args));
            }

            var appArgs = args.ToImmutableArray();

            if (logger is null)
            {
                var loggerConfiguration = new LoggerConfiguration().WriteTo.Console();

                string correlationId = GetCorrelationId(appArgs);

                if (!string.IsNullOrWhiteSpace(correlationId))
                {
                    loggerConfiguration.Enrich.WithProperty("CorrelationId", correlationId);
                }

                logger = loggerConfiguration.CreateLogger();
            }

            string nugetSource  = GetNuGetSource(appArgs);
            string nugetConfig  = GetNuGetConfig(appArgs);
            string nugetExePath = GetNuGetExePath(appArgs);

            httpClient ??= new HttpClient();
            var nuGetDownloadClient   = new NuGetDownloadClient();
            var nuGetCliSettings      = new NuGetCliSettings(nugetSource, nugetConfig, nugetExePath);
            var nuGetDownloadSettings = new NuGetDownloadSettings();
            var nuGetPackageInstaller = new NuGetPackageInstaller(
                nuGetDownloadClient,
                nuGetCliSettings,
                nuGetDownloadSettings,
                logger);

            return(Task.FromResult(new App(nuGetPackageInstaller, logger, httpClient, disposeNested)));
        }
Ejemplo n.º 3
0
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            await Task.Yield();

            string?nugetExePath = "";

            _logger.Debug("Ensuring nuget.exe exists");

            if (!int.TryParse(_configuration[DeployerAppConstants.NuGetDownloadTimeoutInSeconds],
                              out int initialNuGetDownloadTimeoutInSeconds) || initialNuGetDownloadTimeoutInSeconds <= 0)
            {
                initialNuGetDownloadTimeoutInSeconds = 100;
            }

            try
            {
                var fromSeconds = TimeSpan.FromSeconds(initialNuGetDownloadTimeoutInSeconds);

                using CancellationTokenSource cts = _timeoutHelper.CreateCancellationTokenSource(fromSeconds);
                string?downloadDirectory = _configuration[DeployerAppConstants.NuGetExeDirectory].WithDefault();
                string?exeVersion        = _configuration[DeployerAppConstants.NuGetExeVersion].WithDefault();

                HttpClient httpClient = _httpClientFactory.CreateClient();

                var nuGetDownloadClient = new NuGetDownloadClient();
                NuGetDownloadResult nuGetDownloadResult = await nuGetDownloadClient.DownloadNuGetAsync(
                    new NuGetDownloadSettings(downloadDirectory : downloadDirectory, nugetExeVersion : exeVersion),
                    _logger,
                    httpClient,
                    cts.Token);

                if (nuGetDownloadResult.Succeeded)
                {
                    nugetExePath = nuGetDownloadResult.NuGetExePath;
                }
            }
            catch (Exception ex)
            {
                _logger.Warning(ex, "Could not download nuget.exe");
            }

            if (_configuration is { } && _nugetConfiguration is {})
Ejemplo n.º 4
0
        public static async Task <DeployerApp> BuildAppAsync([NotNull] string[] inputArgs,
                                                             ILogger?logger = null,
                                                             CancellationToken cancellationToken = default)
        {
            if (inputArgs is null)
            {
                throw new ArgumentNullException(nameof(inputArgs));
            }

            var args = inputArgs.ToImmutableArray();

            bool hasDefinedLogger = logger is {};

            string outputTemplate = GetOutputTemplate(args);

            var levelSwitch = new LoggingLevelSwitch();

            logger ??= new LoggerConfiguration()
            .WriteTo.Console(outputTemplate: outputTemplate, standardErrorFromLevel: LogEventLevel.Error)
            .MinimumLevel.ControlledBy(levelSwitch)
            .CreateLogger();

            logger.Verbose("Using output template {Template}", outputTemplate);

            try
            {
                string?machineSettings =
                    GetMachineSettingsFile(new DirectoryInfo(Path.Combine(
                                                                 Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "tools", "Milou.Deployer")));

                AppSettingsBuilder appSettingsBuilder;

                try
                {
                    appSettingsBuilder = KeyValueConfigurationManager
                                         .Add(new ReflectionKeyValueConfiguration(typeof(AppBuilder).Assembly))
                                         .Add(new ReflectionKeyValueConfiguration(typeof(ConfigurationKeys).Assembly));
                }
                catch (Exception ex) when(!ex.IsFatal())
                {
                    logger.Error(ex, "Could note create settings");
                    throw;
                }

                if (!string.IsNullOrWhiteSpace(machineSettings))
                {
                    logger.Information("Using machine specific configuration file '{Settings}'", machineSettings);
                    appSettingsBuilder =
                        appSettingsBuilder.Add(new JsonKeyValueConfiguration(machineSettings, false));
                }

                string?configurationFile =
                    Environment.GetEnvironmentVariable(ConfigurationKeys.KeyValueConfigurationFile);

                if (!string.IsNullOrWhiteSpace(configurationFile) && File.Exists(configurationFile))
                {
                    logger.Information("Using configuration values from file '{ConfigurationFile}'", configurationFile);
                    appSettingsBuilder =
                        appSettingsBuilder.Add(new JsonKeyValueConfiguration(configurationFile, false));
                }

                var argsAsParameters = args
                                       .Where(arg => arg.StartsWith("-", StringComparison.OrdinalIgnoreCase))
                                       .Select(arg => arg.TrimStart('-'))
                                       .ToImmutableArray();

                MultiSourceKeyValueConfiguration configuration = appSettingsBuilder
                                                                 .Add(new EnvironmentVariableKeyValueConfigurationSource())
                                                                 .AddCommandLineArgsSettings(argsAsParameters)
                                                                 .Add(new UserJsonConfiguration())
                                                                 .Build();

                logger.Debug("Using configuration: {Configuration}", configuration.SourceChain);

                string logPath = configuration[ConsoleConfigurationKeys.LoggingFilePath];

                string environmentLogLevel =
                    configuration[ConfigurationKeys.LogLevelEnvironmentVariable];

                string configurationLogLevel = configuration[ConfigurationKeys.LogLevel];

                var logLevel =
                    Arbor.App.Extensions.Logging.LogEventLevelExtensions.ParseOrDefault(
                        environmentLogLevel.WithDefault(configurationLogLevel));

                levelSwitch.MinimumLevel = logLevel;

                LoggerConfiguration loggerConfiguration = new LoggerConfiguration()
                                                          .WriteTo.Console(outputTemplate: outputTemplate);

                if (!string.IsNullOrWhiteSpace(logPath))
                {
                    loggerConfiguration = loggerConfiguration.WriteTo.File(logPath);
                }

                if (!hasDefinedLogger)
                {
                    if (logger is IDisposable disposable)
                    {
                        disposable.Dispose();
                    }

                    logger = loggerConfiguration
                             .MinimumLevel.ControlledBy(levelSwitch)
                             .CreateLogger();
                }

                if (!string.IsNullOrWhiteSpace(machineSettings))
                {
                    logger.Information("Using machine specific configuration file '{Settings}'", machineSettings);
                }

                string?nugetSource = args.GetArgumentValueOrDefault("nuget-source");
                string?nugetConfig = args.GetArgumentValueOrDefault("nuget-config");

                var webDeployConfig = new WebDeployConfig(new WebDeployRulesConfig(
                                                              true,
                                                              true,
                                                              false,
                                                              true,
                                                              true));

                bool allowPreReleaseEnabled =
                    configuration[ConfigurationKeys.AllowPreReleaseEnvironmentVariable]
                    .ParseAsBooleanOrDefault() ||
                    (Debugger.IsAttached &&
                     configuration[ConfigurationKeys.ForceAllowPreRelease]
                     .ParseAsBooleanOrDefault());

                string?nuGetExePath = configuration[ConfigurationKeys.NuGetExePath];

                if (string.IsNullOrWhiteSpace(nuGetExePath))
                {
                    logger.Debug("nuget.exe is not specified, downloading with {Tool}", nameof(NuGetDownloadClient));

                    using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
                    {
                        var nuGetDownloadClient = new NuGetDownloadClient();
                        NuGetDownloadResult nuGetDownloadResult;

                        using (var httpClient = new HttpClient())
                        {
                            nuGetDownloadResult = await nuGetDownloadClient
                                                  .DownloadNuGetAsync(NuGetDownloadSettings.Default, logger, httpClient, cts.Token)
                                                  .ConfigureAwait(false);
                        }

                        if (!nuGetDownloadResult.Succeeded)
                        {
                            throw new InvalidOperationException(
                                      Resources.NuGetExeCouldNotBeDownloaded);
                        }

                        nuGetExePath = nuGetDownloadResult.NuGetExePath;
                    }

                    logger.Debug("Successfully downloaded nuget.exe to '{DownloadedPath}'", nuGetExePath);
                }

                var deployerConfiguration = new DeployerConfiguration(webDeployConfig)
                {
                    NuGetExePath               = nuGetExePath,
                    NuGetConfig                = nugetConfig.WithDefault(configuration[ConfigurationKeys.NuGetConfig]),
                    NuGetSource                = nugetSource.WithDefault(configuration[ConfigurationKeys.NuGetSource]),
                    AllowPreReleaseEnabled     = allowPreReleaseEnabled,
                    StopStartIisWebSiteEnabled = configuration[ConfigurationKeys.StopStartIisWebSiteEnabled]
                                                 .ParseAsBooleanOrDefault(true)
                };

                var nuGetCliSettings = new NuGetCliSettings(
                    deployerConfiguration.NuGetSource,
                    nuGetExePath: deployerConfiguration.NuGetExePath,
                    nugetConfigFile: deployerConfiguration.NuGetConfig);

                var nuGetPackageInstaller =
                    new NuGetPackageInstaller(logger: logger, nugetCliSettings: nuGetCliSettings);

                var deploymentService = new DeploymentService(
                    deployerConfiguration,
                    logger,
                    configuration,
                    new WebDeployHelper(logger),
                    deploymentExecutionDefinition =>
                    IisManager.Create(deployerConfiguration, logger, deploymentExecutionDefinition),
                    nuGetPackageInstaller,
                    new FtpHandlerFactory());

                string temp = configuration[ConfigurationKeys.TempDirectory];

                const string tempEnvironmentVariableName = "temp";

                if (!string.IsNullOrWhiteSpace(temp) && Directory.Exists(temp))
                {
                    Environment.SetEnvironmentVariable(tempEnvironmentVariableName, temp);
                    Environment.SetEnvironmentVariable("tmp", temp);
                }

                var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
                return(new DeployerApp(logger, deploymentService, configuration, levelSwitch, cancellationTokenSource));
            }
            catch (Exception ex) when(!ex.IsFatal())
            {
                logger.Error("Could not build application");
                throw;
            }
        }
Ejemplo n.º 5
0
        public async Task <IReadOnlyCollection <PackageVersion> > GetPackageVersionsAsync(
            [NotNull] string packageId,
            bool useCache                       = true,
            ILogger logger                      = null,
            bool includePreReleased             = false,
            string nugetPackageSource           = null,
            string nugetConfigFile              = null,
            CancellationToken cancellationToken = default)
        {
            if (string.IsNullOrWhiteSpace(packageId))
            {
                throw new ArgumentException("Value cannot be null or whitespace.", nameof(packageId));
            }

            if (packageId.Equals(Constants.NotAvailable))
            {
                return(ImmutableArray <PackageVersion> .Empty);
            }

            string NormalizeKey(string key)
            {
                return(key.Replace(":", "_")
                       .Replace("/", "")
                       .Replace(".", "")
                       .Replace(Path.DirectorySeparatorChar.ToString(), "_"));
            }

            string cacheKey = AllPackagesCacheKey;

            if (!string.IsNullOrWhiteSpace(nugetConfigFile))
            {
                string configCachePart = $"{PackagesCacheKeyBaseUrn}:{NormalizeKey(nugetConfigFile)}";

                cacheKey = !string.IsNullOrWhiteSpace(nugetPackageSource) ? $"{configCachePart}:{NormalizeKey(nugetPackageSource)}" : configCachePart;
            }
            else if (!string.IsNullOrWhiteSpace(nugetPackageSource))
            {
                cacheKey = $"{PackagesCacheKeyBaseUrn}:{NormalizeKey(nugetPackageSource)}";
            }

            cacheKey += $":{packageId}";

            _logger.Verbose("Using package cache key {Key}", cacheKey);

            if (useCache)
            {
                if (_memoryCache.TryGetValue(cacheKey, out IReadOnlyCollection <PackageVersion> packages))
                {
                    if (packages.Count > 0)
                    {
                        _logger.Debug("Returning packages from cache with key {Key} for package id {PackageId}",
                                      cacheKey,
                                      packageId);
                        return(packages);
                    }
                }
            }

            var                 nuGetDownloadClient = new NuGetDownloadClient();
            HttpClient          httpClient          = _httpClientFactory.CreateClient("nuget");
            NuGetDownloadResult nuGetDownloadResult;

            using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)))
            {
                nuGetDownloadResult = await nuGetDownloadClient.DownloadNuGetAsync(NuGetDownloadSettings.Default,
                                                                                   _logger,
                                                                                   httpClient,
                                                                                   cts.Token);
            }

            if (!nuGetDownloadResult.Succeeded)
            {
                if (nuGetDownloadResult.Exception != null)
                {
                    _logger.Error(nuGetDownloadResult.Exception,
                                  "Could not download NuGet.exe: {Result}",
                                  nuGetDownloadResult.Result);
                }
                else
                {
                    _logger.Error("Could not download NuGet.exe: {Result}", nuGetDownloadResult.Result);
                }
            }

            string nugetExe = nuGetDownloadResult.NuGetExePath;

            if (string.IsNullOrWhiteSpace(nugetExe))
            {
                throw new DeployerAppException("The nuget.exe path is not set");
            }

            if (!File.Exists(nugetExe))
            {
                throw new DeployerAppException($"The nuget.exe path '{nugetExe}' does not exist");
            }

            string packageSourceAppSettingsKey = ConfigurationConstants.NuGetPackageSourceName;

            string packageSource = nugetPackageSource.WithDefault(_keyValueConfiguration[packageSourceAppSettingsKey]);

            var args = new List <string> {
                "list", packageId
            };

            if (includePreReleased)
            {
                args.Add("-PreRelease");
            }

            if (!string.IsNullOrWhiteSpace(packageSource))
            {
                logger?.Debug("Using package source '{PackageSource}' for package {Package}", packageSource, packageId);
                args.Add("-source");
                args.Add(packageSource);
            }
            else
            {
                logger?.Debug(
                    "There is no package source defined i app settings, key '{PackageSourceAppSettingsKey}', using all sources",
                    packageSourceAppSettingsKey);
            }

            args.Add("-AllVersions");
            args.Add("-NonInteractive");
            args.Add("-Verbosity");
            args.Add("normal");

            string configFile =
                nugetConfigFile.WithDefault(_keyValueConfiguration[ConfigurationConstants.NugetConfigFile]);

            if (configFile.HasValue() && File.Exists(configFile))
            {
                _logger.Debug("Using NuGet config file {NuGetConfigFile} for package {Package}", configFile, packageId);
                args.Add("-ConfigFile");
                args.Add(configFile);
            }

            var builder    = new List <string>();
            var errorBuild = new List <string>();

            logger?.Debug("Running NuGet from package service to find packages with timeout {Seconds} seconds",
                          _deploymentConfiguration.ListTimeOutInSeconds);

            ExitCode exitCode;

            using (var cancellationTokenSource =
                       new CancellationTokenSource(TimeSpan.FromSeconds(_deploymentConfiguration.ListTimeOutInSeconds)))
            {
                using (CancellationTokenSource linked =
                           CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cancellationTokenSource.Token))
                {
                    exitCode = await ProcessRunner.ExecuteProcessAsync(nugetExe,
                                                                       args,
                                                                       (message, category) =>
                    {
                        builder.Add(message);
                        _logger.Debug("{Category} {Message}", category, message);
                    },
                                                                       (message, category) =>
                    {
                        errorBuild.Add(message);
                        _logger.Error("{Category} {Message}", category, message);
                    },
                                                                       (message, category) => _logger.Debug("{Category} {ProcessToolMessage}", category, message),
                                                                       (message, category) => _logger.Verbose("{Category} {ProcessToolMessage}", category, message),
                                                                       cancellationToken : linked.Token);
                }
            }

            string standardOut      = string.Join(Environment.NewLine, builder);
            string standardErrorOut = string.Join(Environment.NewLine, errorBuild);

            if (!exitCode.IsSuccess)
            {
                var sources      = new List <string>();
                var sourcesError = new List <string>();

                var sourcesArgs = new List <string> {
                    "sources"
                };

                if (configFile.HasValue() && File.Exists(configFile))
                {
                    sourcesArgs.Add("-ConfigFile");
                    sourcesArgs.Add(configFile);
                }

                sourcesArgs.Add("-NonInteractive");

                if (_logger.IsEnabled(LogEventLevel.Debug) || _logger.IsEnabled(LogEventLevel.Verbose))
                {
                    sourcesArgs.Add("-Verbosity");
                    sourcesArgs.Add("detailed");
                }

                await ProcessRunner.ExecuteProcessAsync(nugetExe,
                                                        sourcesArgs,
                                                        (message, _) => sources.Add(message),
                                                        (message, _) => sourcesError.Add(message),
                                                        (message, category) => _logger.Information("{Category} {ProcessToolMessage}", category, message),
                                                        (message, category) => _logger.Verbose("{Category} {ProcessToolMessage}", category, message),
                                                        cancellationToken : cancellationToken);

                string sourcesOut      = string.Join(Environment.NewLine, sources);
                string sourcesErrorOut = string.Join(Environment.NewLine, sourcesError);

                _logger.Error(
                    "Exit code {Code} when running NuGet list packages; standard out '{StandardOut}', standard error '{StandardErrorOut}', exe path '{NugetExe}', arguments '{Arguments}', nuget sources '{SourcesOut}', sources error '{SourcesErrorOut}'",
                    exitCode.Code,
                    standardOut,
                    standardErrorOut,
                    nugetExe,
                    string.Join(" ", args),
                    sourcesOut,
                    sourcesErrorOut);

                return(Array.Empty <PackageVersion>());
            }

            var ignoredOutputStatements = new List <string> {
                "Using credentials", "No packages found"
            };

            List <string> included =
                builder.Where(line => !ignoredOutputStatements.Any(ignored =>
                                                                   line.IndexOf(ignored, StringComparison.InvariantCultureIgnoreCase) >= 0))
                .ToList();

            List <PackageVersion> items = included.Select(
                package =>
            {
                string[] parts = package.Split(' ');

                string currentPackageId = parts[0];

                try
                {
                    string version = parts.Last();

                    if (!SemanticVersion.TryParse(version, out SemanticVersion semanticVersion))
                    {
                        _logger.Debug(
                            "Found package version {Version} for package {Package}, skipping because it could not be parsed as semantic version",
                            version,
                            currentPackageId);
                        return(null);
                    }

                    if (!packageId.Equals(currentPackageId, StringComparison.OrdinalIgnoreCase))
                    {
                        _logger.Debug(
                            "Found package {Package}, skipping because it does match requested package {RequestedPackage}",
                            currentPackageId,
                            packageId);

                        return(null);
                    }

                    return(new PackageVersion(packageId, semanticVersion));
                }
                catch (Exception ex) when(!ex.IsFatal())
                {
                    _logger.Warning(ex, "Error parsing package '{Package}'", package);
                    return(null);
                }
            })
                                          .Where(packageVersion => packageVersion != null)
                                          .OrderBy(packageVersion => packageVersion.PackageId)
                                          .ThenByDescending(packageVersion => packageVersion.Version)
                                          .ToList();

            var addedPackages = new List <string>();

            foreach (PackageVersion packageVersion in items)
            {
                addedPackages.Add(packageVersion.ToString());
            }

            if (_logger.IsEnabled(LogEventLevel.Verbose))
            {
                _logger.Verbose("Added {Count} packages to in-memory cache with cache key {CacheKey} {PackageVersions}",
                                addedPackages.Count,
                                cacheKey,
                                addedPackages);
            }
            else if (addedPackages.Count > 0 && addedPackages.Count < 20)
            {
                _logger.Information(
                    "Added {Count} packages to in-memory cache with cache key {CacheKey} {PackageVersions}",
                    addedPackages.Count,
                    cacheKey,
                    addedPackages);
            }
            else if (addedPackages.Any())
            {
                _logger.Information("Added {Count} packages to in-memory cache with cache key {CacheKey}",
                                    addedPackages.Count,
                                    cacheKey);
            }

            if (addedPackages.Any())
            {
                _memoryCache.Set(cacheKey, items);
            }
            else
            {
                _logger.Debug("Added no packages to in-memory cache for cache key {CacheKey}", cacheKey);
            }

            return(items);
        }