Ejemplo n.º 1
0
        private bool TryInitializeUpgrader(out string error)
        {
            if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade)
            {
                error = null;
                if (this.upgrader == null)
                {
                    JsonTracer jsonTracer  = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb");
                    string     logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
                        ProductUpgraderInfo.GetLogDirectoryPath(),
                        GVFSConstants.LogFileTypes.UpgradeVerb);
                    jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any);

                    this.tracer        = jsonTracer;
                    this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSConstants.UpgradeVerbMessages.GVFSUpgradeConfirm : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade);

                    IProductUpgrader upgrader;
                    if (ProductUpgraderFactory.TryCreateUpgrader(out upgrader, this.tracer, out error))
                    {
                        this.upgrader = upgrader;
                    }
                    else
                    {
                        error = $"ERROR: {error}";
                    }
                }

                return(this.upgrader != null);
            }
            else
            {
                error = $"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported on this operating system.";
                return(false);
            }
        }
Ejemplo n.º 2
0
        private void CreateAndConfigureUpgradeLogDirectory()
        {
            string            upgradeLogsPath     = ProductUpgraderInfo.GetLogDirectoryPath();
            DirectorySecurity upgradeLogsSecurity = this.GetUpgradeLogsDirectorySecurity(upgradeLogsPath);

            Directory.CreateDirectory(upgradeLogsPath, upgradeLogsSecurity);
            try
            {
                // Call SetAccessControl in case the directory already existed
                // (in which case the above CreateDirectory was a no-op)
                Directory.SetAccessControl(upgradeLogsPath, upgradeLogsSecurity);
            }
            catch (UnauthorizedAccessException e)
            {
                // UnauthorizedAccessException can occur when the upgrade logs directory was
                // created by a non-elevated user running 'gvfs upgrade'.  Only the owner
                // is allowed to modify the ACLs, and if the logs directory was created by
                // the user running 'gvfs upgrade' then the Adminstrators group is not the owner.

                EventMetadata metadata = new EventMetadata();
                metadata.Add("Exception", e.ToString());
                metadata.Add(
                    TracingConstants.MessageKey.InfoMessage,
                    $"{nameof(this.CreateAndConfigureUpgradeLogDirectory)}: UnauthorizedAccessException when setting log directory ACLs");
                this.tracer.RelatedEvent(EventLevel.Informational, "LogDirACL_UnauthorizedAccessException", metadata);

                // To avoid the ownership issues, rename the old log directory, create a new one, and migrate over
                // all of the contents of the old directory.
                this.MigrateUpgradeLogsToDirectoryWithFreshACLs();
            }
        }
