/// <summary>
        /// Verifies UX Payload file information in Burn_Manifest.xml
        /// </summary>
        /// <param name="embededResourcesDirectoryPath">Output folder where all the embeded resources are.</param>
        /// <param name="acctualFilePath">Path to the acctual file that was packed in the cab.</param>
        /// <param name="expectedFileName">Expected file name of the file.</param>
        /// <param name="verifyFileIsPrimaryPayload">Check if this file is the primary payload for the UX.</param>
        public static void VerifyUXPayloadInformation(string embededResourcesDirectoryPath, string acctualFilePath, string expectedFileName, bool verifyFileIsPrimaryPayload)
        {
            string expectedFileSize = new FileInfo(acctualFilePath).Length.ToString();
            string expectedHash     = FileVerifier.ComputeFileSHA1Hash(acctualFilePath);

            string      burnManifestXPath = string.Format(@"//burn:UX/burn:Payload[@FilePath='{0}']", expectedFileName);
            XmlNodeList burnManifestNodes = BundleTests.QueryBurnManifest(embededResourcesDirectoryPath, burnManifestXPath);

            Assert.True(1 == burnManifestNodes.Count, String.Format("No UX payload with the name: '{0}' was found in Burn_Manifest.xml.", expectedFileName));
            BundleTests.VerifyAttributeValue(burnManifestNodes[0], "FileSize", expectedFileSize);
            BundleTests.VerifyAttributeValue(burnManifestNodes[0], "Sha1Hash", expectedHash);

            // verify the file is the primary payload for the UX
            if (verifyFileIsPrimaryPayload)
            {
                string      burnManifestPayloadsXPath = @"//burn:UX/burn:Payload";
                XmlNodeList burnManifestPayloadNodes  = BundleTests.QueryBurnManifest(embededResourcesDirectoryPath, burnManifestPayloadsXPath);
                if (null == burnManifestPayloadNodes[0].Attributes["FilePath"] || burnManifestPayloadNodes[0].Attributes["FilePath"].Value != expectedFileName)
                {
                    Assert.True(false, String.Format("The primary UX Payload in Burn_Manifest.xml is not '{0}'.", expectedFileName));
                }
            }

            // verify the correct file is added to the ux container
            string extractedFilePath = Path.Combine(Builder.UXContainerFolderName, expectedFileName);

            FileVerifier.VerifyFilesAreIdentical(acctualFilePath, extractedFilePath);
        }
        /// <summary>
        /// Add a file repository. Use ConfigureVerifier in the options to add the file types you want to support.
        /// This version allows you to use a type for injection so you can have multiple file repositories with different
        /// configurations.
        /// </summary>
        /// <typeparam name="InjectT">The type to uniquly identify this repository with. This type is not used anywhere in the actual repository.</typeparam>
        /// <param name="services">The service colleciton to add the file verifier to.</param>
        /// <param name="options">The options.</param>
        /// <returns>The service collection.</returns>
        public static IServiceCollection AddFileRepository <InjectT>(this IServiceCollection services, Action <FileRepositoryOptions <InjectT> > config)
        {
            var options = new FileRepositoryOptions <InjectT>();

            config.Invoke(options);

            if (options.ConfigureVerifier == null)
            {
                throw new InvalidOperationException("You must include a function to configure your file validator.");
            }

            if (options.CreateFileRepository == null)
            {
                throw new InvalidOperationException("You must configure the file repository. Call UseFilesystem on your options to do this.");
            }

            services.AddSingleton <IFileVerifier <InjectT> >(s =>
            {
                var verifier = new FileVerifier <InjectT>();
                options.ConfigureVerifier(verifier);
                return(verifier);
            });

            services.AddSingleton <IFileRepository <InjectT> >(s => options.CreateFileRepository(s));

            return(services);
        }
