public async Task TestDirectoryDeleteAsync_ExcludePaths() { await using var tmpDir = _baseFixture.WithDisposableTempDirectory(_snapFilesystem); var rootDirectory = _snapFilesystem.PathCombine(tmpDir.WorkingDirectory, "rootDirectory"); _snapFilesystem.DirectoryCreate(rootDirectory); var excludeDirectory = _snapFilesystem.PathCombine(rootDirectory, "excludeDirectory"); _snapFilesystem.DirectoryCreate(excludeDirectory); var deleteThisDirectory = _snapFilesystem.PathCombine(rootDirectory, "deleteThisDirectory"); _snapFilesystem.DirectoryCreate(deleteThisDirectory); var excludeFile = _snapFilesystem.PathCombine(rootDirectory, "excludeFile.txt"); await _snapFilesystem.FileWriteUtf8StringAsync("yolo", excludeFile, default); var deleteThisFile = _snapFilesystem.PathCombine(rootDirectory, "deleteThisFile.txt"); await _snapFilesystem.FileWriteUtf8StringAsync("yolo2", excludeFile, default); await _snapFilesystem.DirectoryDeleteAsync(rootDirectory, new List <string> { excludeDirectory, excludeFile }); Assert.True(_snapFilesystem.DirectoryExists(rootDirectory)); Assert.True(_snapFilesystem.DirectoryExists(excludeDirectory)); Assert.True(_snapFilesystem.FileExists(excludeFile)); // Delete Assert.False(_snapFilesystem.FileExists(deleteThisFile)); Assert.False(_snapFilesystem.DirectoryExists(deleteThisDirectory)); }
public async Task TestExtractCoreRunLibAsync(string osPlatformStr) { var osPlatform = OSPlatform.Create(osPlatformStr); string expectedDllFilename; if (osPlatform == OSPlatform.Windows) { expectedDllFilename = "libcorerun-" + ( RuntimeInformation.ProcessArchitecture == Architecture.X86 ? "win-x86" : "win-x64") + ".dll"; } else if (osPlatform == OSPlatform.Linux) { expectedDllFilename = RuntimeInformation.ProcessArchitecture == Architecture.X64 ? "libcorerun-linux-x64.so" : "libcorerun-linux-arm64.so"; } else { throw new PlatformNotSupportedException(); } await using var tempDir = _baseFixture.WithDisposableTempDirectory(_snapFilesystem); var expectedDllFilenameAbsolute = _snapFilesystem.PathCombine(tempDir.WorkingDirectory, expectedDllFilename); await _snapEmbeddedResources.ExtractCoreRunLibAsync(_snapFilesystem, _snapCryptoProvider, tempDir.WorkingDirectory, osPlatform); Assert.True(_snapFilesystem.FileExists(expectedDllFilenameAbsolute)); Assert.True(_snapFilesystem.FileStat(expectedDllFilenameAbsolute).Length > 0); }
internal static SnapApp GetSnapAppFromDirectory([NotNull] this string workingDirectory, [NotNull] ISnapFilesystem filesystem, [NotNull] ISnapAppReader snapAppReader) { if (workingDirectory == null) { throw new ArgumentNullException(nameof(workingDirectory)); } if (filesystem == null) { throw new ArgumentNullException(nameof(filesystem)); } if (snapAppReader == null) { throw new ArgumentNullException(nameof(snapAppReader)); } var snapAppDll = filesystem.PathCombine(workingDirectory, SnapConstants.SnapAppDllFilename); if (!filesystem.FileExists(snapAppDll)) { throw new FileNotFoundException(snapAppDll); } using var snapAppDllFileStream = new FileStream(snapAppDll, FileMode.Open, FileAccess.Read, FileShare.Read); using var snapAppDllAssemblyDefinition = AssemblyDefinition.ReadAssembly(snapAppDllFileStream); return(snapAppDllAssemblyDefinition.GetSnapApp(snapAppReader)); }
public async Task TestDeletePackage_Local_Directory_PackageSource(NuGetProtocolVersion protocolVersion) { await using var deletePackageSrcDirectory = new DisposableDirectory(_baseFixture.WorkingDirectory, _snapFilesystem); var packageIdentity = new PackageIdentity("test", NuGetVersion.Parse("1.0.0")); var packageFilenameAbsolute = _snapFilesystem.PathCombine(deletePackageSrcDirectory, $"{packageIdentity.Id}.{packageIdentity.Version.ToNormalizedString()}.nupkg"); await using (var packageOutputStream = _snapFilesystem.FileReadWrite(packageFilenameAbsolute)) { await using var nupkgStream = BuildNupkg(packageIdentity); await nupkgStream.CopyToAsync(packageOutputStream); } var packageSource = new PackageSource(deletePackageSrcDirectory, "test", true) { ProtocolVersion = (int)protocolVersion }; var packageSources = new NuGetInMemoryPackageSources(deletePackageSrcDirectory, packageSource); await _nugetService.DeleteAsync(packageIdentity, packageSources, packageSource); Assert.False(_snapFilesystem.FileExists(packageFilenameAbsolute)); Assert.Equal(new Uri(deletePackageSrcDirectory), packageSource.SourceUri); }
static Task PushPackageAsync([NotNull] INugetService nugetService, [NotNull] ISnapFilesystem filesystem, [NotNull] IDistributedMutex distributedMutex, [NotNull] INuGetPackageSources nugetSources, [NotNull] PackageSource packageSource, SnapChannel channel, [NotNull] string packageAbsolutePath, CancellationToken cancellationToken, [NotNull] ILog logger) { if (nugetService == null) { throw new ArgumentNullException(nameof(nugetService)); } if (filesystem == null) { throw new ArgumentNullException(nameof(filesystem)); } if (distributedMutex == null) { throw new ArgumentNullException(nameof(distributedMutex)); } if (nugetSources == null) { throw new ArgumentNullException(nameof(nugetSources)); } if (packageSource == null) { throw new ArgumentNullException(nameof(packageSource)); } if (packageAbsolutePath == null) { throw new ArgumentNullException(nameof(packageAbsolutePath)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (!filesystem.FileExists(packageAbsolutePath)) { throw new FileNotFoundException(packageAbsolutePath); } var packageName = filesystem.PathGetFileName(packageAbsolutePath); return(SnapUtility.RetryAsync(async() => { if (!distributedMutex.Acquired) { throw new Exception("Distributed mutex has expired. This is most likely due to intermittent internet connection issues " + "or another user is attempting to publish a new version. Please retry pack operation."); } logger.Info($"Pushing {packageName} to channel {channel.Name} using package source {packageSource.Name}"); var pushStopwatch = new Stopwatch(); pushStopwatch.Restart(); await nugetService.PushAsync(packageAbsolutePath, nugetSources, packageSource, null, cancellationToken: cancellationToken); logger.Info($"Pushed {packageName} to channel {channel.Name} using package source {packageSource.Name} in {pushStopwatch.Elapsed.TotalSeconds:0.0}s."); })); }
public async Task TestExtractCoreRunLibAsync(string osPlatformStr, string expectedDllFilename) { var osPlatform = OSPlatform.Create(osPlatformStr); using var tempDir = _baseFixture.WithDisposableTempDirectory(_snapFilesystem); var expectedDllFilenameAbsolute = _snapFilesystem.PathCombine(tempDir.WorkingDirectory, expectedDllFilename); await _snapEmbeddedResources.ExtractCoreRunLibAsync(_snapFilesystem, _snapCryptoProvider, tempDir.WorkingDirectory, osPlatform); Assert.True(_snapFilesystem.FileExists(expectedDllFilenameAbsolute)); Assert.True(_snapFilesystem.FileStat(expectedDllFilenameAbsolute).Length > 0); }
static int CommandSha256([NotNull] Sha256Options sha256Options, [NotNull] ISnapFilesystem snapFilesystem, [NotNull] ISnapCryptoProvider snapCryptoProvider, [NotNull] ILog logger) { if (sha256Options == null) { throw new ArgumentNullException(nameof(sha256Options)); } if (snapFilesystem == null) { throw new ArgumentNullException(nameof(snapFilesystem)); } if (snapCryptoProvider == null) { throw new ArgumentNullException(nameof(snapCryptoProvider)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (sha256Options.Filename == null || !snapFilesystem.FileExists(sha256Options.Filename)) { logger.Error($"File not found: {sha256Options.Filename}"); return(1); } try { using var fileStream = new FileStream(sha256Options.Filename, FileMode.Open, FileAccess.Read); logger.Info(snapCryptoProvider.Sha256(fileStream)); return(0); } catch (Exception e) { logger.ErrorException($"Error computing SHA256-checksum for filename: {sha256Options.Filename}", e); return(1); } }
public async Task TestRestoreAsync_Downloads_Genesis_Delta_Consecutive_And_Reassembles_Full_From_Newest_Delta_Only() { var snapAppsReleases = new SnapAppsReleases(); var genesisSnapApp = _baseFixturePackaging.BuildSnapApp(); var update1SnapApp = _baseFixturePackaging.Bump(genesisSnapApp); var update2SnapApp = _baseFixturePackaging.Bump(update1SnapApp); var snapAppChannel = genesisSnapApp.GetDefaultChannelOrThrow(); using var rootDirectory = new DisposableDirectory(_baseFixturePackaging.WorkingDirectory, _snapFilesystem); using var restoreDirectory = new DisposableDirectory(_baseFixturePackaging.WorkingDirectory, _snapFilesystem); using var nugetPackageSourcesDirectory = _snapFilesystem.WithDisposableTempDirectory(_baseFixturePackaging.WorkingDirectory); using var genesisReleaseBuilder = _baseFixturePackaging.WithSnapReleaseBuilder(rootDirectory, snapAppsReleases, genesisSnapApp, _releaseBuilderContext); using var update1ReleaseBuilder = _baseFixturePackaging.WithSnapReleaseBuilder(rootDirectory, snapAppsReleases, update1SnapApp, _releaseBuilderContext); using var update2ReleaseBuilder = _baseFixturePackaging.WithSnapReleaseBuilder(rootDirectory, snapAppsReleases, update2SnapApp, _releaseBuilderContext); var packagesDirectory = _snapFilesystem.PathCombine(restoreDirectory.WorkingDirectory, "packages"); var nugetPackageSources = genesisSnapApp.BuildNugetSources(nugetPackageSourcesDirectory.WorkingDirectory); var packageSource = nugetPackageSources.Items.Single(); genesisReleaseBuilder .AddNuspecItem(_baseFixturePackaging.BuildSnapExecutable(genesisSnapApp)) .AddSnapDll(); update1ReleaseBuilder .AddNuspecItem(_baseFixturePackaging.BuildSnapExecutable(update1SnapApp)) .AddSnapDll(); update2ReleaseBuilder .AddNuspecItem(_baseFixturePackaging.BuildSnapExecutable(update2SnapApp)) .AddSnapDll(); using var genesisPackageContext = await _baseFixturePackaging.BuildPackageAsync(genesisReleaseBuilder); using var update1PackageContext = await _baseFixturePackaging.BuildPackageAsync(update1ReleaseBuilder); using var update2PackageContext = await _baseFixturePackaging.BuildPackageAsync(update2ReleaseBuilder); _baseFixtureNuget.SetupDownloadAsyncWithProgressAsync(_nugetServiceMock, genesisPackageContext.FullPackageSnapApp, genesisPackageContext.FullPackageMemoryStream, nugetPackageSources ); _baseFixtureNuget.SetupDownloadAsyncWithProgressAsync(_nugetServiceMock, update1PackageContext.DeltaPackageSnapApp, update1PackageContext.DeltaPackageMemoryStream, nugetPackageSources ); _baseFixtureNuget.SetupDownloadAsyncWithProgressAsync(_nugetServiceMock, update2PackageContext.DeltaPackageSnapApp, update2PackageContext.DeltaPackageMemoryStream, nugetPackageSources ); var snapAppChannelReleases = snapAppsReleases.GetReleases(genesisSnapApp, snapAppChannel); var progressSourceMock = new Mock <ISnapPackageManagerProgressSource>(); progressSourceMock.Setup(x => x.RaiseChecksumProgress( It.IsAny <int>(), It.IsAny <long>(), It.IsAny <long>(), It.IsAny <long>())); progressSourceMock.Setup(x => x.RaiseDownloadProgress( It.IsAny <int>(), It.IsAny <long>(), It.IsAny <long>(), It.IsAny <long>(), It.IsAny <long>())); progressSourceMock.Setup(x => x.RaiseRestoreProgress( It.IsAny <int>(), It.IsAny <long>(), It.IsAny <long>())); var restoreSummary = await _snapPackageManager.RestoreAsync(packagesDirectory, snapAppChannelReleases, packageSource, SnapPackageManagerRestoreType.Default, progressSourceMock.Object); progressSourceMock.Verify(x => x.RaiseChecksumProgress( It.Is <int>(v => v == 0), It.Is <long>(v => v == 0), It.Is <long>(v => v == 0), It.Is <long>(v => v == 4)), Times.Once); progressSourceMock.Verify(x => x.RaiseChecksumProgress( It.Is <int>(v => v == 100), It.Is <long>(v => v == 0), It.Is <long>(v => v == 4), It.Is <long>(v => v == 4)), Times.Once); var totalBytesToDownload = genesisPackageContext.FullPackageSnapRelease.FullFilesize + update1PackageContext.DeltaPackageSnapRelease.DeltaFilesize + update2PackageContext.DeltaPackageSnapRelease.DeltaFilesize; progressSourceMock.Verify(x => x.RaiseDownloadProgress( It.Is <int>(v => v == 0), It.Is <long>(v => v == 0), It.Is <long>(v => v == 3), It.Is <long>(v => v == 0), It.Is <long>(v => v == totalBytesToDownload))); progressSourceMock.Verify(x => x.RaiseRestoreProgress( It.Is <int>(v => v == 0), It.Is <long>(v => v == 0), It.Is <long>(v => v == 8)), Times.Once); progressSourceMock.Verify(x => x.RaiseRestoreProgress( It.Is <int>(v => v == 100), It.Is <long>(v => v == 8), It.Is <long>(v => v == 8)), Times.Once); Assert.Equal(SnapPackageManagerRestoreType.Default, restoreSummary.RestoreType); Assert.Equal(4, restoreSummary.ChecksumSummary.Count); Assert.False(restoreSummary.ChecksumSummary[0].Ok); Assert.Equal(genesisPackageContext.FullPackageSnapRelease.Filename, restoreSummary.ChecksumSummary[0].SnapRelease.Filename); Assert.False(restoreSummary.ChecksumSummary[1].Ok); Assert.Equal(update1PackageContext.DeltaPackageSnapRelease.Filename, restoreSummary.ChecksumSummary[1].SnapRelease.Filename); Assert.False(restoreSummary.ChecksumSummary[2].Ok); Assert.Equal(update2PackageContext.DeltaPackageSnapRelease.Filename, restoreSummary.ChecksumSummary[2].SnapRelease.Filename); Assert.False(restoreSummary.ChecksumSummary[3].Ok); Assert.Equal(update2PackageContext.FullPackageSnapRelease.Filename, restoreSummary.ChecksumSummary[3].SnapRelease.Filename); Assert.Equal(3, restoreSummary.DownloadSummary.Count); Assert.True(restoreSummary.DownloadSummary[0].Ok); Assert.Equal(genesisPackageContext.FullPackageSnapRelease.Filename, restoreSummary.DownloadSummary[0].SnapRelease.Filename); Assert.True(restoreSummary.DownloadSummary[1].Ok); Assert.Equal(update1PackageContext.DeltaPackageSnapRelease.Filename, restoreSummary.DownloadSummary[1].SnapRelease.Filename); Assert.True(restoreSummary.DownloadSummary[2].Ok); Assert.Equal(update2PackageContext.DeltaPackageSnapRelease.Filename, restoreSummary.DownloadSummary[2].SnapRelease.Filename); _nugetServiceMock.Verify(x => x.DownloadAsyncWithProgressAsync( It.IsAny <PackageSource>(), It.Is <DownloadContext>(v => v.PackageIdentity.Equals(genesisPackageContext.FullPackageSnapRelease.BuildPackageIdentity())), It.IsAny <INugetServiceProgressSource>(), It.IsAny <CancellationToken>()), Times.Once); _nugetServiceMock.Verify(x => x.DownloadAsyncWithProgressAsync( It.IsAny <PackageSource>(), It.Is <DownloadContext>(v => v.PackageIdentity.Equals(update1PackageContext.DeltaPackageSnapRelease.BuildPackageIdentity())), It.IsAny <INugetServiceProgressSource>(), It.IsAny <CancellationToken>()), Times.Once); _nugetServiceMock.Verify(x => x.DownloadAsyncWithProgressAsync( It.IsAny <PackageSource>(), It.Is <DownloadContext>(v => v.PackageIdentity.Equals(update2PackageContext.DeltaPackageSnapRelease.BuildPackageIdentity())), It.IsAny <INugetServiceProgressSource>(), It.IsAny <CancellationToken>()), Times.Once); Assert.Single(restoreSummary.ReassembleSummary); Assert.True(restoreSummary.ReassembleSummary[0].Ok); Assert.Equal(update2PackageContext.FullPackageSnapRelease.Filename, restoreSummary.ReassembleSummary[0].SnapRelease.Filename); Assert.True(restoreSummary.Success); var genesisPackageAbsolutePath = _snapFilesystem.PathCombine(packagesDirectory, restoreSummary.DownloadSummary[0].SnapRelease.Filename); var update1FullPackageAbsolutePath = _snapFilesystem.PathCombine(packagesDirectory, update1PackageContext.FullPackageSnapRelease.Filename); var update1DeltaPackageAbsolutePath = _snapFilesystem.PathCombine(packagesDirectory, restoreSummary.DownloadSummary[1].SnapRelease.Filename); var update2FullPackageAbsolutePath = _snapFilesystem.PathCombine(packagesDirectory, restoreSummary.ReassembleSummary[0].SnapRelease.Filename); var update2DeltaPackageAbsolutePath = _snapFilesystem.PathCombine(packagesDirectory, restoreSummary.DownloadSummary[2].SnapRelease.Filename); using (var packageArchiveReader = new PackageArchiveReader(genesisPackageAbsolutePath)) { Assert.Equal(genesisPackageContext.FullPackageSnapRelease.BuildPackageIdentity(), packageArchiveReader.GetIdentity()); Assert.Equal(genesisPackageContext.FullPackageSnapRelease.FullSha256Checksum, _snapCryptoProvider.Sha256(genesisPackageContext.FullPackageSnapRelease, packageArchiveReader, _snapPack)); } Assert.False(_snapFilesystem.FileExists(update1FullPackageAbsolutePath)); using (var packageArchiveReader = new PackageArchiveReader(update1DeltaPackageAbsolutePath)) { Assert.Equal(update1PackageContext.DeltaPackageSnapRelease.BuildPackageIdentity(), packageArchiveReader.GetIdentity()); Assert.Equal(update1PackageContext.DeltaPackageSnapRelease.DeltaSha256Checksum, _snapCryptoProvider.Sha256(update1PackageContext.DeltaPackageSnapRelease, packageArchiveReader, _snapPack)); } using (var packageArchiveReader = new PackageArchiveReader(update2FullPackageAbsolutePath)) { Assert.Equal(update2PackageContext.FullPackageSnapRelease.BuildPackageIdentity(), packageArchiveReader.GetIdentity()); Assert.Equal(update2PackageContext.FullPackageSnapRelease.FullSha256Checksum, _snapCryptoProvider.Sha256(update2PackageContext.FullPackageSnapRelease, packageArchiveReader, _snapPack)); } using (var packageArchiveReader = new PackageArchiveReader(update2DeltaPackageAbsolutePath)) { Assert.Equal(update2PackageContext.DeltaPackageSnapRelease.BuildPackageIdentity(), packageArchiveReader.GetIdentity()); Assert.Equal(update2PackageContext.DeltaPackageSnapRelease.DeltaSha256Checksum, _snapCryptoProvider.Sha256(update2PackageContext.DeltaPackageSnapRelease, packageArchiveReader, _snapPack)); } }
static int Install([NotNull] ISnapInstallerEnvironment environment, [NotNull] ISnapInstallerEmbeddedResources snapInstallerEmbeddedResources, [NotNull] ISnapInstaller snapInstaller, [NotNull] ISnapFilesystem snapFilesystem, [NotNull] ISnapPack snapPack, [NotNull] ISnapOs snapOs, [NotNull] CoreRunLib coreRunLib, [NotNull] ISnapAppReader snapAppReader, [NotNull] ISnapAppWriter snapAppWriter, [NotNull] INugetService nugetService, [NotNull] ISnapPackageManager snapPackageManager, [NotNull] ISnapExtractor snapExtractor, [NotNull] ILog diskLogger, bool headless) { if (environment == null) { throw new ArgumentNullException(nameof(environment)); } if (snapInstallerEmbeddedResources == null) { throw new ArgumentNullException(nameof(snapInstallerEmbeddedResources)); } if (snapInstaller == null) { throw new ArgumentNullException(nameof(snapInstaller)); } if (snapFilesystem == null) { throw new ArgumentNullException(nameof(snapFilesystem)); } if (snapPack == null) { throw new ArgumentNullException(nameof(snapPack)); } if (snapOs == null) { throw new ArgumentNullException(nameof(snapOs)); } if (coreRunLib == null) { throw new ArgumentNullException(nameof(coreRunLib)); } if (snapAppReader == null) { throw new ArgumentNullException(nameof(snapAppReader)); } if (snapAppWriter == null) { throw new ArgumentNullException(nameof(snapAppWriter)); } if (nugetService == null) { throw new ArgumentNullException(nameof(nugetService)); } if (snapPackageManager == null) { throw new ArgumentNullException(nameof(snapPackageManager)); } if (snapExtractor == null) { throw new ArgumentNullException(nameof(snapExtractor)); } if (diskLogger == null) { throw new ArgumentNullException(nameof(diskLogger)); } // NB! All filesystem operations has to be readonly until check that verifies // current user is not elevated to root has run. var cancellationToken = environment.CancellationToken; var installerProgressSource = new SnapProgressSource(); var onFirstAnimationRenderedEvent = new ManualResetEventSlim(false); var exitCode = 1; // ReSharper disable once ImplicitlyCapturedClosure async Task InstallInBackgroundAsync(IMainWindowViewModel mainWindowViewModel) { if (mainWindowViewModel == null) { throw new ArgumentNullException(nameof(mainWindowViewModel)); } if (mainWindowViewModel.Headless) { diskLogger.Info("Headless install."); onFirstAnimationRenderedEvent.Dispose(); } else { diskLogger.Info("Waiting for main window to become visible."); onFirstAnimationRenderedEvent.Wait(cancellationToken); onFirstAnimationRenderedEvent.Dispose(); diskLogger.Info("Main window should now be visible."); } var mainWindowLogger = new LogForwarder(LogLevel.Info, diskLogger, (level, func, exception, parameters) => { var message = func?.Invoke(); if (message == null) { return; } SetStatusText(mainWindowViewModel, message); }); if (coreRunLib.IsElevated()) { var rootUserText = snapOs.OsPlatform == OSPlatform.Windows ? "Administrator" : "root"; mainWindowLogger.Error($"Error! Installer cannot run in an elevated user context: {rootUserText}"); goto done; } diskLogger.Debug($"{nameof(environment.Io.WorkingDirectory)}: {environment.Io.WorkingDirectory}"); diskLogger.Debug($"{nameof(environment.Io.ThisExeWorkingDirectory)}: {environment.Io.ThisExeWorkingDirectory}"); var snapAppDllAbsolutePath = snapFilesystem.PathCombine(environment.Io.ThisExeWorkingDirectory, SnapConstants.SnapAppDllFilename); diskLogger.Debug($"{nameof(snapAppDllAbsolutePath)}: {snapAppDllAbsolutePath}."); if (!snapFilesystem.FileExists(snapAppDllAbsolutePath)) { mainWindowLogger.Info($"Unable to find: {snapFilesystem.PathGetFileName(snapAppDllAbsolutePath)}"); goto done; } SnapApp snapApp; SnapChannel snapChannel; try { snapApp = environment.Io.ThisExeWorkingDirectory.GetSnapAppFromDirectory(snapFilesystem, snapAppReader); snapChannel = snapApp.GetCurrentChannelOrThrow(); } catch (Exception ex) { mainWindowLogger.ErrorException($"Error reading {SnapConstants.SnapAppDllFilename}", ex); goto done; } var nupkgAbsolutePath = snapFilesystem.PathCombine(environment.Io.ThisExeWorkingDirectory, "Setup.nupkg"); var nupkgReleasesAbsolutePath = snapFilesystem.PathCombine(environment.Io.ThisExeWorkingDirectory, snapApp.BuildNugetReleasesFilename()); var offlineInstaller = false; using (var webInstallerDir = new DisposableDirectory(snapOs.SpecialFolders.NugetCacheDirectory, snapFilesystem)) { ISnapAppChannelReleases snapAppChannelReleases; SnapRelease snapReleaseToInstall; // Offline installer if (snapFilesystem.FileExists(nupkgAbsolutePath)) { mainWindowLogger.Info("Offline installer is loading releases nupkg"); try { var releasesFileStream = snapFilesystem.FileRead(nupkgReleasesAbsolutePath); using var packageArchiveReader = new PackageArchiveReader(releasesFileStream); var snapAppsReleases = await snapExtractor.GetSnapAppsReleasesAsync(packageArchiveReader, snapAppReader, cancellationToken); snapAppChannelReleases = snapAppsReleases.GetReleases(snapApp, snapChannel); var isGenesis = !snapAppChannelReleases.HasDeltaReleases(); snapReleaseToInstall = snapAppChannelReleases.GetMostRecentRelease().AsFullRelease(isGenesis); } catch (Exception e) { mainWindowLogger.ErrorException($"Error reading {nupkgAbsolutePath}", e); goto done; } offlineInstaller = true; } // Web installer else if (snapFilesystem.FileExists(snapAppDllAbsolutePath)) { mainWindowLogger.Info("Web installer is downloading releases nupkg"); try { var(snapAppsReleases, packageSource, releasesMemoryStream) = await snapPackageManager.GetSnapsReleasesAsync(snapApp, mainWindowLogger, cancellationToken); releasesMemoryStream?.Dispose(); if (snapAppsReleases == null) { mainWindowLogger.Error("Failed to download releases nupkg. Try rerunning the installer."); goto done; } var snapAppsReleasesBytes = snapAppWriter.ToSnapAppsReleases(snapAppsReleases); await snapFilesystem.FileWriteAsync(snapAppsReleasesBytes, nupkgReleasesAbsolutePath, cancellationToken); snapAppChannelReleases = snapAppsReleases.GetReleases(snapApp, snapApp.GetCurrentChannelOrThrow()); if (!snapAppChannelReleases.Any()) { mainWindowLogger.Error($"Unable to find any releases in channel: {snapAppChannelReleases.Channel.Name}."); goto done; } var isGenesis = snapAppChannelReleases.Count() == 1; snapReleaseToInstall = snapAppChannelReleases.GetMostRecentRelease().AsFullRelease(isGenesis); snapApp.Version = snapReleaseToInstall.Version; mainWindowLogger.Info($"Current version: {snapApp.Version}. Channel: {snapAppChannelReleases.Channel.Name}."); // ReSharper disable once MethodSupportsCancellation await Task.Delay(TimeSpan.FromSeconds(3)); mainWindowLogger.Info("Downloading required assets"); void UpdateProgress(string type, int totalPercentage, long releasesChecksummed = 0, long releasesToChecksum = 0, long releasesDownloaded = 0, long releasesToDownload = 0, long filesRestored = 0, long filesToRestore = 0, long totalBytesDownloaded = 0, long totalBytesToDownload = 0) { void SetProgressText(long current, long total, string defaultText, string pluralText) { var outputText = total > 1 ? pluralText : defaultText; switch (type) { case "Download": if (total > 1) { SetStatusText(mainWindowViewModel, $"{outputText} ({totalPercentage}%): {current} of {total}. " + $"Downloaded so far: {totalBytesDownloaded.BytesAsHumanReadable()}. " + $"Total: {totalBytesToDownload.BytesAsHumanReadable()}"); goto incrementProgress; } SetStatusText(mainWindowViewModel, $"{outputText} ({totalPercentage}%): " + $"Downloaded so far: {totalBytesDownloaded.BytesAsHumanReadable()}. " + $"Total: {totalBytesToDownload.BytesAsHumanReadable()}"); goto incrementProgress; default: if (total > 1) { SetStatusText(mainWindowViewModel, $"{outputText} ({totalPercentage}%): {current} of {total}."); goto incrementProgress; } SetStatusText(mainWindowViewModel, $"{outputText}: {totalPercentage}%"); goto incrementProgress; } incrementProgress: installerProgressSource.Raise(totalPercentage); } switch (type) { case "Checksum": SetProgressText(releasesChecksummed, releasesToChecksum, "Validating payload", "Validating payloads"); break; case "Download": SetProgressText(releasesDownloaded, releasesToDownload, "Downloading payload", "Downloading payloads"); break; case "Restore": SetProgressText(filesRestored, filesToRestore, "Restoring file", "Restoring files"); break; default: diskLogger.Warn($"Unknown progress type: {type}"); break; } } var snapPackageManagerProgressSource = new SnapPackageManagerProgressSource { ChecksumProgress = x => UpdateProgress("Checksum", x.progressPercentage, x.releasesChecksummed, x.releasesToChecksum), DownloadProgress = x => UpdateProgress("Download", x.progressPercentage, releasesDownloaded: x.releasesDownloaded, releasesToDownload: x.releasesToDownload, totalBytesDownloaded: x.totalBytesDownloaded, totalBytesToDownload: x.totalBytesToDownload), RestoreProgress = x => UpdateProgress("Restore", x.progressPercentage, filesRestored: x.filesRestored, filesToRestore: x.filesToRestore) }; var restoreSummary = await snapPackageManager.RestoreAsync(webInstallerDir.WorkingDirectory, snapAppChannelReleases, packageSource, SnapPackageManagerRestoreType.Default, snapPackageManagerProgressSource, diskLogger, cancellationToken); if (!restoreSummary.Success) { mainWindowLogger.Info("Unknown error while restoring assets."); goto done; } mainWindowLogger.Info("Preparing to install payload"); var setupNupkgAbsolutePath = snapOs.Filesystem.PathCombine(webInstallerDir.WorkingDirectory, snapReleaseToInstall.Filename); if (!snapFilesystem.FileExists(setupNupkgAbsolutePath)) { mainWindowLogger.Error($"Payload does not exist on disk: {setupNupkgAbsolutePath}."); goto done; } nupkgAbsolutePath = snapFilesystem.PathCombine(environment.Io.ThisExeWorkingDirectory, "Setup.nupkg"); mainWindowLogger.Info("Copying payload to installer directory"); snapOs.Filesystem.FileDeleteIfExists(nupkgAbsolutePath); await snapOs.Filesystem.FileCopyAsync(setupNupkgAbsolutePath, nupkgAbsolutePath, cancellationToken); mainWindowLogger.Info("Successfully copied payload"); installerProgressSource.Reset(); } catch (Exception e) { mainWindowLogger.ErrorException("Unknown error while restoring assets", e); goto done; } } else { mainWindowLogger.Error("Unknown error. Could not find offline or web installer payload."); goto done; } diskLogger.Trace($"Offline installer: {offlineInstaller}"); diskLogger.Trace($"{nameof(nupkgAbsolutePath)}: {nupkgAbsolutePath}"); diskLogger.Trace($"{nameof(nupkgReleasesAbsolutePath)}: {nupkgReleasesAbsolutePath}"); if (!snapFilesystem.FileExists(nupkgAbsolutePath)) { mainWindowLogger.Error($"Unable to find installer payload: {snapFilesystem.PathGetFileName(nupkgAbsolutePath)}"); goto done; } mainWindowLogger.Info("Attempting to read payload release details"); if (!snapFilesystem.FileExists(nupkgReleasesAbsolutePath)) { mainWindowLogger.Error($"Unable to find releases nupkg: {snapFilesystem.PathGetFileName(nupkgReleasesAbsolutePath)}"); goto done; } var baseDirectory = snapFilesystem.PathCombine(snapOs.SpecialFolders.LocalApplicationData, snapApp.Id); mainWindowLogger.Info($"Installing {snapApp.Id}. Channel name: {snapChannel.Name}"); try { var snapAppInstalled = await snapInstaller.InstallAsync(nupkgAbsolutePath, baseDirectory, snapReleaseToInstall, snapChannel, installerProgressSource, mainWindowLogger, cancellationToken, offlineInstaller); if (snapAppInstalled == null) { goto done; } if (!offlineInstaller && snapAppChannelReleases.HasDeltaReleases()) { var snapReleasesToCopy = new List <SnapRelease> { snapAppChannelReleases.GetGenesisRelease() }; snapReleasesToCopy.AddRange(snapAppChannelReleases.GetDeltaReleases()); if (snapReleasesToCopy.Any()) { var totalSnapReleasesToCopyCount = snapReleasesToCopy.Count; mainWindowLogger.Info($"Copying 1 of {totalSnapReleasesToCopyCount} payloads to application directory."); var packagesDirectory = snapFilesystem.PathCombine(baseDirectory, "packages"); var snapReleasesCopied = 1; foreach (var snapRelease in snapReleasesToCopy) { var nupkgPackageWebInstallerDirectoryAbsolutePath = snapFilesystem.PathCombine( webInstallerDir.WorkingDirectory, snapRelease.Filename); var nupkgPackagePackagesDirectoryAbsolutePath = snapFilesystem.PathCombine( packagesDirectory, snapRelease.Filename); await snapFilesystem.FileCopyAsync( nupkgPackageWebInstallerDirectoryAbsolutePath, nupkgPackagePackagesDirectoryAbsolutePath, cancellationToken); mainWindowLogger.Info($"Copied {snapReleasesCopied} of {totalSnapReleasesToCopyCount} payloads to application directory."); ++snapReleasesCopied; } mainWindowLogger.Info("Successfully copied all payloads."); } snapFilesystem.FileDeleteIfExists(nupkgAbsolutePath); } mainWindowLogger.Info($"Successfully installed {snapApp.Id}."); exitCode = 0; } catch (Exception e) { mainWindowLogger.ErrorException("Unknown error during install", e); } } done: // Give user enough time to read final log message. Thread.Sleep(exitCode == 0 ? 3000 : 10000); environment.Shutdown(); } if (headless) { try { InstallInBackgroundAsync(new ConsoleMainViewModel()).Wait(cancellationToken); } catch (OperationCanceledException) { // ignore } return(exitCode); } var avaloniaApp = BuildAvaloniaApp <App>() .AfterSetup(builder => { MainWindow.Environment = environment; MainWindow.ViewModel = new AvaloniaMainWindowViewModel(snapInstallerEmbeddedResources, installerProgressSource, () => onFirstAnimationRenderedEvent.Set(), cancellationToken); Task.Factory.StartNew(() => InstallInBackgroundAsync(MainWindow.ViewModel), TaskCreationOptions.LongRunning); }); avaloniaApp.StartWithClassicDesktopLifetime(null); return(exitCode); }
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); }
static SnapApps BuildSnapAppsFromDirectory( [NotNull] ISnapFilesystem filesystem, [NotNull] ISnapAppReader reader, [NotNull] string workingDirectory) { if (filesystem == null) { throw new ArgumentNullException(nameof(filesystem)); } if (reader == null) { throw new ArgumentNullException(nameof(reader)); } if (workingDirectory == null) { throw new ArgumentNullException(nameof(workingDirectory)); } const string snapxYamlFilename = "snapx.yml"; var snapsFilename = filesystem.PathCombine(workingDirectory, ".snapx", snapxYamlFilename); if (!filesystem.FileExists(snapsFilename)) { SnapLogger.Error($"Unable to find application YML definition, it does not exist: {snapsFilename}"); goto error; } var content = filesystem.FileReadAllText(snapsFilename); if (string.IsNullOrWhiteSpace(content)) { SnapLogger.Error($"Application yml file is empty (does not containy any YML): {snapsFilename}"); goto error; } try { var snapApps = reader.BuildSnapAppsFromYamlString(content); if (snapApps == null) { goto error; } const int expectedSchemaVersion = 1; if (snapApps.Schema != expectedSchemaVersion) { throw new Exception($"Invalid schema version: {snapApps.Schema}. Expected schema version: {expectedSchemaVersion}."); } snapApps.Generic ??= new SnapAppsGeneric(); snapApps.Apps ??= new List <SnapsApp>(); snapApps.Channels ??= new List <SnapsChannel>(); return(snapApps); } catch (YamlException yamlException) { var moreHelpfulExceptionMaybe = yamlException.InnerException ?? yamlException; SnapLogger.ErrorException($"{snapxYamlFilename} file contains incorrect yaml syntax. Error message: {moreHelpfulExceptionMaybe.Message}.", moreHelpfulExceptionMaybe); } catch (Exception e) { SnapLogger.ErrorException($"Unknown error deserializing {snapxYamlFilename}", e); } error: return(new SnapApps()); }
internal static SnapApp BuildSnapApp([NotNull] this SnapApps snapApps, string id, [NotNull] string rid, [NotNull] INuGetPackageSources nuGetPackageSources, [NotNull] ISnapFilesystem snapFilesystem) { if (snapApps == null) { throw new ArgumentNullException(nameof(snapApps)); } if (rid == null) { throw new ArgumentNullException(nameof(rid)); } if (nuGetPackageSources == null) { throw new ArgumentNullException(nameof(nuGetPackageSources)); } if (snapFilesystem == null) { throw new ArgumentNullException(nameof(snapFilesystem)); } var snapApp = snapApps.Apps.SingleOrDefault(x => string.Equals(x.Id, id, StringComparison.OrdinalIgnoreCase)); if (snapApp == null) { throw new Exception($"Unable to find snap with id: {id}"); } if (!Guid.TryParse(snapApp.SuperVisorId, out var superVisorId)) { throw new Exception("Unable to parse supervisor id. Please use a unique guid to identify your application."); } if (superVisorId == Guid.Empty) { throw new Exception("Supervisor id cannot be an empty guid."); } var snapAppUniqueRuntimeIdentifiers = snapApp.Targets.Select(x => x.Rid).ToList(); if (snapAppUniqueRuntimeIdentifiers.Distinct().Count() != snapApp.Targets.Count) { throw new Exception($"Target runtime identifiers (rids) must be unique: {string.Join(",", snapAppUniqueRuntimeIdentifiers)}. Snap id: {snapApp.Id}"); } var snapAppTarget = snapApp.Targets.SingleOrDefault(x => string.Equals(x.Rid, rid, StringComparison.OrdinalIgnoreCase)); if (snapAppTarget == null) { throw new Exception($"Unable to find target with rid: {rid}. Snap id: {snapApp.Id}"); } snapAppTarget.Installers = snapAppTarget.Installers.Distinct().ToList(); if (!snapAppTarget.Rid.IsRuntimeIdentifierValidSafe()) { throw new Exception($"Unsupported rid: {rid}. Snap id: {snapApp.Id}"); } if (!snapAppTarget.Framework.IsNetFrameworkValidSafe()) { throw new Exception($"Unknown .NET framework: {snapAppTarget.Framework}"); } var snapAppTargetUniqueShortcuts = snapAppTarget.Shortcuts.Select(x => x).ToList(); if (snapAppTargetUniqueShortcuts.Distinct().Count() != snapAppTarget.Shortcuts.Count) { throw new Exception($"Target shortcut locations must be unique: {string.Join(", ", snapAppTargetUniqueShortcuts)}. Snap id: {snapApp.Id}"); } var snapAppTargetUniqueInstallers = snapAppTarget.Installers.Select(x => x).ToList(); if (snapAppTargetUniqueInstallers.Distinct().Count() != snapAppTarget.Installers.Count) { throw new Exception($"Target installer types must be unique: {string.Join(", ", snapAppTargetUniqueInstallers)}. Snap id: {snapApp.Id}"); } if (snapAppTarget.Icon != null) { snapAppTarget.Icon = snapFilesystem.PathGetFullPath(snapAppTarget.Icon); if (!snapFilesystem.FileExists(snapAppTarget.Icon)) { throw new Exception($"Unable to find icon: {snapAppTarget.Icon}."); } } var snapAppUniqueChannels = snapApp.Channels.Distinct().ToList(); if (snapAppUniqueChannels.Count != snapApp.Channels.Count) { throw new Exception($"Channel list must be unique: {string.Join(",", snapApp.Channels)}. Snap id: {snapApp.Id}"); } var snapAppsDefaultChannel = snapApps.Channels.First(); var snapAppDefaultChannel = snapApp.Channels.First(); if (!string.Equals(snapAppsDefaultChannel.Name, snapAppDefaultChannel, StringComparison.Ordinal)) { throw new Exception($"Default channel must be {snapAppsDefaultChannel.Name}. Snap id: {snapApp.Id}"); } var snapAppAvailableChannels = snapApps.Channels.Where(rhs => snapApp.Channels.Any(lhs => lhs.Equals(rhs.Name, StringComparison.OrdinalIgnoreCase))).ToList(); if (!snapAppAvailableChannels.Any()) { throw new Exception($"Could not find any global channels. Channel list: {string.Join(",", snapAppUniqueChannels)}. Snap id: {snapApp.Id}"); } var snapFeeds = new List <SnapFeed>(); snapFeeds.AddRange(nuGetPackageSources.BuildSnapFeeds()); snapFeeds.AddRange(snapApps.Channels.Select(x => x.UpdateFeed).OfType <SnapsHttpFeed>().DistinctBy(x => x.Source).Select(x => new SnapHttpFeed(x))); var snapNugetFeeds = snapFeeds.Where(x => x is SnapNugetFeed).Cast <SnapNugetFeed>().ToList(); var snapHttpFeeds = snapFeeds.Where(x => x is SnapHttpFeed).Cast <SnapHttpFeed>().ToList(); var snapAppChannels = new List <SnapChannel>(); for (var i = 0; i < snapAppAvailableChannels.Count; i++) { var snapsChannel = snapAppAvailableChannels[i]; var pushFeed = snapNugetFeeds.SingleOrDefault(x => x.Name == snapsChannel.PushFeed.Name); if (pushFeed == null) { throw new Exception($"Unable to resolve push feed: {snapsChannel.PushFeed.Name}. Channel: {snapsChannel.Name}. Application id: {snapApp.Id}"); } SnapFeed updateFeed = null; switch (snapsChannel.UpdateFeed) { case SnapsNugetFeed snapsNugetFeed: updateFeed = snapNugetFeeds.SingleOrDefault(x => x.Name == snapsNugetFeed.Name); break; case SnapsHttpFeed snapsHttpFeed: updateFeed = snapHttpFeeds.SingleOrDefault(x => x.Source == snapsHttpFeed.Source); break; } if (updateFeed == null) { throw new Exception($"Unable to resolve update feed type: {snapsChannel.UpdateFeed?.GetType().Name}. Channel: {snapsChannel.Name}. Application id: {snapApp.Id}"); } var currentChannel = i == 0; // Default snap channel is always the first one defined. snapAppChannels.Add(new SnapChannel(snapsChannel.Name, currentChannel, pushFeed, updateFeed)); } var snapChannelNugetFeedNames = snapAppChannels.Select(x => x.PushFeed.Name).Distinct().ToList(); if (snapChannelNugetFeedNames.Count > 1) { throw new Exception($"Multiple nuget push feeds is not supported: {string.Join(",", snapChannelNugetFeedNames)}. Application id: {snapApp.Id}"); } if (snapAppTarget.PersistentAssets.Any(x => x.StartsWith("app-", StringComparison.OrdinalIgnoreCase))) { throw new Exception("Fatal error! A persistent asset starting with 'app-' was detected in manifest. This is a reserved keyword."); } return(new SnapApp { Id = snapApp.Id, SuperVisorId = superVisorId.ToString(), Channels = snapAppChannels, Target = new SnapTarget(snapAppTarget), Authors = snapApp.Nuspec.Authors, Description = snapApp.Nuspec.Description, ReleaseNotes = snapApp.Nuspec.ReleaseNotes, RepositoryUrl = snapApp.Nuspec.RepositoryUrl, RepositoryType = snapApp.Nuspec.RepositoryType }); }