Ejemplo n.º 3
0
        public override bool TryPrepareLogDirectory(out string error)
        {
            // Under normal circumstances
            // ProductUpgraderInfo.GetLogDirectoryPath will have
            // already been created by Scalar.Service.  If for some
            // reason it does not (e.g. the service failed to start),
            // we need to create
            // ProductUpgraderInfo.GetLogDirectoryPath() explicity to
            // ensure that it has the correct ACLs (so that both admin
            // and non-admin users can create log files).  If the logs
            // directory does not already exist, this call could fail
            // when running as a non-elevated user.
            string createDirectoryError;

            if (!this.FileSystem.TryCreateDirectoryWithAdminAndUserModifyPermissions(ProductUpgraderInfo.GetLogDirectoryPath(), out createDirectoryError))
            {
                error  = $"ERROR: Unable to create directory `{ProductUpgraderInfo.GetLogDirectoryPath()}`";
                error += $"\n{createDirectoryError}";
                error += $"\n\nTry running {ScalarConstants.UpgradeVerbMessages.ScalarUpgrade} from an elevated command prompt.";
                return(false);
            }

            error = null;
            return(true);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Saves a copy of installer log from platform native installer in the
        /// upgrader diagnose logs directory. On the Mac, it is not possible to
        /// specify custom log file path as command line arg to the installer.
        /// </summary>
        private void SaveSystemInstallerLogs()
        {
            if (GVFSPlatform.Instance.SupportsSystemInstallLog)
            {
                string systemInstallerLog = GVFSPlatform.Instance.GetSystemInstallerLogPath();
                if (!string.IsNullOrEmpty(systemInstallerLog))
                {
                    string destinationPath = GVFSEnlistment.GetNewGVFSLogFileName(
                        ProductUpgraderInfo.GetLogDirectoryPath(),
                        GVFSConstants.LogFileTypes.UpgradeSystemInstaller,
                        this.UpgradeInstanceId);

                    try
                    {
                        using (Stream sourceStream = this.fileSystem.OpenFileStream(
                                   systemInstallerLog,
                                   FileMode.Open,
                                   FileAccess.Read,
                                   FileShare.ReadWrite,
                                   callFlushFileBuffers: false))
                            using (Stream destStream = this.fileSystem.OpenFileStream(
                                       destinationPath,
                                       FileMode.Create,
                                       FileAccess.Write,
                                       FileShare.None,
                                       callFlushFileBuffers: false))
                            {
                                // Copy the last 100K from the system wide installer log.
                                // System wide installer log (/var/log/install.log) holds
                                // all messages from installd, including the ones that
                                // it generated while installing VFSForGit. We don't
                                // need to capture the whole file, which can be lengthy.
                                // From my testing, the last 100K captured immediately
                                // after upgrade, is found to be lengthy enough to
                                // contain all of Git + GCM + Service + VFSForGit
                                // installer log messages.
                                long hundredKB      = 100 * 1024;
                                long copyFromOffset = sourceStream.Length > hundredKB ? sourceStream.Length - hundredKB : 0;

                                sourceStream.Seek(copyFromOffset, SeekOrigin.Begin);
                                sourceStream.CopyTo(destStream);
                            }
                    }
                    catch (Exception ex)
                    {
                        EventMetadata metadata = new EventMetadata();
                        metadata.Add("Exception", ex.ToString());
                        this.tracer.RelatedError(
                            metadata,
                            $"{nameof(this.SaveSystemInstallerLogs)} - Error saving native installer log file.");
                    }
                }
                else
                {
                    this.tracer.RelatedError($"{nameof(this.SaveSystemInstallerLogs)} - Error getting native installer log file path.");
                }
            }
        }
Ejemplo n.º 5
0
        private bool TryInitializeUpgrader(out string error)
        {
            if (this.DryRun && this.Confirmed)
            {
                error = $"{DryRunOption} and {ConfirmOption} arguments are not compatible.";
                return(false);
            }

            if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade)
            {
                error = null;
                if (this.upgrader == null)
                {
                    this.productUpgraderPlatformStrategy = GVFSPlatform.Instance.CreateProductUpgraderPlatformInteractions(this.fileSystem, tracer: null);
                    if (!this.productUpgraderPlatformStrategy.TryPrepareLogDirectory(out error))
                    {
                        return(false);
                    }

                    JsonTracer jsonTracer  = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb");
                    string     logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
                        ProductUpgraderInfo.GetLogDirectoryPath(),
                        GVFSConstants.LogFileTypes.UpgradeVerb);
                    jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any);

                    this.tracer        = jsonTracer;
                    this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSPlatform.Instance.Constants.UpgradeConfirmCommandMessage : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade);

                    string gitBinPath = GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath();
                    if (string.IsNullOrEmpty(gitBinPath))
                    {
                        error = $"nameof(this.TryInitializeUpgrader): Unable to locate git installation. Ensure git is installed and try again.";
                        return(false);
                    }

                    ICredentialStore credentialStore = new GitProcess(gitBinPath, workingDirectoryRoot: null);

                    ProductUpgrader upgrader;
                    if (ProductUpgrader.TryCreateUpgrader(this.tracer, this.fileSystem, new LocalGVFSConfig(), credentialStore, this.DryRun, this.NoVerify, out upgrader, out error))
                    {
                        this.upgrader = upgrader;
                    }
                    else
                    {
                        error = $"ERROR: {error}";
                    }
                }

                return(this.upgrader != null);
            }
            else
            {
                error = $"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported on this operating system.";
                return(false);
            }
        }