Example #3
0
        /// <summary>
        /// Verify the pacakge Payload information in Burn_Manifest.xml
        /// </summary>
        /// <param name="embededResourcesDirectoryPath">Output folder where all the embeded resources are.</param>
        /// <param name="expectedParentPackageType">Parent Package type.</param>
        /// <param name="expectedParentPackageName">Parent Package name; this is the attribute used to locate the package.</param>
        /// <param name="expectedFileName">Payload name; this is the attribute used to locate the payload.</param>
        /// <param name="expectedDownloadURL">@DownloadURL expected value.</param>
        /// <param name="acctualFilePath">Path to the acctual file to compate against file in cab.</param>
        public static void VerifyPackagePayloadInformation(string embededResourcesDirectoryPath, PackageTests.PackageType expectedParentPackageType, string expectedParentPackageName, string expectedParentPackageId, string expectedFileName, string expectedDownloadURL, string acctualFilePath)
        {
            string expectedFileSize = new FileInfo(acctualFilePath).Length.ToString();
            string expectedHash     = FileVerifier.ComputeFileSHA1Hash(acctualFilePath);

            // find the Payload element
            string      payloadXPath = string.Format(@"//burn:Payload[@FilePath='{0}']", expectedFileName);
            XmlNodeList payloadNodes = BundleTests.QueryBurnManifest(embededResourcesDirectoryPath, payloadXPath);

            Assert.AreEqual(1, payloadNodes.Count, "No Package payload with the name: '{0}' was found in Burn_Manifest.xml.", expectedFileName);
            BundleTests.VerifyAttributeValue(payloadNodes[0], "FileSize", expectedFileSize);
            BundleTests.VerifyAttributeValue(payloadNodes[0], "Sha1Hash", expectedHash);
            BundleTests.VerifyAttributeValue(payloadNodes[0], "DownloadUrl", expectedDownloadURL);

            // make sure the payload is added to the package
            string      payloadId = payloadNodes[0].Attributes["Id"].Value;
            string      packagePayloadRefXPath = string.Format(@"//burn:{0}[@Id='{1}']/burn:PayloadRef[@Id='{2}']", GetPackageElementName(expectedParentPackageType), expectedParentPackageId, payloadId);
            XmlNodeList packagePayloadRefNodes = BundleTests.QueryBurnManifest(embededResourcesDirectoryPath, packagePayloadRefXPath);

            Assert.AreEqual(1, packagePayloadRefNodes.Count, "Package payload with the name: '{0}' was found under Package '{1}'.", expectedFileName, expectedParentPackageId);

            // verify the correct file is added to the attached container
            if (null == expectedDownloadURL)
            {
                string extractedFilePath = Path.Combine(Builder.AttachedContainerFolderName, expectedFileName);
                FileVerifier.VerifyFilesAreIdentical(acctualFilePath, extractedFilePath);
            }
        }
        public void NotDefinedFileTypeValid()
        {
            var verifier = new FileVerifier().AddPng();

            verifier.AllowUnknownFiles = true;
            TestSuccessVerifier("TestFiles/NotDefinedFile.ndf", verifier, "application/unknown");
        }
        public static void FileVerifierTest_Default_5_Sha256_Hex()
        {
            var filePath = Path.Combine(Path.GetTempPath(), $"__test_{Guid.NewGuid().ToString()}__");
            var fileInfo = new FileInfo(filePath);

            File.WriteAllText(filePath, Guid.NewGuid().ToString());
            var checksum = Sha256.GetInstance().GenerateInHex(fileInfo);
            var verified = FileVerifier.VerifyAsync(fileInfo, fileInfo.Length, checksum, FileVerifier.ChecksumType.Sha256, CancellationToken.None).Result;

            Assert.True(verified);
            try { fileInfo.Delete(); } catch (Exception) { }
        }
        public void FileDownloader_Default_2_DownloadFileAsync()
        {
            var fileDownloader = FileDownloader.GetInstance();

            FileDownloader.DownloadOperationResult downloadResult = FileDownloader.DownloadStatus.Unknown;
            for (var i = 0; i < 3; i++)
            {
                _output.WriteLine($"Start to download: {TestFileUrl}");
                long progress = 0;
                downloadResult = fileDownloader.DownloadFileAsync(
                    TestFileUrl,
                    new FileInfo(TestFileDestPath),
                    TestFileSize,
                    incSize =>
                {
                    progress += incSize;
                    _output.WriteLine($"Download progress: {progress}");
                },
                    CancellationToken.None).Result;

                if (downloadResult.Success)
                {
                    if (!FileVerifier.VerifyAsync(new FileInfo(TestFileDestPath), TestFileSize, TestFileHash, TestFileChecksumType,
                                                  CancellationToken.None).Result)
                    {
                        downloadResult = FileDownloader.DownloadStatus.InternalError;
                        continue;
                    }
                    break;
                }
                SpinWait.SpinUntil(() => false, TimeSpan.FromSeconds(1));
            }
            try
            {
                File.Delete(TestFileDestPath);
            }
            catch (Exception) { }

            _output.WriteLine($"Download status: {downloadResult?.Status}");

            Assert.NotNull(downloadResult);
            Assert.Equal(FileDownloader.DownloadStatus.Success, downloadResult.Status);
        }
