public void TestBuildSnapAppAssembly() { var snapAppBefore = _baseFixture.BuildSnapApp(); using var assembly = _snapAppWriter.BuildSnapAppAssembly(snapAppBefore); var snapAppAfter = assembly.GetSnapApp(_snapAppReader); Assert.NotNull(snapAppAfter); }
public async Task TestGetSnapAppFromDirectory() { var snapApp = _baseFixture.BuildSnapApp(); await using var tmpDir = _baseFixture.WithDisposableTempDirectory(_fileSystem); using var assemblyDefinition = _appWriter.BuildSnapAppAssembly(snapApp); var snapAppDllAbsolutePath = _fileSystem.PathCombine(tmpDir.WorkingDirectory, assemblyDefinition.BuildRelativeFilename()); assemblyDefinition.Write(snapAppDllAbsolutePath); var appSpecAfter = tmpDir.WorkingDirectory.GetSnapAppFromDirectory(_fileSystem, _appReader); Assert.NotNull(appSpecAfter); }
public void TestBuildSnapAppAssembly_Throws_If_Channel_UpdateFeed_Source_Is_Null() { var snapAppBefore = _baseFixture.BuildSnapApp(); snapAppBefore.Channels.ForEach(x => { x.UpdateFeed.Source = null; }); Assert.True(snapAppBefore.Channels.Count > 0); var ex = Assert.Throws <Exception>(() => _snapAppWriter.BuildSnapAppAssembly(snapAppBefore)); Assert.StartsWith("Update feed Source cannot be null", ex.Message); }
async Task InvokePostInstall(SnapApp snapApp, NuspecReader nuspecReader, string baseDirectory, string appDirectory, SemanticVersion currentVersion, bool isInitialInstall, ILog logger = null, CancellationToken cancellationToken = default) { var chmod = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows); var coreRunExeAbsolutePath = _snapOs.Filesystem .PathCombine(baseDirectory, _snapEmbeddedResources.GetCoreRunExeFilenameForSnapApp(snapApp)); var mainExeAbsolutePath = _snapOs.Filesystem .PathCombine(appDirectory, _snapEmbeddedResources.GetCoreRunExeFilenameForSnapApp(snapApp)); var iconAbsolutePath = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && snapApp.Target.Icon != null? _snapOs.Filesystem.PathCombine(appDirectory, snapApp.Target.Icon) : null; var snapChannel = snapApp.GetCurrentChannelOrThrow(); logger?.Debug($"{nameof(coreRunExeAbsolutePath)}: {coreRunExeAbsolutePath}"); logger?.Debug($"{nameof(mainExeAbsolutePath)}: {mainExeAbsolutePath}"); logger?.Debug($"{nameof(iconAbsolutePath)}: {iconAbsolutePath}"); async Task ChmodAsync(string exePath) { if (exePath == null) { throw new ArgumentNullException(nameof(exePath)); } logger?.Info($"Attempting to change file permission for executable: {exePath}."); var chmodSuccess = await _snapOs.ProcessManager.ChmodExecuteAsync(exePath, cancellationToken); logger?.Info($"Permissions changed successfully: {(chmodSuccess ? "true" : "false")}."); } if (chmod) { await ChmodAsync(coreRunExeAbsolutePath); await ChmodAsync(mainExeAbsolutePath); } var coreRunExeFilename = _snapOs.Filesystem.PathGetFileName(coreRunExeAbsolutePath); if (!snapApp.Target.Shortcuts.Any()) { logger?.Warn("This application does not specify any shortcut locations."); } else { var shortcutLocations = snapApp.Target.Shortcuts.First(); snapApp.Target.Shortcuts.Skip(1).ForEach(x => shortcutLocations |= x); logger?.Info($"Shortcuts will be created in the following locations: {string.Join(", ", shortcutLocations)}"); try { var shortcutDescription = new SnapOsShortcutDescription { SnapApp = snapApp, UpdateOnly = isInitialInstall == false, NuspecReader = nuspecReader, ShortcutLocations = shortcutLocations, ExeAbsolutePath = coreRunExeAbsolutePath, IconAbsolutePath = iconAbsolutePath }; await _snapOs.CreateShortcutsForExecutableAsync(shortcutDescription, logger, cancellationToken); } catch (Exception e) { logger?.ErrorException($"Exception thrown while creating shortcut for exe: {coreRunExeFilename}", e); } } var snapAppDllAbsolutePath = _snapOs.Filesystem.PathCombine(appDirectory, SnapConstants.SnapAppDllFilename); try { logger?.Info($"Updating {snapAppDllAbsolutePath}. Current channel is: {snapChannel.Name}."); using var snapAppDllAssemblyDefinition = _snapAppWriter.BuildSnapAppAssembly(snapApp); using var snapAPpDllDestinationStream = _snapOs.Filesystem.FileWrite(snapAppDllAbsolutePath); snapAppDllAssemblyDefinition.Write(snapAPpDllDestinationStream); } catch (Exception e) { logger?.ErrorException($"Unknown error updating {snapAppDllAbsolutePath}", e); } var allSnapAwareApps = new List <string> { mainExeAbsolutePath }.Select(x => { var installOrUpdateTxt = isInitialInstall ? "--snapx-installed" : "--snapx-updated"; return(new ProcessStartInfoBuilder(x) .Add(installOrUpdateTxt) .Add(currentVersion.ToNormalizedString())); }) .ToList(); await InvokeSnapApps(allSnapAwareApps, TimeSpan.FromSeconds(15), isInitialInstall, currentVersion, logger, cancellationToken); }
public async Task TestInstall_Offline_Using_Local_PackageSource() { await using var specialFoldersAnyOs = new SnapOsSpecialFoldersUnitTest(_snapFilesystem, _baseFixturePackaging.WorkingDirectory); await using var packagesDirectory = new DisposableDirectory(specialFoldersAnyOs.WorkingDirectory, _snapFilesystem); using var cts = new CancellationTokenSource(); SetupSnapOsMock(specialFoldersAnyOs); var snapInstallerIoEnvironment = new SnapInstallerIoEnvironment { WorkingDirectory = specialFoldersAnyOs.WorkingDirectory, ThisExeWorkingDirectory = specialFoldersAnyOs.WorkingDirectory, SpecialFolders = specialFoldersAnyOs, }; var snapAppsReleases = new SnapAppsReleases(); var genesisSnapApp = _baseFixturePackaging.BuildSnapApp(localPackageSourceDirectory: packagesDirectory); Assert.True(genesisSnapApp.Channels.Count >= 2); using var genesisSnapReleaseBuilder = _baseFixturePackaging.WithSnapReleaseBuilder(packagesDirectory, snapAppsReleases, genesisSnapApp, _snapReleaseBuilderContext); var mainAssemblyDefinition = _baseFixturePackaging.BuildSnapExecutable(genesisSnapApp); genesisSnapReleaseBuilder .AddNuspecItem(mainAssemblyDefinition) .AddSnapDll(); using var genesisPackageContext = await _baseFixturePackaging.BuildPackageAsync(genesisSnapReleaseBuilder, cts.Token); var releasesFilename = _snapFilesystem.PathCombine(snapInstallerIoEnvironment.ThisExeWorkingDirectory, genesisPackageContext.FullPackageSnapApp.BuildNugetReleasesFilename()); var snapAppDllFilename = _snapFilesystem.PathCombine(snapInstallerIoEnvironment.ThisExeWorkingDirectory, SnapConstants.SnapAppDllFilename); var setupNupkgFilename = _snapFilesystem.PathCombine(snapInstallerIoEnvironment.ThisExeWorkingDirectory, SnapConstants.SetupNupkgFilename); await using var releasePackageMemoryStream = _snapPack.BuildReleasesPackage(genesisSnapApp, snapAppsReleases); await _snapFilesystem.FileWriteAsync(genesisPackageContext.FullPackageMemoryStream, setupNupkgFilename, cts.Token); await _snapFilesystem.FileWriteAsync(releasePackageMemoryStream, releasesFilename, cts.Token); using var snapAppDllAssemblyDefinition = _snapAppWriter.BuildSnapAppAssembly(genesisSnapApp); snapAppDllAssemblyDefinition.Write(snapAppDllFilename); var(exitCode, installerType) = await Program.MainImplAsync(new[] { "--headless" }, LogLevel.Info, cts, _snapOsMock.Object, x => { x.Register <ISnapInstallerIoEnvironment>(_ => snapInstallerIoEnvironment); return((ISnapInstallerEnvironment)x.GetInstance(typeof(ISnapInstallerEnvironment))); }); Assert.Equal(0, exitCode); Assert.Equal(SnapInstallerType.Offline, installerType); var appInstallDirectory = _snapInstaller.GetApplicationDirectory( _snapFilesystem.PathCombine(specialFoldersAnyOs.LocalApplicationData, genesisSnapApp.Id), genesisSnapApp.Version); var files = _snapFilesystem.DirectoryGetAllFiles(appInstallDirectory).OrderBy(x => x, new OrdinalIgnoreCaseComparer()).ToList(); Assert.Equal(3, files.Count); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Assert.EndsWith($"{genesisSnapApp.Id}.exe", files[0]); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { Assert.EndsWith(genesisSnapApp.Id, files[0]); } else { throw new PlatformNotSupportedException(); } Assert.EndsWith(SnapConstants.SnapAppDllFilename, files[1]); Assert.EndsWith(SnapConstants.SnapDllFilename, files[2]); }
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); }