Ejemplo n.º 6
0
        public override void Execute()
        {
            this.ValidatePathParameter(this.EnlistmentRootPathParameter);

            this.Output.WriteLine("Most recent log files:");

            string errorMessage;
            string enlistmentRoot;

            if (!GVFSPlatform.Instance.TryGetGVFSEnlistmentRoot(this.EnlistmentRootPathParameter, out enlistmentRoot, out errorMessage))
            {
                this.ReportErrorAndExit(
                    "Error: '{0}' is not a valid GVFS enlistment",
                    this.EnlistmentRootPathParameter);
            }

            string gvfsLogsRoot = Path.Combine(
                enlistmentRoot,
                GVFSPlatform.Instance.Constants.DotGVFSRoot,
                GVFSConstants.DotGVFS.LogName);

            if (this.LogType == null)
            {
                this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.Clone);

                // By using MountPrefix ("mount") DisplayMostRecent will display either mount_verb, mount_upgrade, or mount_process, whichever is more recent
                this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.MountPrefix);
                this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.Prefetch);
                this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.Dehydrate);
                this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.Repair);
                this.DisplayMostRecent(gvfsLogsRoot, GVFSConstants.LogFileTypes.Sparse);

                string serviceLogsRoot = Path.Combine(
                    GVFSPlatform.Instance.GetDataRootForGVFSComponent(GVFSConstants.Service.ServiceName),
                    GVFSConstants.Service.LogDirectory);
                this.DisplayMostRecent(serviceLogsRoot, GVFSConstants.LogFileTypes.Service);

                this.DisplayMostRecent(ProductUpgraderInfo.GetLogDirectoryPath(), GVFSConstants.LogFileTypes.UpgradePrefix);
            }
            else
            {
                string logFile = FindNewestFileInFolder(gvfsLogsRoot, this.LogType);
                if (logFile == null)
                {
                    this.ReportErrorAndExit("No log file found");
                }
                else
                {
                    foreach (string line in File.ReadAllLines(logFile))
                    {
                        this.Output.WriteLine(line);
                    }
                }
            }
        }
Ejemplo n.º 7
0
        private void MigrateUpgradeLogsToDirectoryWithFreshACLs()
        {
            string upgradeLogsPath     = ProductUpgraderInfo.GetLogDirectoryPath();
            string tempUpgradeLogsPath = Path.Combine(
                Path.GetDirectoryName(upgradeLogsPath),
                ProductUpgraderInfo.LogDirectory + "_" + Guid.NewGuid().ToString("N"));

            this.tracer.RelatedInfo($"{nameof(this.MigrateUpgradeLogsToDirectoryWithFreshACLs)}: Renaming '{upgradeLogsPath}' to '{tempUpgradeLogsPath}'");
            Directory.Move(upgradeLogsPath, tempUpgradeLogsPath);

            this.tracer.RelatedInfo($"{nameof(this.MigrateUpgradeLogsToDirectoryWithFreshACLs)}: Creating new '{upgradeLogsPath}' directory with appropriate ACLs");
            DirectorySecurity upgradeLogsSecurity = this.GetUpgradeLogsDirectorySecurity(upgradeLogsPath);

            Directory.CreateDirectory(upgradeLogsPath, upgradeLogsSecurity);

            try
            {
                DirectoryInfo tempDirectoryInfo = new DirectoryInfo(tempUpgradeLogsPath);

                this.tracer.RelatedInfo($"Moving directories from '{tempUpgradeLogsPath}' to '{upgradeLogsPath}'");
                foreach (DirectoryInfo logDirectoryInfo in tempDirectoryInfo.EnumerateDirectories(searchPattern: "*", searchOption: SearchOption.TopDirectoryOnly))
                {
                    Directory.Move(logDirectoryInfo.FullName, Path.Combine(upgradeLogsPath, logDirectoryInfo.Name));
                }

                this.tracer.RelatedInfo($"Moving files from '{tempUpgradeLogsPath}' to '{upgradeLogsPath}'");
                foreach (FileInfo logFileInfo in tempDirectoryInfo.EnumerateFiles(searchPattern: "*", searchOption: SearchOption.TopDirectoryOnly))
                {
                    File.Move(logFileInfo.FullName, Path.Combine(upgradeLogsPath, logFileInfo.Name));
                }

                FileSystemInfo[] remainingChildren = tempDirectoryInfo.GetFileSystemInfos();
                if (remainingChildren.Length > 0)
                {
                    this.tracer.RelatedWarning(
                        $"{nameof(this.MigrateUpgradeLogsToDirectoryWithFreshACLs)}: Skipping delete of old directory, {remainingChildren.Length} items still present on disk");
                }
                else
                {
                    PhysicalFileSystem fileSystem = new PhysicalFileSystem();
                    fileSystem.DeleteDirectory(tempUpgradeLogsPath, recursive: false);
                }
            }
            catch (Exception e)
            {
                EventMetadata metadata = new EventMetadata();
                metadata.Add("Exception", e.ToString());
                metadata.Add(nameof(tempUpgradeLogsPath), tempUpgradeLogsPath);
                metadata.Add(nameof(upgradeLogsPath), upgradeLogsPath);

                this.tracer.RelatedWarning(
                    metadata,
                    $"{nameof(this.MigrateUpgradeLogsToDirectoryWithFreshACLs)}: Caught exception migrating files from the old upgrade log directory");
            }
        }