Example #7
0
        /// <summary>
        /// Verifies Package information in Burn-Manifest.xml and Burn-UxManifest.xml.
        /// </summary>
        /// <param name="embededResourcesDirectoryPath">Output folder where all the embeded resources are.</param>
        /// <param name="expectedPackageName">Package name; this is the attribute used to locate the package.</param>
        /// <param name="expectedPackageType">Package type MSI, MSP, MSU or EXE.</param>
        /// <param name="expectedId">Expected Package @Id value.</param>
        /// <param name="expectedInstallCondition">Expected Package @InstallCondition value.</param>
        /// <param name="expectedProductCode">Expected Package @ProductCode value.</param>
        /// <param name="expecteVital">Package is viatal or not.</param>
        /// <param name="expectedPermanent">Expected Package @Permanent value.</param>
        /// <param name="expectedCacheId">Expected Package @CacheId value.</param>
        /// <param name="expectedInstallCommmand">Expected @InstallCommand value.</param>
        /// <param name="expectedUninstallCommmand">Expected @UninstallCommand value.</param>
        /// <param name="expectedRepairCommand">Expected @RepairCommand value.</param>
        /// <param name="expectedDownloadURL">Expected Package @DownloadURL value.</param>
        /// <param name="acctualFilePath">Path to the acctual file for comparison.</param>
        private static void VerifyPackageInformation(string embededResourcesDirectoryPath, string expectedPackageName, PackageType expectedPackageType, string expectedId,
                                                     string expectedInstallCondition, string expectedProductCode, bool expecteVital, bool expectedPermanent, string expectedCacheId,
                                                     string expectedInstallCommmand, string expectedUninstallCommmand, string expectedRepairCommand, string expectedDownloadURL,
                                                     string acctualFilePath)
        {
            string expectedFileSize    = new FileInfo(acctualFilePath).Length.ToString();
            string expectedHash        = FileVerifier.ComputeFileSHA1Hash(acctualFilePath);
            string expectedProductSize = expectedFileSize;

            // verify the Burn_Manifest has the correct information
            string      burnManifestXPath = string.Format(@"//burn:{0}[@Id='{1}']", GetPackageElementName(expectedPackageType), expectedId);
            XmlNodeList burnManifestNodes = BundleTests.QueryBurnManifest(embededResourcesDirectoryPath, burnManifestXPath);

            Assert.AreEqual(1, burnManifestNodes.Count, "No MsiPackage with the Id: '{0}' was found in Burn_Manifest.xml.", expectedId);
            BundleTests.VerifyAttributeValue(burnManifestNodes[0], "InstallCondition", expectedInstallCondition);
            BundleTests.VerifyAttributeValue(burnManifestNodes[0], "Permanent", expectedPermanent ? "yes" : "no");
            BundleTests.VerifyAttributeValue(burnManifestNodes[0], "Vital", expecteVital ? "yes" : "no");

            if (expectedPackageType == PackageType.MSI)
            {
                BundleTests.VerifyAttributeValue(burnManifestNodes[0], "ProductCode", expectedProductCode);
            }

            if (expectedPackageType == PackageType.EXE)
            {
                BundleTests.VerifyAttributeValue(burnManifestNodes[0], "InstallArguments", expectedInstallCommmand);
                BundleTests.VerifyAttributeValue(burnManifestNodes[0], "UninstallArguments", expectedUninstallCommmand);
                BundleTests.VerifyAttributeValue(burnManifestNodes[0], "RepairArguments", expectedRepairCommand);
            }

            if (!String.IsNullOrEmpty(expectedCacheId))
            {
                BundleTests.VerifyAttributeValue(burnManifestNodes[0], "CacheId", expectedCacheId);
            }

            // verify payload information
            PackageTests.VerifyPackagePayloadInformation(embededResourcesDirectoryPath, expectedPackageType, expectedPackageName, expectedId, expectedPackageName, expectedDownloadURL, acctualFilePath);

            // verify the Burn-UxManifest has the correct information
            string expectedProductName = null;
            string expectedDiscription = null;

            if (expectedPackageType == PackageType.EXE)
            {
                FileVersionInfo fileInfo = FileVersionInfo.GetVersionInfo(acctualFilePath);
                expectedProductName = string.IsNullOrEmpty(fileInfo.ProductName) ? null : fileInfo.ProductName;
                expectedDiscription = string.IsNullOrEmpty(fileInfo.FileDescription) ? null : fileInfo.FileDescription;
            }
            else if (expectedPackageType == PackageType.MSI)
            {
                string subject = Verifier.GetMsiSummaryInformationProperty(acctualFilePath, Verifier.MsiSummaryInformationProperty.Subject);
                expectedProductName = string.IsNullOrEmpty(subject) ? null : subject;
            }

            string      burnUxManifestXPath = string.Format(@"//burnUx:WixPackageProperties[@Package='{0}']", expectedId);
            XmlNodeList burnUxManifestNodes = BundleTests.QueryBurnUxManifest(embededResourcesDirectoryPath, burnUxManifestXPath);

            Assert.AreEqual(1, burnUxManifestNodes.Count, "No WixPackageProperties for Package: '{0}' was found in Burn-UxManifest.xml.", expectedId);
            BundleTests.VerifyAttributeValue(burnUxManifestNodes[0], "Vital", expecteVital ? "yes" : "no");
            BundleTests.VerifyAttributeValue(burnUxManifestNodes[0], "DownloadSize", expectedFileSize);
            BundleTests.VerifyAttributeValue(burnUxManifestNodes[0], "PackageSize", expectedFileSize);
            BundleTests.VerifyAttributeValue(burnUxManifestNodes[0], "InstalledSize", expectedFileSize);
            BundleTests.VerifyAttributeValue(burnUxManifestNodes[0], "DisplayName", expectedProductName);
            BundleTests.VerifyAttributeValue(burnUxManifestNodes[0], "Description", expectedDiscription);
        }
