Example #1
0
        static int CommandRcEdit([NotNull] RcEditOptions opts, [NotNull] ICoreRunLib coreRunLib,
                                 [NotNull] ISnapFilesystem snapFilesystem, [NotNull] ILog logger)
        {
            if (opts == null)
            {
                throw new ArgumentNullException(nameof(opts));
            }
            if (coreRunLib == null)
            {
                throw new ArgumentNullException(nameof(coreRunLib));
            }
            if (snapFilesystem == null)
            {
                throw new ArgumentNullException(nameof(snapFilesystem));
            }
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            var exitCode = 1;

            if (!snapFilesystem.FileExists(opts.Filename))
            {
                logger.Error($"Filename does not exist: {opts.Filename}.");
                goto done;
            }

            if (opts.ConvertSubSystemToWindowsGui)
            {
                logger.Info($"Attempting to change subsystem to Windows GUI for executable: {snapFilesystem.PathGetFileName(opts.Filename)}.");

                using (var srcStream = snapFilesystem.FileReadWrite(opts.Filename, false))
                {
                    if (!srcStream.ChangeSubsystemToWindowsGui(SnapLogger))
                    {
                        goto done;
                    }

                    logger.Info("Subsystem has been successfully changed to Windows GUI.");
                }

                exitCode = 0;
            }

            if (opts.IconFilename != null)
            {
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    logger.Error("Modifying executable icon is not supported on this OS platform.");
                    goto done;
                }
                opts.IconFilename = snapFilesystem.PathGetFullPath(opts.IconFilename);
                if (!snapFilesystem.FileExists(opts.IconFilename))
                {
                    logger.Error($"Unable to find icon with filename: {opts.IconFilename}");
                    goto done;
                }

                if (!coreRunLib.SetIcon(opts.Filename, opts.IconFilename))
                {
                    logger.Error($"Unknown error setting icon for executable {opts.Filename}. Icon filename: {opts.Filename}.");
                    goto done;
                }

                logger.Info("Icon has been successfully updated.");
                exitCode = 0;
            }

done:
            return(exitCode);
        }