Ejemplo n.º 8
0
        private bool TryInitializeUpgrader(out string error)
        {
            if (this.DryRun && this.Confirmed)
            {
                error = $"{DryRunOption} and {ConfirmOption} arguments are not compatible.";
                return(false);
            }

            if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade)
            {
                error = null;
                if (this.upgrader == null)
                {
                    // Under normal circumstances ProductUpgraderInfo.GetLogDirectoryPath will have already been created by GVFS.Service.  If for some reason it
                    // does not (e.g. the service failed to start), we need to create ProductUpgraderInfo.GetLogDirectoryPath() explicity to ensure that
                    // it has the correct ACLs (so that both admin and non-admin users can create log files).
                    // If the logs directory does not already exist, this call could fail when running as a non-elevated user.
                    string createDirectoryError;
                    if (!this.fileSystem.TryCreateDirectoryWithAdminAndUserModifyPermissions(ProductUpgraderInfo.GetLogDirectoryPath(), out createDirectoryError))
                    {
                        error  = $"ERROR: Unable to create directory `{ProductUpgraderInfo.GetLogDirectoryPath()}`";
                        error += $"\n{createDirectoryError}";
                        error += $"\n\nTry running {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} from an elevated command prompt.";
                        return(false);
                    }

                    JsonTracer jsonTracer  = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb");
                    string     logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
                        ProductUpgraderInfo.GetLogDirectoryPath(),
                        GVFSConstants.LogFileTypes.UpgradeVerb);
                    jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any);

                    this.tracer        = jsonTracer;
                    this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSConstants.UpgradeVerbMessages.GVFSUpgradeConfirm : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade);

                    ProductUpgrader upgrader;
                    if (ProductUpgrader.TryCreateUpgrader(this.tracer, this.fileSystem, this.DryRun, this.NoVerify, out upgrader, out error))
                    {
                        this.upgrader = upgrader;
                    }
                    else
                    {
                        error = $"ERROR: {error}";
                    }
                }

                return(this.upgrader != null);
            }
            else
            {
                error = $"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported on this operating system.";
                return(false);
            }
        }
