public async Task <List <string> > ExtractAsync(string destinationDirectoryAbsolutePath, [NotNull] SnapRelease snapRelease, IAsyncPackageCoreReader asyncPackageCoreReader, CancellationToken cancellationToken = default) { if (destinationDirectoryAbsolutePath == null) { throw new ArgumentNullException(nameof(destinationDirectoryAbsolutePath)); } if (snapRelease == null) { throw new ArgumentNullException(nameof(snapRelease)); } if (asyncPackageCoreReader == null) { throw new ArgumentNullException(nameof(asyncPackageCoreReader)); } var snapApp = await _snapPack.GetSnapAppAsync(asyncPackageCoreReader, cancellationToken); var coreRunExeFilename = _snapEmbeddedResources.GetCoreRunExeFilenameForSnapApp(snapApp); var extractedFiles = new List <string>(); _snapFilesystem.DirectoryCreateIfNotExists(destinationDirectoryAbsolutePath); var files = !snapRelease.IsFull ? snapRelease .New .Concat(snapRelease.Modified) .OrderBy(x => x.NuspecTargetPath, new OrdinalIgnoreCaseComparer()) .ToList() : snapRelease.Files; foreach (var checksum in files) { var isSnapRootTargetItem = checksum.NuspecTargetPath.StartsWith(SnapConstants.NuspecAssetsTargetPath); string dstFilename; if (isSnapRootTargetItem) { dstFilename = _snapFilesystem.PathCombine(destinationDirectoryAbsolutePath, checksum.Filename); if (checksum.Filename == coreRunExeFilename) { dstFilename = _snapFilesystem.PathCombine( _snapFilesystem.DirectoryGetParent(destinationDirectoryAbsolutePath), checksum.Filename); } } else { var targetPath = checksum.NuspecTargetPath[(SnapConstants.NuspecRootTargetPath.Length + 1)..];
public async Task TestInstallAsync() { var snapAppsReleases = new SnapAppsReleases(); var genesisSnapApp = _baseFixture.BuildSnapApp(); Assert.True(genesisSnapApp.Channels.Count >= 2); using var testDirectory = new DisposableDirectory(_baseFixture.WorkingDirectory, _snapFilesystem); using var genesisSnapReleaseBuilder = _baseFixture.WithSnapReleaseBuilder(testDirectory, snapAppsReleases, genesisSnapApp, _snapReleaseBuilderContext); var mainAssemblyDefinition = _baseFixture.BuildSnapExecutable(genesisSnapApp); genesisSnapReleaseBuilder .AddNuspecItem(mainAssemblyDefinition) .AddNuspecItem(mainAssemblyDefinition.BuildRuntimeConfigFilename(_snapFilesystem), mainAssemblyDefinition.BuildRuntimeConfig()) .AddNuspecItem(_baseFixture.BuildLibrary("test1")) .AddSnapDll(); using var genesisPackageContext = await _baseFixture.BuildPackageAsync(genesisSnapReleaseBuilder); var anyOs = SnapOs.AnyOs; Assert.NotNull(anyOs); var loggerMock = new Mock <ILog>(); var progressSource = new Mock <ISnapProgressSource>(); progressSource. Setup(x => x.Raise(It.IsAny <int>())); var failedRunAsyncReturnValues = new List <(int exitCode, string stdOut)>(); var snapOsProcessManager = new Mock <ISnapOsProcessManager>(); snapOsProcessManager .Setup(x => x.RunAsync(It.IsAny <ProcessStartInfoBuilder>(), It.IsAny <CancellationToken>())) .ReturnsAsync((ProcessStartInfoBuilder builder, CancellationToken cancellationToken) => { var result = _snapOsProcessManager.RunAsync(builder, cancellationToken).GetAwaiter().GetResult(); if (result.exitCode != 0) { failedRunAsyncReturnValues.Add(result); } return(result); }); snapOsProcessManager .Setup(x => x.StartNonBlocking(It.IsAny <ProcessStartInfoBuilder>())) .Returns((ProcessStartInfoBuilder builder) => _snapOsProcessManager.StartNonBlocking(builder)); snapOsProcessManager .Setup(x => x.ChmodExecuteAsync(It.IsAny <string>(), It.IsAny <CancellationToken>())) .Returns((string filename, CancellationToken cancellationToken) => _snapOsProcessManager.ChmodExecuteAsync(filename, cancellationToken)); _snapOsMock .Setup(x => x.Filesystem) .Returns(_snapFilesystem); _snapOsMock .Setup(x => x.ProcessManager) .Returns(snapOsProcessManager.Object); _snapOsMock .Setup(x => x.CreateShortcutsForExecutableAsync( It.IsAny <SnapOsShortcutDescription>(), It.IsAny <ILog>(), It.IsAny <CancellationToken>())) .Returns(Task.CompletedTask); using var baseDirectory = _baseFixture.WithDisposableTempDirectory(_snapFilesystem); using var installCts = new CancellationTokenSource(); var snapCurrentChannel = genesisPackageContext.FullPackageSnapApp.GetCurrentChannelOrThrow(); await _snapInstaller.InstallAsync( genesisPackageContext.FullPackageAbsolutePath, baseDirectory.WorkingDirectory, genesisPackageContext.FullPackageSnapRelease, snapCurrentChannel, progressSource.Object, loggerMock.Object, installCts.Token); var appDirectory = _snapFilesystem.PathCombine(baseDirectory.WorkingDirectory, $"app-{genesisPackageContext.FullPackageSnapApp.Version}"); var snapAppUpdated = appDirectory.GetSnapAppFromDirectory(_snapFilesystem, _snapAppReader); var snapAppUpdatedChannel = snapAppUpdated.GetCurrentChannelOrThrow(); Assert.Equal(snapCurrentChannel.Name, snapAppUpdatedChannel.Name); #if !PLATFORM_WINDOWS || NETFULLFRAMEWORK // TODO: ENABLE ME! Unable to run a dotnet 2.2 (shared) executable on Windows. Assert.Empty(failedRunAsyncReturnValues); #endif var coreRunExe = _snapFilesystem.PathCombine(baseDirectory.WorkingDirectory, _snapEmbeddedResources.GetCoreRunExeFilenameForSnapApp(genesisPackageContext.FullPackageSnapApp)); var appExe = _snapFilesystem.PathCombine(baseDirectory.WorkingDirectory, $"app-{genesisSnapReleaseBuilder.SnapApp.Version}", genesisSnapReleaseBuilder.CoreRunExe); var snapInstalledArguments = $"--snapx-installed {genesisPackageContext.FullPackageSnapApp.Version.ToNormalizedString()}"; var snapFirstRunArguments = $"--snapx-first-run {genesisPackageContext.FullPackageSnapApp.Version.ToNormalizedString()}"; progressSource.Verify(x => x.Raise(It.Is <int>(v => v == 100)), Times.Once); if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { snapOsProcessManager.Verify(x => x.ChmodExecuteAsync( It.IsAny <string>(), It.IsAny <CancellationToken>()), Times.Exactly(2)); snapOsProcessManager.Verify(x => x.ChmodExecuteAsync( It.Is <string>(v => v == coreRunExe), It.Is <CancellationToken>(v => v == installCts.Token)), Times.Once); snapOsProcessManager.Verify(x => x.ChmodExecuteAsync( It.Is <string>(v => v == appExe), It.Is <CancellationToken>(v => v == installCts.Token)), Times.Once); } _snapOsMock.Verify(x => x.KillAllProcessesInsideDirectory( It.Is <string>(v => v == baseDirectory.WorkingDirectory)), Times.Once); _snapOsMock.Verify(x => x.CreateShortcutsForExecutableAsync( It.IsAny <SnapOsShortcutDescription>(), It.IsAny <ILog>(), It.IsAny <CancellationToken>()), Times.Once); _snapOsMock.Verify(x => x.CreateShortcutsForExecutableAsync( It.Is <SnapOsShortcutDescription>(v => v.ExeAbsolutePath == coreRunExe), It.Is <ILog>(v => v != null), It.Is <CancellationToken>(v => v == installCts.Token)), Times.Once); snapOsProcessManager.Verify(x => x.RunAsync( It.IsAny <ProcessStartInfoBuilder>(), It.IsAny <CancellationToken>()), Times.Once); snapOsProcessManager.Verify(x => x.RunAsync( It.Is <ProcessStartInfoBuilder>(v => v.Filename == appExe && v.Arguments == snapInstalledArguments), It.Is <CancellationToken>(v => v == installCts.Token)), Times.Once); snapOsProcessManager.Verify(x => x.StartNonBlocking( It.Is <ProcessStartInfoBuilder>(v => v.Filename == appExe & v.Arguments == snapFirstRunArguments)), Times.Once); snapOsProcessManager.Verify(x => x.StartNonBlocking( It.IsAny <ProcessStartInfoBuilder>()), Times.Once); }
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); }