Example #2
0
        static async Task <(bool success, bool canContinueIfError, string installerExeAbsolutePath)> BuildInstallerAsync([NotNull] ILog logger, [NotNull] ISnapOs snapOs,
                                                                                                                         [NotNull] ISnapxEmbeddedResources snapxEmbeddedResources, [NotNull] ISnapPack snapPack, [NotNull] ISnapAppReader snapAppReader,
                                                                                                                         [NotNull] ISnapAppWriter snapAppWriter, [NotNull] SnapApp snapApp, ICoreRunLib coreRunLib,
                                                                                                                         [NotNull] string installersWorkingDirectory, string fullNupkgAbsolutePath, [NotNull] string releasesNupkgAbsolutePath, bool offline,
                                                                                                                         CancellationToken cancellationToken)
        {
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }
            if (snapOs == null)
            {
                throw new ArgumentNullException(nameof(snapOs));
            }
            if (snapxEmbeddedResources == null)
            {
                throw new ArgumentNullException(nameof(snapxEmbeddedResources));
            }
            if (snapPack == null)
            {
                throw new ArgumentNullException(nameof(snapPack));
            }
            if (snapAppReader == null)
            {
                throw new ArgumentNullException(nameof(snapAppReader));
            }
            if (snapAppWriter == null)
            {
                throw new ArgumentNullException(nameof(snapAppWriter));
            }
            if (snapApp == null)
            {
                throw new ArgumentNullException(nameof(snapApp));
            }
            if (installersWorkingDirectory == null)
            {
                throw new ArgumentNullException(nameof(installersWorkingDirectory));
            }
            if (releasesNupkgAbsolutePath == null)
            {
                throw new ArgumentNullException(nameof(releasesNupkgAbsolutePath));
            }

            var installerPrefix = offline ? "offline" : "web";
            var snapChannel     = snapApp.GetCurrentChannelOrThrow();

            logger.Info($"Preparing to build {installerPrefix} installer for channel: {snapChannel.Name}. Version: {snapApp.Version}.");

            var progressSource = new SnapProgressSource {
                Progress = percentage => { logger.Info($"Progress: {percentage}%."); }
            };

            using var rootTempDir = snapOs.Filesystem.WithDisposableTempDirectory(installersWorkingDirectory);
            MemoryStream installerZipMemoryStream;
            MemoryStream warpPackerMemoryStream;

            string snapAppTargetRid;
            string warpPackerRid;
            string warpPackerArch;
            string installerFilename;
            string setupExtension;
            string setupIcon = null;
            var    chmod     = false;
            var    changeSubSystemToWindowsGui = false;
            var    installerIconSupported      = false;

            if (snapOs.OsPlatform == OSPlatform.Windows)
            {
                warpPackerMemoryStream = snapxEmbeddedResources.WarpPackerWindows;
                warpPackerRid          = "win-x64";
                installerIconSupported = true;
            }
            else if (snapOs.OsPlatform == OSPlatform.Linux)
            {
                warpPackerMemoryStream = snapxEmbeddedResources.WarpPackerLinux;
                warpPackerRid          = "linux-x64";
                chmod = true;
            }
            else
            {
                throw new PlatformNotSupportedException();
            }

            switch (snapApp.Target.Rid)
            {
            case "win-x64":
                installerZipMemoryStream    = snapxEmbeddedResources.SetupWindows;
                warpPackerArch              = "windows-x64";
                snapAppTargetRid            = "win-x64";
                installerFilename           = "Snap.Installer.exe";
                changeSubSystemToWindowsGui = true;
                setupExtension              = ".exe";
                if (installerIconSupported && snapApp.Target.Icon != null)
                {
                    setupIcon = snapApp.Target.Icon;
                }
                break;

            case "linux-x64":
                installerZipMemoryStream = snapxEmbeddedResources.SetupLinux;
                warpPackerArch           = "linux-x64";
                snapAppTargetRid         = "linux-x64";
                installerFilename        = "Snap.Installer";
                setupExtension           = ".bin";
                break;

            default:
                throw new PlatformNotSupportedException($"Unsupported rid: {snapApp.Target.Rid}");
            }

            var repackageTempDir = snapOs.Filesystem.PathCombine(rootTempDir.WorkingDirectory, "repackage");

            snapOs.Filesystem.DirectoryCreateIfNotExists(repackageTempDir);

            var rootTempDirWarpPackerAbsolutePath = snapOs.Filesystem.PathCombine(rootTempDir.WorkingDirectory, $"warp-packer-{warpPackerRid}.exe");
            var installerRepackageAbsolutePath    = snapOs.Filesystem.PathCombine(repackageTempDir, installerFilename);

            async Task BuildOfflineInstallerAsync()
            {
                if (fullNupkgAbsolutePath == null)
                {
                    throw new ArgumentNullException(nameof(fullNupkgAbsolutePath));
                }

                var repackageDirSnapAppDllAbsolutePath    = snapOs.Filesystem.PathCombine(repackageTempDir, SnapConstants.SnapAppDllFilename);
                var repackageDirFullNupkgAbsolutePath     = snapOs.Filesystem.PathCombine(repackageTempDir, "Setup.nupkg");
                var repackageDirReleasesNupkgAbsolutePath = snapOs.Filesystem.PathCombine(repackageTempDir,
                                                                                          snapOs.Filesystem.PathGetFileName(releasesNupkgAbsolutePath));

                await using (installerZipMemoryStream)
                    await using (warpPackerMemoryStream)
                    {
                        using var snapAppAssemblyDefinition       = snapAppWriter.BuildSnapAppAssembly(snapApp);
                        await using var snapAppDllDstMemoryStream = snapOs.Filesystem.FileWrite(repackageDirSnapAppDllAbsolutePath);
                        await using var warpPackerDstStream       = snapOs.Filesystem.FileWrite(rootTempDirWarpPackerAbsolutePath);
                        using var zipArchive = new ZipArchive(installerZipMemoryStream, ZipArchiveMode.Read);
                        snapAppAssemblyDefinition.Write(snapAppDllDstMemoryStream);

                        progressSource.Raise(10);

                        logger.Info("Extracting installer to temp directory.");
                        zipArchive.ExtractToDirectory(repackageTempDir);

                        progressSource.Raise(20);

                        logger.Info("Copying assets to temp directory.");

                        await Task.WhenAll(
                            warpPackerMemoryStream.CopyToAsync(warpPackerDstStream, cancellationToken),
                            snapOs.Filesystem.FileCopyAsync(fullNupkgAbsolutePath, repackageDirFullNupkgAbsolutePath, cancellationToken),
                            snapOs.Filesystem.FileCopyAsync(releasesNupkgAbsolutePath, repackageDirReleasesNupkgAbsolutePath, cancellationToken));

                        if (installerIconSupported && setupIcon != null)
                        {
                            logger.Debug($"Writing installer icon: {setupIcon}.");

                            var zipArchiveInstallerFilename = snapOs.Filesystem.PathCombine(repackageTempDir, installerFilename);

                            var rcEditOptions = new RcEditOptions
                            {
                                Filename     = zipArchiveInstallerFilename,
                                IconFilename = setupIcon
                            };

                            CommandRcEdit(rcEditOptions, coreRunLib, snapOs.Filesystem, logger);
                        }
                    }
            }

            async Task BuildWebInstallerAsync()
            {
                var repackageDirSnapAppDllAbsolutePath = snapOs.Filesystem.PathCombine(repackageTempDir, SnapConstants.SnapAppDllFilename);

                await using (installerZipMemoryStream)
                    await using (warpPackerMemoryStream)
                    {
                        await using var warpPackerDstStream = snapOs.Filesystem.FileWrite(rootTempDirWarpPackerAbsolutePath);
                        using var zipArchive = new ZipArchive(installerZipMemoryStream, ZipArchiveMode.Read);
                        using var snapAppAssemblyDefinition       = snapAppWriter.BuildSnapAppAssembly(snapApp);
                        await using var snapAppDllDstMemoryStream = snapOs.Filesystem.FileWrite(repackageDirSnapAppDllAbsolutePath);
                        snapAppAssemblyDefinition.Write(snapAppDllDstMemoryStream);

                        progressSource.Raise(10);

                        logger.Info("Extracting installer to temp directory.");
                        zipArchive.ExtractToDirectory(repackageTempDir);

                        progressSource.Raise(20);

                        logger.Info("Copying assets to temp directory.");

                        await Task.WhenAll(
                            warpPackerMemoryStream.CopyToAsync(warpPackerDstStream, cancellationToken));

                        if (installerIconSupported && setupIcon != null)
                        {
                            logger.Debug($"Writing installer icon: {setupIcon}.");

                            var zipArchiveInstallerFilename = snapOs.Filesystem.PathCombine(repackageTempDir, installerFilename);

                            var rcEditOptions = new RcEditOptions
                            {
                                Filename     = zipArchiveInstallerFilename,
                                IconFilename = setupIcon
                            };

                            CommandRcEdit(rcEditOptions, coreRunLib, snapOs.Filesystem, logger);
                        }
                    }
            }

            var installerFinalAbsolutePath = snapOs.Filesystem.PathCombine(installersWorkingDirectory,
                                                                           $"Setup-{snapAppTargetRid}-{snapApp.Id}-{snapChannel.Name}-{installerPrefix}{setupExtension}");

            if (offline)
            {
                await BuildOfflineInstallerAsync();
            }
            else
            {
                await BuildWebInstallerAsync();
            }

            progressSource.Raise(50);

            var processStartInfoBuilder = new ProcessStartInfoBuilder(rootTempDirWarpPackerAbsolutePath)
                                          .Add($"--arch {warpPackerArch}")
                                          .Add($"--exec {installerFilename}")
                                          .Add($"--output {installerFinalAbsolutePath.ForwardSlashesSafe()}")
                                          .Add($"--input_dir {repackageTempDir.ForwardSlashesSafe()}");

            if (chmod)
            {
                await snapOs.ProcessManager.ChmodExecuteAsync(rootTempDirWarpPackerAbsolutePath, cancellationToken);

                await snapOs.ProcessManager.ChmodExecuteAsync(installerRepackageAbsolutePath, cancellationToken);
            }

            logger.Info("Packaging installer.");

            var(exitCode, stdout) = await snapOs.ProcessManager.RunAsync(processStartInfoBuilder, cancellationToken);

            if (exitCode != 0)
            {
                logger.Error(
                    $"Warp packer exited with error code: {exitCode}. Warp packer executable path: {rootTempDirWarpPackerAbsolutePath}. Stdout: {stdout}.");
                return(false, false, null);
            }

            progressSource.Raise(80);

            if (changeSubSystemToWindowsGui)
            {
                // NB! Unable to set icon on warped executable. Please refer to the following issue:
                // https://github.com/electron/rcedit/issues/70

                var rcEditOptions = new RcEditOptions
                {
                    ConvertSubSystemToWindowsGui = true,
                    Filename = installerFinalAbsolutePath,
                    //IconFilename = setupIcon
                };

                CommandRcEdit(rcEditOptions, coreRunLib, snapOs.Filesystem, logger);
            }

            if (chmod)
            {
                await snapOs.ProcessManager.ChmodExecuteAsync(installerFinalAbsolutePath, cancellationToken);
            }

            progressSource.Raise(100);

            return(true, false, installerFinalAbsolutePath);
        }