Ejemplo n.º 9
0
        private void CreateAndConfigureProgramDataDirectories()
        {
            string serviceDataRootPath = ScalarPlatform.Instance.GetSecureDataRootForScalar();

            // Create Scalar.Service and Scalar.Upgrade related directories (if they don't already exist)
            // TODO #136: Determine if we still should be creating Scalar.Service here
            DirectoryEx.CreateDirectory(serviceDataRootPath);
            DirectoryEx.CreateDirectory(this.serviceDataLocation);
            DirectoryEx.CreateDirectory(ProductUpgraderInfo.GetUpgradeProtectedDataDirectory());

            // Special rules for the upgrader logs and registry, as non-elevated users need to be be able to write
            this.CreateAndConfigureUserWriteableDirectory(this.repoRegistryLocation);
            this.CreateAndConfigureUserWriteableDirectory(ProductUpgraderInfo.GetLogDirectoryPath());
            this.CreateAndConfigureUserWriteableDirectory(ScalarPlatform.Instance.GetLogsDirectoryForGVFSComponent(ScalarConstants.Service.UIName));
        }
        private void CreateAndConfigureProgramDataDirectories()
        {
            string serviceDataRootPath = Path.GetDirectoryName(this.serviceDataLocation);

            DirectorySecurity serviceDataRootSecurity = this.GetServiceDirectorySecurity(serviceDataRootPath);

            // Create GVFS.Service and GVFS.Upgrade related directories (if they don't already exist)
            Directory.CreateDirectory(serviceDataRootPath, serviceDataRootSecurity);
            Directory.CreateDirectory(this.serviceDataLocation, serviceDataRootSecurity);
            Directory.CreateDirectory(ProductUpgraderInfo.GetUpgradeProtectedDataDirectory(), serviceDataRootSecurity);

            // Ensure the ACLs are set correctly on any files or directories that were already created (e.g. after upgrading VFS4G)
            Directory.SetAccessControl(serviceDataRootPath, serviceDataRootSecurity);

            // Special rules for the upgrader logs, as non-elevated users need to be be able to write
            this.CreateAndConfigureLogDirectory(ProductUpgraderInfo.GetLogDirectoryPath());
            this.CreateAndConfigureLogDirectory(GVFSPlatform.Instance.GetDataRootForGVFSComponent(GVFSConstants.Service.UIName));
        }
Ejemplo n.º 11
0
        private void CreateAndConfigureUpgradeLogDirectory()
        {
            string upgradeLogsPath = ProductUpgraderInfo.GetLogDirectoryPath();

            string error;

            if (!GVFSPlatform.Instance.FileSystem.TryCreateDirectoryWithAdminAndUserModifyPermissions(upgradeLogsPath, out error))
            {
                EventMetadata metadata = new EventMetadata();
                metadata.Add("Area", EtwArea);
                metadata.Add(nameof(upgradeLogsPath), upgradeLogsPath);
                metadata.Add(nameof(error), error);
                this.tracer.RelatedWarning(
                    metadata,
                    $"{nameof(this.CreateAndConfigureUpgradeLogDirectory)}: Failed to create upgrade logs directory",
                    Keywords.Telemetry);
            }
        }
Ejemplo n.º 12
0
        public UpgradeOrchestrator()
        {
            string logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
                ProductUpgraderInfo.GetLogDirectoryPath(),
                GVFSConstants.LogFileTypes.UpgradeProcess);
            JsonTracer jsonTracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeProcess");

            jsonTracer.AddLogFileEventListener(
                logFilePath,
                DefaultEventLevel,
                Keywords.Any);

            this.tracer        = jsonTracer;
            this.preRunChecker = new InstallerPreRunChecker(this.tracer, GVFSConstants.UpgradeVerbMessages.GVFSUpgradeConfirm);
            this.output        = Console.Out;
            this.input         = Console.In;
            this.mount         = false;
            this.ExitCode      = ReturnCode.Success;
        }