Example #8
0
        /// <summary>
        /// Add the main edity services. If you call this and can use the default IFileFinder, you don't need to
        /// do anything else, if you want to customize the file finder add it before calling this function.
        /// </summary>
        /// <param name="services">The services collection.</param>
        /// <param name="setupEditySettings">Callback to configure the edity settings.</param>
        /// <returns>The service collection.</returns>
        public static IServiceCollection AddEdity(this IServiceCollection services, Action <EditySettings> setupEditySettings)
        {
            services.AddThreaxSharedHttpClient();

            var editySettings = new EditySettings();

            setupEditySettings.Invoke(editySettings);

            //Setup the mapper
            var mapperConfig = SetupMappings();

            services.AddScoped <IMapper>(i => mapperConfig.CreateMapper());

            //Setup repos
            services.TryAddScoped <ICommitRepository, CommitRepository>();
            services.TryAddScoped <ISyncRepository, SyncRepository>();
            services.TryAddScoped <IPathBaseInjector, PathBaseInjector>();
            services.TryAddScoped <IDraftRepository, DraftRepository>();
            services.TryAddScoped <IPublishRepository, PublishRepository>();
            services.TryAddScoped <IHistoryRepository, HistoryRepository>();
            services.TryAddScoped <IMergeRepository, MergeRepository>();
            services.TryAddScoped <IPageRepository, PageRepository>();
            services.TryAddScoped <ITemplateRepository, TemplateRepository>();
            services.TryAddScoped <IAssetRepository, AssetRepository>();
            services.TryAddScoped <IBranchRepository, BranchRepository>();
            services.TryAddSingleton <IOverrideValuesProvider>(s => new DefaultOverrideValuesProvider(editySettings.OverrideVars));
            services.TryAddScoped <IGitCredentialsProvider, DefaultGitCredentialsProvider>();

            var baseUrl = HalcyonConventionOptions.HostVariable;

            if (editySettings.BaseUrl != null)
            {
                baseUrl += "/" + editySettings.BaseUrl;
            }

            var halOptions = new HalcyonConventionOptions()
            {
                BaseUrl                   = baseUrl,
                HalDocEndpointInfo        = new HalDocEndpointInfo(typeof(EndpointDocController)),
                MakeAllControllersHalcyon = false
            };

            services.AddConventionalHalcyon(halOptions);

            var halClientGenOptions = new HalClientGenOptions()
            {
                SourceAssemblies = new Assembly[] { typeof(EdityMvcExtensions).GetTypeInfo().Assembly }
            };

            if (editySettings.AdditionalMvcLibraries != null)
            {
                halClientGenOptions.SourceAssemblies = halClientGenOptions.SourceAssemblies.Concat(editySettings.AdditionalMvcLibraries);
            }

            services.AddHalClientGen(halClientGenOptions);

            if (editySettings.Events == null)
            {
                editySettings.Events = new EdityEvents();
            }

            services.TryAddScoped <ITargetFileInfoProvider>(s =>
            {
                var fileFinder = s.GetRequiredService <IFileFinder>();
                return(new DefaultTargetFileInfoProvider(fileFinder.Project.DefaultPage));
            });

            services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>();

            services.TryAddSingleton <WorkQueue, WorkQueue>();
            services.TryAddSingleton <ICompileService>(s => new CompileService(s.GetRequiredService <WorkQueue>(), mapperConfig.CreateMapper()));

            services.TryAddScoped <IUserInfo, DefaultUserInfo>();

            services.TryAddScoped <IPhaseDetector>(s =>
            {
                var settings = new JsonSerializerSettings();
                settings.SetToHalcyonDefault();
                var serializer = JsonSerializer.Create(settings);
                return(new CookiePhaseDetector("edityBranch", serializer, s.GetRequiredService <IHttpContextAccessor>()));
            });

            services.AddSingleton <EditySettings>(s => editySettings);

            switch (editySettings.ProjectMode)
            {
            case ProjectMode.OneRepo:
            default:
                services.AddTransient <ProjectFinder, OneRepo>(s =>
                {
                    return(new OneRepo(editySettings.ProjectPath, editySettings.EdityCorePath, editySettings.SitePath));
                });
                break;

            case ProjectMode.OneRepoPerUser:
                services.AddTransient <ProjectFinder, OneRepoPerUser>(s =>
                {
                    return(new OneRepoPerUser(editySettings, s.GetRequiredService <IPhaseDetector>(), s.GetRequiredService <ILogger <OneRepoPerUser> >()));
                });
                break;
            }

            services.AddTransient <Repository, Repository>(s =>
            {
                var userInfo      = s.GetRequiredService <IUserInfo>();
                var projectFinder = s.GetRequiredService <ProjectFinder>();
                var projectFolder = projectFinder.GetUserProjectPath(userInfo.UniqueUserName);
                return(new Repository(Repository.Discover(projectFolder)));
            });

            services.AddTransient <Signature, Signature>(s =>
            {
                var userInfo = s.GetRequiredService <IUserInfo>();
                return(new Signature(userInfo.PrettyUserName, userInfo.Email, DateTime.Now));
            });

            services.AddTransient <SiteBuilderSettings, SiteBuilderSettings>(s =>
            {
                return(new SiteBuilderSettings()
                {
                    OutDir = editySettings.OutputPath
                });
            });

            services.TryAddScoped <IContentCompilerFactory, ContentCompilerFactory>();

            services.AddDefaultFileFinder();

            services.AddSingleton <BuildTaskManager>(s =>
            {
                var buildTaskManager = new BuildTaskManager();
                buildTaskManager.SetBuildTaskType("PublishMenu", typeof(PublishMenu));
                buildTaskManager.SetBuildTaskType("CreateIISWebConfig", typeof(CreateIISWebConfig));
                buildTaskManager.SetBuildTaskType("GetPublishRepo", typeof(GetPublishRepo));
                buildTaskManager.SetBuildTaskType("PublishToGitRepo", typeof(PublishToGitRepo));
                buildTaskManager.SetBuildTaskType("AddGithubCname", typeof(AddGithubCname));
                editySettings.Events.CustomizeBuildTasks?.Invoke(buildTaskManager);
                return(buildTaskManager);
            });

            services.TryAddTransient <PullPublish>(s =>
            {
                var projectFinder = s.GetRequiredService <ProjectFinder>();
                return(new PullPublish(projectFinder.MasterRepoPath, projectFinder.PublishedProjectPath));
            });

            services.AddTransient <ISiteBuilder>(s =>
            {
                var settings            = s.GetRequiredService <SiteBuilderSettings>();
                var compilerFactory     = s.GetRequiredService <IContentCompilerFactory>();
                var fileFinder          = s.GetRequiredService <IFileFinder>();
                String deploymentFolder = null;
                if (editySettings.Publisher == Publishers.RoundRobin)
                {
                    deploymentFolder = Guid.NewGuid().ToString();
                }
                var builder          = new SiteBuilder(settings, compilerFactory, fileFinder, deploymentFolder);
                var buildTaskManager = s.GetRequiredService <BuildTaskManager>();

                //Customize publisher settings depending on compiler setting
                switch (editySettings.Publisher)
                {
                case Publishers.RoundRobin:
                    var outputBaseFolder = settings.OutDir;
                    settings.OutDir      = Path.GetFullPath(Path.Combine(settings.OutDir, deploymentFolder));
                    builder.AddPublishTask(new RoundRobinPublisher(settings.OutDir));
                    break;
                }

                if (editySettings.ProjectMode == ProjectMode.OneRepoPerUser)
                {
                    builder.AddPreBuildTask(s.GetRequiredService <PullPublish>());
                }

                foreach (var preBuild in fileFinder.Project.PreBuildTasks)
                {
                    builder.AddPreBuildTask(buildTaskManager.CreateBuildTask(preBuild));
                }

                foreach (var postBuild in fileFinder.Project.PostBuildTasks)
                {
                    builder.AddPostBuildTask(buildTaskManager.CreateBuildTask(postBuild));
                }

                foreach (var publish in fileFinder.Project.PublishTasks)
                {
                    builder.AddPublishTask(buildTaskManager.CreateBuildTask(publish));
                }

                foreach (var postPublish in fileFinder.Project.PostPublishTasks)
                {
                    builder.AddPostPublishTask(buildTaskManager.CreateBuildTask(postPublish));
                }

                editySettings.Events.CustomizeSiteBuilder(new SiteBuilderEventArgs()
                {
                    SiteBuilder = builder,
                    Services    = s
                });

                return(builder);
            });

            services.AddExceptionErrorFilters(new ExceptionFilterOptions()
            {
                DetailedErrors = editySettings.DetailedErrors
            });

            // Add framework services.
            var mvcBuilder = services.AddMvc(o =>
            {
                o.UseExceptionErrorFilters();
                o.UseConventionalHalcyon(halOptions);
            })
                             .AddNewtonsoftJson(o =>
            {
                o.SerializerSettings.SetToHalcyonDefault();
                o.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            })
                             .AddEdityControllers(editySettings.AdditionalMvcLibraries);

            editySettings.Events.CustomizeMvcBuilder?.Invoke(mvcBuilder);

            services.AddScoped <ICompileRequestDetector, CompileRequestDetector>();

            services.AddSingleton <IFileVerifier>(s =>
            {
                var verifier = new FileVerifier()
                               .AddHtml()
                               .AddBitmap()
                               .AddJpeg()
                               .AddPng()
                               .AddSvgXml()
                               .AddGif()
                               .AddPdf()
                               .AddDocx()
                               .AddDoc()
                               .AddPptx()
                               .AddPpt()
                               .AddXlsx()
                               .AddXls()
                               .AddJson();

                editySettings.Events.CustomizeFileVerifier?.Invoke(verifier);

                return(verifier);
            });

            services.AddScoped <IToolRunner>(s =>
            {
                var tools = new ToolRunner()
                            .UseClientGenTools();

                editySettings.Events.CustomizeTools?.Invoke(tools);

                return(tools);
            });

            services.AddThreaxProcessHelper(o =>
            {
                //o.IncludeLogOutput = false;
                //o.DecorateProcessRunner = r => new SpyProcessRunner(r)
                //{
                //    Events = new ProcessEvents()
                //    {
                //        ErrorDataReceived = (o, e) => { if (e.DataReceivedEventArgs.Data != null) Console.WriteLine(e.DataReceivedEventArgs.Data); },
                //        OutputDataReceived = (o, e) => { if (e.DataReceivedEventArgs.Data != null) Console.WriteLine(e.DataReceivedEventArgs.Data); },
                //    }
                //};
            });

            return(services);
        }