Example #1
0
        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));
        }
Example #2
0
        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);
        }
Example #3
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));
        }
Example #4
0
        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);
        }
Example #5
0
        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.");
            }));
        }
Example #6
0
        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);
        }
Example #7
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));
            }
        }
Example #9
0
        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);
        }
Example #10
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 #11
0
        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());
        }
Example #12
0
        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
            });
        }