Ejemplo n.º 13
0
        public override bool TryRunInstaller(InstallActionWrapper installActionWrapper, out string error)
        {
            string localError = null;
            int    installerExitCode;
            bool   installSuccessful = true;

            using (ITracer activity = this.tracer.StartActivity(nameof(this.TryRunInstaller), EventLevel.Informational))
            {
                InstallActionInfo currentInstallAction = null;
                try
                {
                    string platformKey = InstallManifest.WindowsPlatformKey;

                    if (!this.TryRecursivelyDeleteInstallerDirectory(out error))
                    {
                        return(false);
                    }

                    if (!this.noVerify)
                    {
                        if (!this.nuGetFeed.VerifyPackage(this.DownloadedPackagePath))
                        {
                            error = "Package signature validation failed. Check the upgrade logs for more details.";
                            activity.RelatedError(error);
                            this.fileSystem.DeleteFile(this.DownloadedPackagePath);
                            return(false);
                        }
                    }

                    this.UnzipPackage();
                    this.installManifest = InstallManifest.FromJsonFile(Path.Combine(this.ExtractedInstallerPath, ContentDirectoryName, InstallManifestFileName));
                    if (!this.installManifest.PlatformInstallManifests.TryGetValue(platformKey, out InstallManifestPlatform platformInstallManifest) ||
                        platformInstallManifest == null)
                    {
                        activity.RelatedError($"Extracted InstallManifest from JSON, but there was no entry for {platformKey}.");
                        error = $"No entry in the manifest for the current platform ({platformKey}). Please verify the upgrade package.";
                        return(false);
                    }

                    activity.RelatedInfo($"Extracted InstallManifest from JSON. InstallActions: {platformInstallManifest.InstallActions.Count}");

                    foreach (InstallActionInfo entry in platformInstallManifest.InstallActions)
                    {
                        currentInstallAction = entry;
                        string installerPath = Path.Combine(this.ExtractedInstallerPath, ContentDirectoryName, entry.InstallerRelativePath);

                        string args = entry.Args ?? string.Empty;

                        // Replace tokens on args
                        string processedArgs = NuGetUpgrader.ReplaceArgTokens(args, this.UpgradeInstanceId, ProductUpgraderInfo.GetLogDirectoryPath());

                        activity.RelatedInfo(
                            "Running install action: Name: {0}, Version: {1}, InstallerPath: {2} RawArgs: {3}, ProcessedArgs: {4}",
                            entry.Name,
                            entry.Version,
                            installerPath,
                            args,
                            processedArgs);

                        string progressMessage = string.IsNullOrWhiteSpace(entry.Version) ?
                                                 $"Running {entry.Name}" :
                                                 $"Running {entry.Name} (version {entry.Version})";

                        installActionWrapper(
                            () =>
                        {
                            if (!this.dryRun)
                            {
                                this.RunInstaller(installerPath, processedArgs, out installerExitCode, out localError);
                            }
                            else
                            {
                                // We add a sleep here to ensure
                                // the message for this install
                                // action is written to the
                                // console.  Even though the
                                // message is written with a delay
                                // of 0, the messages are not
                                // always written out.  If / when
                                // we can ensure that the message
                                // is written out to console, then
                                // we can remove this sleep.
                                Thread.Sleep(1500);
                                installerExitCode = 0;
                            }

                            installSuccessful = installerExitCode == 0;

                            return(installSuccessful);
                        },
                            progressMessage);

                        if (!installSuccessful)
                        {
                            break;
                        }
                    }
                }
                catch (Exception ex)
                {
                    localError        = ex.Message;
                    installSuccessful = false;
                }

                if (!installSuccessful)
                {
                    string installActionName = string.IsNullOrEmpty(currentInstallAction?.Name) ?
                                               "installer" :
                                               currentInstallAction.Name;

                    error = string.IsNullOrEmpty(localError) ?
                            $"The {installActionName} failed, but no error message was provided by the failing command." :
                            $"The {installActionName} failed with the following error: {localError}";

                    activity.RelatedError($"Could not complete all install actions. The following error was encountered: {error}");
                    return(false);
                }
                else
                {
                    activity.RelatedInfo($"Install actions completed successfully.");
                    error = null;
                    return(true);
                }
            }
        }