Esempio n. 1
0
        static string BuildInstallersDirectory([NotNull] ISnapFilesystem filesystem, [NotNull] string workingDirectory, [NotNull] SnapAppsGeneric snapAppsGeneric,
                                               [NotNull] SnapApp snapApp)
        {
            if (filesystem == null)
            {
                throw new ArgumentNullException(nameof(filesystem));
            }
            if (workingDirectory == null)
            {
                throw new ArgumentNullException(nameof(workingDirectory));
            }
            if (snapAppsGeneric == null)
            {
                throw new ArgumentNullException(nameof(snapAppsGeneric));
            }
            if (snapApp == null)
            {
                throw new ArgumentNullException(nameof(snapApp));
            }

            var properties = new Dictionary <string, string>
            {
                { "id", snapApp.Id },
                { "rid", snapApp.Target.Rid }
            };

            return(snapAppsGeneric.Installers == null?
                   filesystem.PathCombine(workingDirectory, ".snapx", "installers", "$id$/$rid$").ExpandProperties(properties) :
                       filesystem.PathCombine(workingDirectory, snapAppsGeneric.Artifacts.ExpandProperties(properties)));
        }
Esempio n. 2
0
        public async Task TestGetSnapAppFromDirectory()
        {
            var snapApp = _baseFixture.BuildSnapApp();

            await using var tmpDir       = _baseFixture.WithDisposableTempDirectory(_fileSystem);
            using var assemblyDefinition = _appWriter.BuildSnapAppAssembly(snapApp);
            var snapAppDllAbsolutePath = _fileSystem.PathCombine(tmpDir.WorkingDirectory, assemblyDefinition.BuildRelativeFilename());

            assemblyDefinition.Write(snapAppDllAbsolutePath);

            var appSpecAfter = tmpDir.WorkingDirectory.GetSnapAppFromDirectory(_fileSystem, _appReader);

            Assert.NotNull(appSpecAfter);
        }
Esempio n. 3
0
        public async Task <List <string> > ExtractAsync(string destinationDirectoryAbsolutePath, [NotNull] SnapRelease snapRelease,
                                                        IAsyncPackageCoreReader asyncPackageCoreReader, CancellationToken cancellationToken = default)
        {
            if (destinationDirectoryAbsolutePath == null)
            {
                throw new ArgumentNullException(nameof(destinationDirectoryAbsolutePath));
            }
            if (snapRelease == null)
            {
                throw new ArgumentNullException(nameof(snapRelease));
            }
            if (asyncPackageCoreReader == null)
            {
                throw new ArgumentNullException(nameof(asyncPackageCoreReader));
            }

            var snapApp = await _snapPack.GetSnapAppAsync(asyncPackageCoreReader, cancellationToken);

            var coreRunExeFilename = _snapEmbeddedResources.GetCoreRunExeFilenameForSnapApp(snapApp);
            var extractedFiles     = new List <string>();

            _snapFilesystem.DirectoryCreateIfNotExists(destinationDirectoryAbsolutePath);

            var files = !snapRelease.IsFull ?
                        snapRelease
                        .New
                        .Concat(snapRelease.Modified)
                        .OrderBy(x => x.NuspecTargetPath, new OrdinalIgnoreCaseComparer())
                        .ToList() :
                        snapRelease.Files;

            foreach (var checksum in files)
            {
                var isSnapRootTargetItem = checksum.NuspecTargetPath.StartsWith(SnapConstants.NuspecAssetsTargetPath);

                string dstFilename;
                if (isSnapRootTargetItem)
                {
                    dstFilename = _snapFilesystem.PathCombine(destinationDirectoryAbsolutePath, checksum.Filename);

                    if (checksum.Filename == coreRunExeFilename)
                    {
                        dstFilename = _snapFilesystem.PathCombine(
                            _snapFilesystem.DirectoryGetParent(destinationDirectoryAbsolutePath), checksum.Filename);
                    }
                }
                else
                {
                    var targetPath = checksum.NuspecTargetPath[(SnapConstants.NuspecRootTargetPath.Length + 1)..];
Esempio n. 4
0
        public async Task TestGetMetadatasAsync_Local_Directory_PackageSource(NuGetProtocolVersion protocolVersion)
        {
            await using var packagesDirectory = new DisposableDirectory(_baseFixture.WorkingDirectory, _snapFilesystem);

            var packageSource = new PackageSource(packagesDirectory, "test", true)
            {
                ProtocolVersion = (int)protocolVersion
            };

            var initialPackageIdentity = new PackageIdentity("test", NuGetVersion.Parse("1.0.0"));

            var initialPackageFilenameAbsolute = _snapFilesystem.PathCombine(packagesDirectory,
                                                                             $"{initialPackageIdentity.Id}.{initialPackageIdentity.Version.ToNormalizedString()}.nupkg");

            await using (var packageOutputStream = _snapFilesystem.FileReadWrite(initialPackageFilenameAbsolute))
            {
                await using var nupkgStream = BuildNupkg(initialPackageIdentity);
                await nupkgStream.CopyToAsync(packageOutputStream);
            }

            var secondPackageIdentity = new PackageIdentity("test", NuGetVersion.Parse("2.0.0"));

            var secondPackageFilenameAbsolute = _snapFilesystem.PathCombine(packagesDirectory,
                                                                            $"{secondPackageIdentity.Id}.{secondPackageIdentity.Version.ToNormalizedString()}.nupkg");

            await using (var packageOutputStream = _snapFilesystem.FileReadWrite(secondPackageFilenameAbsolute))
            {
                await using var nupkgStream = BuildNupkg(secondPackageIdentity);
                await nupkgStream.CopyToAsync(packageOutputStream);
            }

            var differentPackageIdentity = new PackageIdentity("test2", NuGetVersion.Parse("2.0.0"));

            var differentPackageFilenameAbsolute = _snapFilesystem.PathCombine(packagesDirectory,
                                                                               $"{differentPackageIdentity.Id}.{differentPackageIdentity.Version.ToNormalizedString()}.nupkg");

            await using (var packageOutputStream = _snapFilesystem.FileReadWrite(differentPackageFilenameAbsolute))
            {
                await using var nupkgStream = BuildNupkg(differentPackageIdentity);
                await nupkgStream.CopyToAsync(packageOutputStream);
            }

            var packageSources = new NuGetInMemoryPackageSources(packagesDirectory, packageSource);

            var packages = await _nugetService
                           .GetMetadatasAsync("test", packageSources, false, cancellationToken : CancellationToken.None);

            Assert.Equal(2, packages.Count);
        }
Esempio n. 5
0
        public DisposableDirectory(string workingDirectory, ISnapFilesystem filesystem, bool createRandomSubdirectory = true)
        {
            _filesystem      = filesystem;
            WorkingDirectory = !createRandomSubdirectory ? workingDirectory : filesystem.PathCombine(workingDirectory, Guid.NewGuid().ToString());

            filesystem.DirectoryCreateIfNotExists(WorkingDirectory);
        }
Esempio n. 6
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);
        }
Esempio n. 7
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));
        }
Esempio n. 8
0
        async Task WriteNugetConfigToWorkingDirectoryAsync()
        {
            const string nugetConfigXml =
                @"<?xml version=""1.0"" encoding=""utf-8""?><configuration><packageSources><add key=""nuget.org"" value=""https://api.nuget.org/v3/index.json"" /></packageSources><activePackageSource><add key=""All"" value=""(Aggregate source)"" /></activePackageSource> </configuration>";

            var dstFilename = _snapFilesystem.PathCombine(_baseFixture.WorkingDirectory, "nuget.config");
            await _snapFilesystem.FileWriteUtf8StringAsync(nugetConfigXml, dstFilename, CancellationToken.None);
        }
Esempio n. 9
0
        static (SnapApps snapApps, List <SnapApp> snapAppTargets, bool error, string snapsAbsoluteFilename) BuildSnapAppsesFromDirectory(
            [NotNull] ISnapFilesystem filesystem, [NotNull] ISnapAppReader reader, [NotNull] INuGetPackageSources nuGetPackageSources,
            [NotNull] string workingDirectory)
        {
            if (filesystem == null)
            {
                throw new ArgumentNullException(nameof(filesystem));
            }
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }
            if (nuGetPackageSources == null)
            {
                throw new ArgumentNullException(nameof(nuGetPackageSources));
            }
            if (workingDirectory == null)
            {
                throw new ArgumentNullException(nameof(workingDirectory));
            }

            var snapsFilename = filesystem.PathCombine(workingDirectory, ".snapx", SnapxYamlFilename);

            try
            {
                var snapApps = BuildSnapAppsFromDirectory(filesystem, reader, workingDirectory);
                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();

                return(snapApps, snapApps.BuildSnapApps(nuGetPackageSources, filesystem).ToList(), false, snapsFilename);
            }
            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(), new List <SnapApp>(), true, snapsFilename);
        }
Esempio n. 10
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);
        }
Esempio n. 11
0
        public CoreRunLib([JetBrains.Annotations.NotNull] ISnapFilesystem filesystem, OSPlatform osPlatform, [JetBrains.Annotations.NotNull] string workingDirectory)
        {
            if (filesystem == null)
            {
                throw new ArgumentNullException(nameof(filesystem));
            }
            if (workingDirectory == null)
            {
                throw new ArgumentNullException(nameof(workingDirectory));
            }

            if (!osPlatform.IsSupportedOsVersion())
            {
                throw new PlatformNotSupportedException();
            }

            _osPlatform = osPlatform;

            var filename = filesystem.PathCombine(workingDirectory, "libcorerun-");

            #if SNAP_BOOTSTRAP
            return;
            #endif

            var rid = osPlatform.BuildRid();
            if (osPlatform == OSPlatform.Windows)
            {
                filename += $"{rid}.dll";
                _libPtr   = NativeMethodsWindows.dlopen(filename);
            }
            else if (osPlatform == OSPlatform.Linux)
            {
                filename += $"{rid}.so";
                _libPtr   = NativeMethodsUnix.dlopen(filename, NativeMethodsUnix.libdl_RTLD_NOW | NativeMethodsUnix.libdl_RTLD_LOCAL);
            }

            if (_libPtr == IntPtr.Zero)
            {
                throw new FileNotFoundException($"Failed to load corerun: {filename}. " +
                                                $"OS: {osPlatform}. " +
                                                $"64-bit OS: {Environment.Is64BitOperatingSystem}. " +
                                                $"Last error: {Marshal.GetLastWin32Error()}.");
            }

            // generic
            pal_is_elevated = new Delegate <pal_is_elevated_delegate>(_libPtr, osPlatform);
            pal_set_icon    = new Delegate <pal_set_icon_delegate>(_libPtr, osPlatform);

            // filesystem
            pal_fs_chmod       = new Delegate <pal_fs_chmod_delegate>(_libPtr, osPlatform);
            pal_fs_file_exists = new Delegate <pal_fs_file_exists_delegate>(_libPtr, osPlatform);
        }
Esempio n. 12
0
        static string BuildPackagesDirectory([NotNull] ISnapFilesystem filesystem, [NotNull] string workingDirectory)
        {
            if (filesystem == null)
            {
                throw new ArgumentNullException(nameof(filesystem));
            }
            if (workingDirectory == null)
            {
                throw new ArgumentNullException(nameof(workingDirectory));
            }

            return(filesystem.PathCombine(workingDirectory, ".snapx", "packages"));
        }
 public static string GetFullPath(this AssemblyDefinition assemblyDefinition, [NotNull] ISnapFilesystem filesystem, [NotNull] string workingDirectory, OSPlatform osPlatform = default)
 {
     if (assemblyDefinition == null)
     {
         throw new ArgumentNullException(nameof(assemblyDefinition));
     }
     if (filesystem == null)
     {
         throw new ArgumentNullException(nameof(filesystem));
     }
     if (workingDirectory == null)
     {
         throw new ArgumentNullException(nameof(workingDirectory));
     }
     return(filesystem.PathCombine(workingDirectory, assemblyDefinition.BuildRelativeFilename(osPlatform)));
 }
Esempio n. 14
0
        public async Task TestRestoreAsync_SnapAppReleases_Empty(SnapPackageManagerRestoreType restoreType)
        {
            var genesisSnapApp = _baseFixturePackaging.BuildSnapApp();
            var snapAppChannel = genesisSnapApp.GetDefaultChannelOrThrow();

            await using var restoreDirectory             = new DisposableDirectory(_baseFixturePackaging.WorkingDirectory, _snapFilesystem);
            await using var nugetPackageSourcesDirectory = _snapFilesystem.WithDisposableTempDirectory(_baseFixturePackaging.WorkingDirectory);
            var packagesDirectory   = _snapFilesystem.PathCombine(restoreDirectory.WorkingDirectory, "packages");
            var nugetPackageSources = genesisSnapApp.BuildNugetSources(nugetPackageSourcesDirectory.WorkingDirectory);
            var packageSource       = nugetPackageSources.Items.Single();

            var snapAppChannelReleases = new SnapAppChannelReleases(genesisSnapApp, snapAppChannel, Enumerable.Empty <SnapRelease>());
            var restoreSummary         = await _snapPackageManager.RestoreAsync(packagesDirectory, snapAppChannelReleases, packageSource, restoreType);

            Assert.Empty(restoreSummary.ChecksumSummary);
            Assert.Empty(restoreSummary.DownloadSummary);
            Assert.Empty(restoreSummary.ReassembleSummary);
            Assert.Equal(restoreType, restoreSummary.RestoreType);
            Assert.True(restoreSummary.Success);
        }
Esempio n. 15
0
        internal static void GetCoreRunExecutableFullPath([NotNull] this SnapApp snapApp, [NotNull] ISnapFilesystem snapFilesystem, [NotNull] string baseDirectory, out string coreRunFullPath)
        {
            if (snapApp == null)
            {
                throw new ArgumentNullException(nameof(snapApp));
            }
            if (snapFilesystem == null)
            {
                throw new ArgumentNullException(nameof(snapFilesystem));
            }
            if (baseDirectory == null)
            {
                throw new ArgumentNullException(nameof(baseDirectory));
            }

            var exeName = snapApp.Id;

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                exeName += ".exe";
            }

            coreRunFullPath = snapFilesystem.PathCombine(baseDirectory, exeName);
        }
        public async Task TestRestoreAsync_Checksums_Genesis()
        {
            var snapAppsReleases = new SnapAppsReleases();
            var genesisSnapApp   = _baseFixturePackaging.BuildSnapApp();
            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);
            var packagesDirectory = _snapFilesystem.PathCombine(restoreDirectory.WorkingDirectory, "packages");

            _snapFilesystem.DirectoryCreate(packagesDirectory);

            var nugetPackageSources = genesisSnapApp.BuildNugetSources(nugetPackageSourcesDirectory.WorkingDirectory);
            var packageSource       = nugetPackageSources.Items.Single();

            genesisReleaseBuilder
            .AddNuspecItem(_baseFixturePackaging.BuildSnapExecutable(genesisSnapApp))
            .AddSnapDll();

            using var genesisPackageContext = await _baseFixturePackaging.BuildPackageAsync(genesisReleaseBuilder);

            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>()));

            var genesisPackageAbsolutePath = _snapFilesystem.PathCombine(packagesDirectory, genesisPackageContext.FullPackageSnapRelease.Filename);

            await _snapFilesystem.FileCopyAsync(genesisPackageContext.FullPackageAbsolutePath, genesisPackageAbsolutePath, default);

            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 == 1)), Times.Once);

            progressSourceMock.Verify(x => x.RaiseChecksumProgress(
                                          It.Is <int>(v => v == 100),
                                          It.Is <long>(v => v == 1),
                                          It.Is <long>(v => v == 1),
                                          It.Is <long>(v => v == 1)), Times.Once);

            Assert.Equal(SnapPackageManagerRestoreType.Default, restoreSummary.RestoreType);

            Assert.Single(restoreSummary.ChecksumSummary);
            Assert.True(restoreSummary.ChecksumSummary[0].Ok);
            Assert.Equal(genesisPackageContext.FullPackageSnapRelease.Filename, restoreSummary.ChecksumSummary[0].SnapRelease.Filename);

            Assert.Empty(restoreSummary.DownloadSummary);
            Assert.Empty(restoreSummary.ReassembleSummary);
            Assert.True(restoreSummary.Success);

            using var packageArchiveReader = new PackageArchiveReader(genesisPackageAbsolutePath);
            Assert.Equal(genesisPackageContext.FullPackageSnapRelease.BuildPackageIdentity(), packageArchiveReader.GetIdentity());
            Assert.Equal(genesisPackageContext.FullPackageSnapRelease.FullSha256Checksum,
                         _snapCryptoProvider.Sha256(genesisPackageContext.FullPackageSnapRelease, packageArchiveReader, _snapPack));
        }
Esempio n. 17
0
        static async Task <int> CommandPromoteAsync([NotNull] PromoteOptions options, [NotNull] ISnapFilesystem filesystem,
                                                    [NotNull] ISnapAppReader snapAppReader, [NotNull] ISnapAppWriter snapAppWriter, [NotNull] INuGetPackageSources nuGetPackageSources,
                                                    [NotNull] INugetService nugetService, [NotNull] IDistributedMutexClient distributedMutexClient,
                                                    [NotNull] ISnapPackageManager snapPackageManager, [NotNull] ISnapPack snapPack, [NotNull] ISnapOsSpecialFolders specialFolders,
                                                    [NotNull] ISnapNetworkTimeProvider snapNetworkTimeProvider, [NotNull] ISnapExtractor snapExtractor, [NotNull] ISnapOs snapOs,
                                                    [NotNull] ISnapxEmbeddedResources snapxEmbeddedResources, [NotNull] ICoreRunLib coreRunLib,
                                                    [NotNull] ILog logger, [NotNull] string workingDirectory, CancellationToken cancellationToken)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (filesystem == null)
            {
                throw new ArgumentNullException(nameof(filesystem));
            }
            if (snapAppReader == null)
            {
                throw new ArgumentNullException(nameof(snapAppReader));
            }
            if (snapAppWriter == null)
            {
                throw new ArgumentNullException(nameof(snapAppWriter));
            }
            if (nuGetPackageSources == null)
            {
                throw new ArgumentNullException(nameof(nuGetPackageSources));
            }
            if (nugetService == null)
            {
                throw new ArgumentNullException(nameof(nugetService));
            }
            if (distributedMutexClient == null)
            {
                throw new ArgumentNullException(nameof(distributedMutexClient));
            }
            if (snapPackageManager == null)
            {
                throw new ArgumentNullException(nameof(snapPackageManager));
            }
            if (snapPack == null)
            {
                throw new ArgumentNullException(nameof(snapPack));
            }
            if (specialFolders == null)
            {
                throw new ArgumentNullException(nameof(specialFolders));
            }
            if (snapNetworkTimeProvider == null)
            {
                throw new ArgumentNullException(nameof(snapNetworkTimeProvider));
            }
            if (snapExtractor == null)
            {
                throw new ArgumentNullException(nameof(snapExtractor));
            }
            if (snapOs == null)
            {
                throw new ArgumentNullException(nameof(snapOs));
            }
            if (snapxEmbeddedResources == null)
            {
                throw new ArgumentNullException(nameof(snapxEmbeddedResources));
            }
            if (coreRunLib == null)
            {
                throw new ArgumentNullException(nameof(coreRunLib));
            }
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }
            if (workingDirectory == null)
            {
                throw new ArgumentNullException(nameof(workingDirectory));
            }
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            if (nugetService == null)
            {
                throw new ArgumentNullException(nameof(nugetService));
            }

            var stopWatch = new Stopwatch();

            stopWatch.Restart();

            options.Channel = string.IsNullOrWhiteSpace(options.Channel) ? null : options.Channel;

            if (options.Channel != null && !options.Channel.IsValidChannelName())
            {
                logger.Error($"Invalid channel name: {options.Channel}");
                return(1);
            }

            var(snapApps, snapApp, error, _) = BuildSnapAppFromDirectory(filesystem, snapAppReader,
                                                                         nuGetPackageSources, options.Id, options.Rid, workingDirectory);
            if (snapApp == null)
            {
                if (!error)
                {
                    logger.Error($"Unable to find snap with id: {options.Id}. Rid: {options.Rid}.");
                }

                return(1);
            }

            var installersDirectory = BuildInstallersDirectory(filesystem, workingDirectory, snapApps.Generic, snapApp);
            var packagesDirectory   = BuildPackagesDirectory(filesystem, workingDirectory, snapApps.Generic, snapApp);

            var promoteBaseChannel = snapApp.Channels.SingleOrDefault(x => string.Equals(x.Name, options.Channel, StringComparison.OrdinalIgnoreCase));

            if (promoteBaseChannel == null)
            {
                logger.Error($"Unable to find channel: {options.Channel}.");
                return(1);
            }

            MaybeOverrideLockToken(snapApps, logger, options.Id, options.LockToken);

            if (string.IsNullOrWhiteSpace(snapApps.Generic.Token))
            {
                logger.Error("Please specify a lock token in your snapx.yml file. It's sufficient to generate random UUID (Guid).");
                return(1);
            }

            await using var distributedMutex = WithDistributedMutex(distributedMutexClient, logger, snapApps.BuildLockKey(snapApp), cancellationToken);

            logger.Info('-'.Repeat(TerminalBufferWidth));

            var tryAcquireRetries = options.LockRetries == -1 ? int.MaxValue : options.LockRetries;

            if (!await distributedMutex.TryAquireAsync(TimeSpan.FromSeconds(15), tryAcquireRetries))
            {
                logger.Info('-'.Repeat(TerminalBufferWidth));
                return(1);
            }

            var channelsStr = string.Join(", ", snapApp.Channels.Select(x => x.Name));

            logger.Info('-'.Repeat(TerminalBufferWidth));
            logger.Info($"Snap id: {options.Id}");
            logger.Info($"Rid: {options.Rid}");
            logger.Info($"Source channel: {options.Channel}");
            logger.Info($"Channels: {channelsStr}");
            logger.Info('-'.Repeat(TerminalBufferWidth));

            logger.Info("Downloading releases nupkg.");
            var(snapAppsReleases, _, releasesMemoryStream) = await snapPackageManager.GetSnapsReleasesAsync(snapApp, logger, cancellationToken);

            if (releasesMemoryStream != null)
            {
                await releasesMemoryStream.DisposeAsync();
            }
            if (snapAppsReleases == null)
            {
                logger.Error($"Unknown error downloading releases nupkg: {snapApp.BuildNugetReleasesFilename()}.");
                return(1);
            }

            var snapAppChannelReleases = snapAppsReleases.GetReleases(snapApp, promoteBaseChannel);

            var mostRecentRelease = snapAppChannelReleases.GetMostRecentRelease();

            if (mostRecentRelease == null)
            {
                logger.Error($"Unable to find any releases in channel: {promoteBaseChannel.Name}.");
                return(1);
            }

            snapApp.Version = mostRecentRelease.Version;

            var currentChannelIndex = mostRecentRelease.Channels.FindIndex(channelName => channelName == promoteBaseChannel.Name);
            var promotableChannels  = snapApp.Channels
                                      .Skip(currentChannelIndex + 1)
                                      .Select(channel =>
            {
                var releasesThisChannel = snapAppsReleases.GetReleases(snapApp, channel);
                if (releasesThisChannel.Any(x => mostRecentRelease.IsFull ? x.IsFull : x.IsDelta && x.Version == snapApp.Version))
                {
                    return(null);
                }

                return(channel);
            })
                                      .Where(x => x != null)
                                      .ToList();

            if (!promotableChannels.Any())
            {
                logger.Info($"Version {snapApp.Version} is already promoted to all channels.");
                return(0);
            }

            var promoteToChannels = new List <SnapChannel>();

            if (options.ToAllRemainingChannels)
            {
                promoteToChannels.AddRange(promotableChannels);
            }
            else
            {
                promoteToChannels.Add(promotableChannels.First());
            }

            var promoteToChannelsStr = string.Join(", ", promoteToChannels.Select(x => x.Name));

            if (!logger.Prompt("y|yes",
                               $"You are about to promote {snapApp.Id} ({snapApp.Version}) to the following " +
                               $"channel{(promoteToChannels.Count > 1 ? "s" : string.Empty)}: {promoteToChannelsStr}. " +
                               "Do you want to continue? [y|n]", infoOnly: options.YesToAllPrompts)
                )
            {
                return(1);
            }

            foreach (var snapRelease in snapAppChannelReleases.Where(x => x.Version <= mostRecentRelease.Version))
            {
                foreach (var promoteToChannel in promoteToChannels)
                {
                    if (!snapRelease.Channels.Contains(promoteToChannel.Name))
                    {
                        snapRelease.Channels.Add(promoteToChannel.Name);
                    }
                }
            }

            logger.Info("Building releases nupkg.");

            var nowUtc = await SnapUtility.RetryAsync(async() => await snapNetworkTimeProvider.NowUtcAsync(), 3, 1500);

            if (!nowUtc.HasValue)
            {
                logger.Error($"Unknown error while retrieving NTP timestamp from server: {snapNetworkTimeProvider}");
                return(1);
            }

            snapAppsReleases.LastWriteAccessUtc = nowUtc.Value;

            await using var releasesPackageMemoryStream = snapPack.BuildReleasesPackage(snapApp, snapAppsReleases);
            logger.Info("Finished building releases nupkg.");

            var restoreOptions = new RestoreOptions
            {
                Id                  = options.Id,
                Rid                 = options.Rid,
                BuildInstallers     = false,
                RestoreStrategyType = SnapPackageManagerRestoreType.Default
            };

            var restoreSuccess = 0 == await CommandRestoreAsync(
                restoreOptions, filesystem, snapAppReader, snapAppWriter, nuGetPackageSources,
                snapPackageManager, snapOs, snapxEmbeddedResources, coreRunLib, snapPack,
                logger, workingDirectory, cancellationToken
                );

            if (!restoreSuccess)
            {
                return(1);
            }

            await using var tmpDir = new DisposableDirectory(specialFolders.NugetCacheDirectory, filesystem);
            var releasesPackageFilename     = snapApp.BuildNugetReleasesFilename();
            var releasesPackageAbsolutePath = filesystem.PathCombine(tmpDir.WorkingDirectory, releasesPackageFilename);
            await filesystem.FileWriteAsync(releasesPackageMemoryStream, releasesPackageAbsolutePath, cancellationToken);

            if (!options.SkipInstallers && snapApp.Target.Installers.Any())
            {
                foreach (var channel in promoteToChannels)
                {
                    var snapAppInstaller = new SnapApp(snapApp);
                    snapAppInstaller.SetCurrentChannel(channel.Name);

                    var fullNupkgAbsolutePath = filesystem.PathCombine(packagesDirectory, snapApp.BuildNugetFullFilename());

                    if (snapApp.Target.Installers.Any(x => x.HasFlag(SnapInstallerType.Offline)))
                    {
                        logger.Info('-'.Repeat(TerminalBufferWidth));

                        var(installerOfflineSuccess, canContinueIfError, installerOfflineExeAbsolutePath) = await BuildInstallerAsync(logger, snapOs,
                                                                                                                                      snapxEmbeddedResources, snapAppWriter, snapAppInstaller, coreRunLib,
                                                                                                                                      installersDirectory, fullNupkgAbsolutePath, releasesPackageAbsolutePath,
                                                                                                                                      true, cancellationToken);

                        if (!installerOfflineSuccess)
                        {
                            if (!canContinueIfError || !logger.Prompt("y|yes", "Installer was not built. Do you still want to continue? (y|n)", infoOnly: options.YesToAllPrompts))
                            {
                                logger.Info('-'.Repeat(TerminalBufferWidth));
                                logger.Error("Unknown error building offline installer.");
                                return(1);
                            }
                        }
                        else
                        {
                            var installerOfflineExeStat = filesystem.FileStat(installerOfflineExeAbsolutePath);
                            logger.Info($"Successfully built offline installer. File size: {installerOfflineExeStat.Length.BytesAsHumanReadable()}.");
                        }
                    }

                    if (snapApp.Target.Installers.Any(x => x.HasFlag(SnapInstallerType.Web)))
                    {
                        logger.Info('-'.Repeat(TerminalBufferWidth));

                        var(installerWebSuccess, canContinueIfError, installerWebExeAbsolutePath) = await BuildInstallerAsync(logger, snapOs, snapxEmbeddedResources, snapAppWriter, snapAppInstaller, coreRunLib,
                                                                                                                              installersDirectory, null, releasesPackageAbsolutePath,
                                                                                                                              false, cancellationToken);

                        if (!installerWebSuccess)
                        {
                            if (!canContinueIfError ||
                                !logger.Prompt("y|yes", "Installer was not built. Do you still want to continue? (y|n)", infoOnly: options.YesToAllPrompts))
                            {
                                logger.Info('-'.Repeat(TerminalBufferWidth));
                                logger.Error("Unknown error building web installer.");
                                return(1);
                            }
                        }
                        else
                        {
                            var installerWebExeStat = filesystem.FileStat(installerWebExeAbsolutePath);
                            logger.Info($"Successfully built web installer. File size: {installerWebExeStat.Length.BytesAsHumanReadable()}.");
                        }
                    }
                }
            }

            logger.Info('-'.Repeat(TerminalBufferWidth));

            foreach (var(channel, packageSource) in promoteToChannels.Select(snapChannel =>
            {
                var packageSource = nuGetPackageSources.Items.Single(x => x.Name == snapChannel.PushFeed.Name);
                return(snapChannel, packageSource);
            }).DistinctBy(x => x.packageSource.SourceUri))
            {
                logger.Info($"Uploading releases nupkg to feed: {packageSource.Name}.");

                await PushPackageAsync(nugetService, filesystem, distributedMutex, nuGetPackageSources, packageSource,
                                       channel, releasesPackageAbsolutePath, logger, cancellationToken);

                var skipInitialBlock = packageSource.IsLocalOrUncPath();

                await BlockUntilSnapUpdatedReleasesNupkgAsync(logger, snapPackageManager,
                                                              snapAppsReleases, snapApp, channel, TimeSpan.FromSeconds(15), cancellationToken, skipInitialBlock, options.SkipAwaitUpdate);

                logger.Info($"Successfully uploaded releases nupkg to channel: {channel.Name}.");
                logger.Info('-'.Repeat(TerminalBufferWidth));
            }

            logger.Info($"Promote completed in {stopWatch.Elapsed.TotalSeconds:0.0}s.");

            await CommandListAsync(new ListOptions { Id = snapApp.Id }, filesystem, snapAppReader,
                                   nuGetPackageSources, nugetService, snapExtractor, snapPackageManager, logger, workingDirectory, cancellationToken);

            return(0);
        }
Esempio n. 18
0
        static async Task <int> CommandRestoreAsync([NotNull] RestoreOptions restoreOptions,
                                                    [NotNull] ISnapFilesystem filesystem, [NotNull] ISnapAppReader snapAppReader, ISnapAppWriter snapAppWriter,
                                                    [NotNull] INuGetPackageSources nuGetPackageSources, [NotNull] ISnapPackageManager snapPackageManager,
                                                    [NotNull] ISnapOs snapOs, [NotNull] ISnapxEmbeddedResources snapxEmbeddedResources, [NotNull] ICoreRunLib coreRunLib,
                                                    [NotNull] ISnapPack snapPack, [NotNull] ILog logger,
                                                    [NotNull] string workingDirectory, CancellationToken cancellationToken)
        {
            if (restoreOptions == null)
            {
                throw new ArgumentNullException(nameof(restoreOptions));
            }
            if (filesystem == null)
            {
                throw new ArgumentNullException(nameof(filesystem));
            }
            if (snapAppReader == null)
            {
                throw new ArgumentNullException(nameof(snapAppReader));
            }
            if (nuGetPackageSources == null)
            {
                throw new ArgumentNullException(nameof(nuGetPackageSources));
            }
            if (snapPackageManager == null)
            {
                throw new ArgumentNullException(nameof(snapPackageManager));
            }
            if (snapOs == null)
            {
                throw new ArgumentNullException(nameof(snapOs));
            }
            if (snapxEmbeddedResources == null)
            {
                throw new ArgumentNullException(nameof(snapxEmbeddedResources));
            }
            if (coreRunLib == null)
            {
                throw new ArgumentNullException(nameof(coreRunLib));
            }
            if (snapPack == null)
            {
                throw new ArgumentNullException(nameof(snapPack));
            }
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            if (workingDirectory == null)
            {
                throw new ArgumentNullException(nameof(workingDirectory));
            }
            var stopwatch = new Stopwatch();

            stopwatch.Restart();

            var(snapApps, snapAppTargets, errorBuildingSnapApps, _) = BuildSnapAppsesFromDirectory(filesystem, snapAppReader,
                                                                                                   nuGetPackageSources, workingDirectory, requirePushFeed: false);

            if (!snapApps.Apps.Any() || errorBuildingSnapApps)
            {
                return(1);
            }

            if (restoreOptions.Id != null)
            {
                snapAppTargets.RemoveAll(x =>
                                         !string.Equals(x.Id, restoreOptions.Id, StringComparison.OrdinalIgnoreCase));
            }

            if (restoreOptions.Rid != null)
            {
                snapAppTargets.RemoveAll(x =>
                                         !string.Equals(x.Target.Rid, restoreOptions.Rid, StringComparison.OrdinalIgnoreCase));
            }

            if (!snapAppTargets.Any())
            {
                logger.Error($"Unable to restore application {restoreOptions.Id} because it does not exist.");
                return(1);
            }

            if (restoreOptions.BuildInstallers)
            {
                restoreOptions.RestoreStrategyType = SnapPackageManagerRestoreType.Default;
            }

            var applicationNames = snapAppTargets.Select(x => x.Id).Distinct().ToList();
            var rids             = snapAppTargets.Select(x => x.Target.Rid).Distinct().ToList();

            logger.Info($"Applications that will be restored: {string.Join(", ", applicationNames)}. Runtime identifiers (RID): {string.Join(", ", rids)}.");

            var releasePackages = new Dictionary <string, (SnapAppsReleases snapReleases, PackageSource packageSource)>();

            foreach (var snapApp in snapAppTargets)
            {
                var packagesDirectory         = BuildPackagesDirectory(filesystem, workingDirectory, snapApps.Generic, snapApp);
                var installersDirectory       = BuildInstallersDirectory(filesystem, workingDirectory, snapApps.Generic, snapApp);
                var releasesNupkgAbsolutePath = filesystem.PathCombine(filesystem.DirectoryGetParent(packagesDirectory), snapApp.BuildNugetReleasesFilename());

                filesystem.DirectoryCreateIfNotExists(packagesDirectory);
                filesystem.DirectoryCreateIfNotExists(installersDirectory);

                logger.Info('-'.Repeat(TerminalBufferWidth));
                logger.Info($"Id: {snapApp.Id}.");
                logger.Info($"Rid: {snapApp.Target.Rid}");
                logger.Info($"Packages directory: {packagesDirectory}");
                logger.Info($"Restore strategy: {restoreOptions.RestoreStrategyType}");
                logger.Info($"Restore installers: {(restoreOptions.BuildInstallers ? "yes" : "no")}");

                SnapAppsReleases snapAppsReleases;
                PackageSource    packageSource;
                if (releasePackages.TryGetValue(snapApp.Id, out var cached))
                {
                    snapAppsReleases = cached.snapReleases;
                    packageSource    = cached.packageSource;
                }
                else
                {
                    logger.Info($"Downloading releases nupkg for application {snapApp.Id}");

                    // ReSharper disable once UseDeconstruction
                    var uncached = await snapPackageManager.GetSnapsReleasesAsync(snapApp, logger, cancellationToken);

                    if (uncached.snapAppsReleases == null)
                    {
                        logger.Error($"Failed to download releases nupkg for application {snapApp.Id}");
                        continue;
                    }

                    await using (uncached.releasesMemoryStream)
                    {
                        await filesystem.FileWriteAsync(uncached.releasesMemoryStream, releasesNupkgAbsolutePath, cancellationToken);
                    }

                    snapAppsReleases = uncached.snapAppsReleases;
                    packageSource    = uncached.packageSource;
                    releasePackages.Add(snapApp.Id, (uncached.snapAppsReleases, uncached.packageSource));

                    logger.Info($"Downloaded releases nupkg. Current version: {snapAppsReleases.Version}.");
                }

                foreach (var snapChannel in snapAppsReleases.GetChannels(snapApp))
                {
                    logger.Info('-'.Repeat(TerminalBufferWidth));
                    logger.Info($"Restoring channel {snapChannel.Name}.");

                    var snapAppReleases = snapAppsReleases.GetReleases(snapApp, snapChannel);
                    if (!snapAppReleases.Any())
                    {
                        logger.Info($"Skipping restore for channel {snapChannel.Name} because no releases was found.");
                        continue;
                    }

                    var restoreSummary = await snapPackageManager.RestoreAsync(packagesDirectory, snapAppReleases, packageSource,
                                                                               restoreOptions.RestoreStrategyType,
                                                                               logger : logger, cancellationToken : cancellationToken,
                                                                               checksumConcurrency : restoreOptions.RestoreConcurrency,
                                                                               downloadConcurrency : restoreOptions.DownloadConcurrency);

                    if (!restoreSummary.Success || !restoreOptions.BuildInstallers)
                    {
                        continue;
                    }

                    var mostRecentSnapRelease = snapAppReleases.GetMostRecentRelease();

                    var snapAppInstaller = new SnapApp(snapAppReleases.App)
                    {
                        Version = mostRecentSnapRelease.Version
                    };

                    snapAppInstaller.SetCurrentChannel(snapChannel.Name);

                    if (restoreOptions.BuildInstallers ||
                        snapApp.Target.Installers.Any(x => x.HasFlag(SnapInstallerType.Web)))
                    {
                        logger.Info('-'.Repeat(TerminalBufferWidth));

                        await BuildInstallerAsync(logger, snapOs, snapxEmbeddedResources, snapAppWriter, snapAppInstaller, coreRunLib,
                                                  installersDirectory, null, releasesNupkgAbsolutePath, false, cancellationToken);
                    }

                    if (restoreOptions.BuildInstallers ||
                        snapApp.Target.Installers.Any(x => x.HasFlag(SnapInstallerType.Offline)))
                    {
                        logger.Info('-'.Repeat(TerminalBufferWidth));

                        var fullNupkgAbsolutePath = filesystem.PathCombine(packagesDirectory, mostRecentSnapRelease.BuildNugetFullFilename());

                        await BuildInstallerAsync(logger, snapOs, snapxEmbeddedResources, snapAppWriter, snapAppInstaller, coreRunLib,
                                                  installersDirectory, fullNupkgAbsolutePath, releasesNupkgAbsolutePath, true, cancellationToken);
                    }

                    logger.Info($"Finished restoring channel {snapChannel.Name}.");
                    logger.Info('-'.Repeat(TerminalBufferWidth));
                }
            }

            logger.Info('-'.Repeat(TerminalBufferWidth));
            logger.Info($"Restore completed in {stopwatch.Elapsed.TotalSeconds:0.0}s.");

            return(0);
        }
Esempio n. 19
0
        public async Task TestDirectoryDeleteAsync()
        {
            await using var tmpDir = _baseFixture.WithDisposableTempDirectory(_snapFilesystem);
            var rootDirectory = _snapFilesystem.PathCombine(tmpDir.WorkingDirectory, "rootDirectory");

            _snapFilesystem.DirectoryCreate(rootDirectory);

            var subDirectory = _snapFilesystem.PathCombine(rootDirectory, "subDirectory");

            _snapFilesystem.DirectoryCreate(subDirectory);

            var testFile = _snapFilesystem.PathCombine(subDirectory, "mytEstFile.txt");
            await _snapFilesystem.FileWriteUtf8StringAsync("yolo", testFile, default);

            await _snapFilesystem.DirectoryDeleteAsync(rootDirectory);

            Assert.False(_snapFilesystem.DirectoryExists(rootDirectory));
        }
Esempio n. 20
0
        static async Task <int> CommandPackAsync([NotNull] PackOptions packOptions, [NotNull] ISnapFilesystem filesystem,
                                                 [NotNull] ISnapAppReader snapAppReader, [NotNull] ISnapAppWriter snapAppWriter, [NotNull] INuGetPackageSources nuGetPackageSources,
                                                 [NotNull] ISnapPack snapPack, [NotNull] INugetService nugetService, [NotNull] ISnapOs snapOs,
                                                 [NotNull] ISnapxEmbeddedResources snapxEmbeddedResources, [NotNull] ISnapExtractor snapExtractor,
                                                 [NotNull] ISnapPackageManager snapPackageManager, [NotNull] ICoreRunLib coreRunLib, [NotNull] ISnapNetworkTimeProvider snapNetworkTimeProvider,
                                                 [NotNull] ILog logger, [NotNull] IDistributedMutexClient distributedMutexClient, [NotNull] string workingDirectory, CancellationToken cancellationToken)
        {
            if (packOptions == null)
            {
                throw new ArgumentNullException(nameof(packOptions));
            }
            if (filesystem == null)
            {
                throw new ArgumentNullException(nameof(filesystem));
            }
            if (snapAppReader == null)
            {
                throw new ArgumentNullException(nameof(snapAppReader));
            }
            if (snapAppWriter == null)
            {
                throw new ArgumentNullException(nameof(snapAppWriter));
            }
            if (nuGetPackageSources == null)
            {
                throw new ArgumentNullException(nameof(nuGetPackageSources));
            }
            if (snapPack == null)
            {
                throw new ArgumentNullException(nameof(snapPack));
            }
            if (nugetService == null)
            {
                throw new ArgumentNullException(nameof(nugetService));
            }
            if (snapOs == null)
            {
                throw new ArgumentNullException(nameof(snapOs));
            }
            if (snapxEmbeddedResources == null)
            {
                throw new ArgumentNullException(nameof(snapxEmbeddedResources));
            }
            if (snapExtractor == null)
            {
                throw new ArgumentNullException(nameof(snapExtractor));
            }
            if (snapPackageManager == null)
            {
                throw new ArgumentNullException(nameof(snapPackageManager));
            }
            if (coreRunLib == null)
            {
                throw new ArgumentNullException(nameof(coreRunLib));
            }
            if (snapNetworkTimeProvider == null)
            {
                throw new ArgumentNullException(nameof(snapNetworkTimeProvider));
            }
            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }
            if (distributedMutexClient == null)
            {
                throw new ArgumentNullException(nameof(distributedMutexClient));
            }
            if (workingDirectory == null)
            {
                throw new ArgumentNullException(nameof(workingDirectory));
            }

            var stopwatch = new Stopwatch();

            stopwatch.Restart();

            var(snapApps, snapApp, error, snapsManifestAbsoluteFilename) = BuildSnapAppFromDirectory(filesystem, snapAppReader,
                                                                                                     nuGetPackageSources, packOptions.Id, packOptions.Rid, workingDirectory);
            if (snapApp == null)
            {
                if (!error)
                {
                    logger.Error($"Snap with id {packOptions.Id} was not found in manifest: {snapsManifestAbsoluteFilename}");
                }

                return(1);
            }

            if (!SemanticVersion.TryParse(packOptions.Version, out var semanticVersion))
            {
                logger.Error($"Unable to parse semantic version (v2): {packOptions.Version}");
                return(1);
            }

            snapApp.Version = semanticVersion;

            if (packOptions.ReleasesNotes != null)
            {
                snapApp.ReleaseNotes = packOptions.ReleasesNotes;
            }

            var artifactsDirectory  = BuildArtifactsDirectory(filesystem, workingDirectory, snapApps.Generic, snapApp);
            var installersDirectory = BuildInstallersDirectory(filesystem, workingDirectory, snapApps.Generic, snapApp);
            var packagesDirectory   = BuildPackagesDirectory(filesystem, workingDirectory, snapApps.Generic, snapApp);

            filesystem.DirectoryCreateIfNotExists(installersDirectory);
            filesystem.DirectoryCreateIfNotExists(packagesDirectory);

            var snapAppChannel = snapApp.GetDefaultChannelOrThrow();

            MaybeOverrideLockToken(snapApps, logger, packOptions.Id, packOptions.LockToken);

            if (string.IsNullOrWhiteSpace(snapApps.Generic.Token))
            {
                logger.Error("Please specify a token in your snapx.yml file. A random UUID is sufficient.");
                return(1);
            }

            await using var distributedMutex = WithDistributedMutex(distributedMutexClient, logger, snapApps.BuildLockKey(snapApp), cancellationToken);

            logger.Info($"Schema version: {snapApps.Schema}");
            logger.Info($"Packages directory: {packagesDirectory}");
            logger.Info($"Artifacts directory: {artifactsDirectory}");
            logger.Info($"Installers directory: {installersDirectory}");
            logger.Info($"Pack strategy: {snapApps.Generic.PackStrategy}");
            logger.Info('-'.Repeat(TerminalBufferWidth));
            logger.Info($"Id: {snapApp.Id}");
            logger.Info($"Version: {snapApp.Version}");
            logger.Info($"Channel: {snapAppChannel.Name}");
            logger.Info($"Rid: {snapApp.Target.Rid}");
            logger.Info($"OS: {snapApp.Target.Os.ToString().ToLowerInvariant()}");
            var installersStr = !snapApp.Target.Installers.Any() ? "None" : string.Join(", ", snapApp.Target.Installers);

            logger.Info($"Installers: {installersStr}");
            var shortcutsStr = !snapApp.Target.Shortcuts.Any() ? "None" : string.Join(", ", snapApp.Target.Shortcuts);

            logger.Info($"Shortcuts: {shortcutsStr}");

            logger.Info('-'.Repeat(TerminalBufferWidth));

            var tryAcquireRetries = packOptions.LockRetries == -1 ? int.MaxValue : packOptions.LockRetries;

            if (!await distributedMutex.TryAquireAsync(TimeSpan.FromSeconds(15), tryAcquireRetries))
            {
                logger.Info('-'.Repeat(TerminalBufferWidth));
                return(1);
            }

            logger.Info('-'.Repeat(TerminalBufferWidth));

            var updateFeedPackageSource = await snapPackageManager.GetPackageSourceAsync(snapApp);

            logger.Info("Downloading releases nupkg.");

            var snapReleasesPackageDirectory = filesystem.DirectoryGetParent(packagesDirectory);

            filesystem.DirectoryCreateIfNotExists(snapReleasesPackageDirectory);

            var(snapAppsReleases, _, currentReleasesMemoryStream) = await snapPackageManager.GetSnapsReleasesAsync(snapApp, logger, cancellationToken);

            if (currentReleasesMemoryStream != null)
            {
                await currentReleasesMemoryStream.DisposeAsync();
            }

            if (snapAppsReleases == null)
            {
                if (!logger.Prompt("y|yes", "Unable to find a previous release in any of your NuGet package sources. " +
                                   "Is this the first time you are publishing this application? " +
                                   "NB! The package may not yet be visible to due to upstream caching. [y/n]", infoOnly: packOptions.YesToAllPrompts)
                    )
                {
                    return(1);
                }

                snapAppsReleases = new SnapAppsReleases();
            }
            else
            {
                logger.Info($"Downloaded releases nupkg. Current version: {snapAppsReleases.Version}.");

                if (packOptions.Gc)
                {
                    var releasesRemoved = snapAppsReleases.Gc(snapApp);
                    logger.Info($"Garbage collected (removed) {releasesRemoved} releases.");
                }

                var snapAppChannelReleases = snapAppsReleases.GetReleases(snapApp, snapAppChannel);

                var restoreSummary = await snapPackageManager.RestoreAsync(packagesDirectory, snapAppChannelReleases,
                                                                           updateFeedPackageSource, SnapPackageManagerRestoreType.Pack, logger : logger, cancellationToken : cancellationToken);

                if (!restoreSummary.Success)
                {
                    return(1);
                }

                if (snapAppChannelReleases.Any(x => x.Version >= snapApp.Version))
                {
                    logger.Error($"Version {snapApp.Version} is already published to feed: {updateFeedPackageSource.Name}.");
                    return(1);
                }
            }

            var snapPackageDetails = new SnapPackageDetails
            {
                SnapApp             = snapApp,
                NuspecBaseDirectory = artifactsDirectory,
                PackagesDirectory   = packagesDirectory,
                SnapAppsReleases    = snapAppsReleases
            };

            logger.Info('-'.Repeat(TerminalBufferWidth));
            logger.Info($"Building nupkg: {snapApp.Version}.");

            var pushPackages = new List <string>();

            var(fullNupkgMemoryStream, fullSnapApp, fullSnapRelease, deltaNupkgMemorystream, deltaSnapApp, deltaSnapRelease) =
                await snapPack.BuildPackageAsync(snapPackageDetails, coreRunLib, cancellationToken);

            var fullNupkgAbsolutePath = filesystem.PathCombine(packagesDirectory, fullSnapRelease.Filename);

            await using (fullNupkgMemoryStream)
                await using (deltaNupkgMemorystream)
                {
                    logger.Info($"Writing full nupkg to disk: {fullSnapRelease.Filename}. File size: {fullSnapRelease.FullFilesize.BytesAsHumanReadable()}");
                    await filesystem.FileWriteAsync(fullNupkgMemoryStream, fullNupkgAbsolutePath, default);

                    if (!fullSnapRelease.IsGenesis)
                    {
                        var deltaNupkgAbsolutePath = filesystem.PathCombine(packagesDirectory, deltaSnapRelease.Filename);
                        logger.Info(
                            $"Writing delta nupkg to disk: {deltaSnapRelease.Filename}. File size: {deltaSnapRelease.DeltaFilesize.BytesAsHumanReadable()}");
                        await filesystem.FileWriteAsync(deltaNupkgMemorystream, deltaNupkgAbsolutePath, default);
                    }
                }

            var fullOrDeltaSnapApp           = deltaSnapApp ?? fullSnapApp;
            var fullOrDeltaNupkgAbsolutePath = filesystem.PathCombine(packagesDirectory, fullOrDeltaSnapApp.BuildNugetFilename());

            pushPackages.Add(fullOrDeltaNupkgAbsolutePath);

            logger.Info('-'.Repeat(TerminalBufferWidth));
            logger.Info($"Retrieving network time from: {snapNetworkTimeProvider}.");

            var nowUtc = await SnapUtility.RetryAsync(async() => await snapNetworkTimeProvider.NowUtcAsync(), 3);

            if (!nowUtc.HasValue)
            {
                logger.Error($"Unknown error retrieving network time from: {snapNetworkTimeProvider}");
                return(1);
            }

            var localTimeStr = TimeZoneInfo
                               .ConvertTimeFromUtc(nowUtc.Value, TimeZoneInfo.Local)
                               .ToString("F", CultureInfo.CurrentCulture);

            logger.Info($"Successfully retrieved network time. Time is now: {localTimeStr}");
            logger.Info('-'.Repeat(TerminalBufferWidth));

            fullSnapRelease.CreatedDateUtc = nowUtc.Value;
            if (deltaSnapRelease != null)
            {
                deltaSnapRelease.CreatedDateUtc = nowUtc.Value;
            }
            snapAppsReleases.LastWriteAccessUtc = nowUtc.Value;

            int?forcedDbVersion = null;

            if (packOptions.DbVersion > 0)
            {
                if (packOptions.DbVersion <= snapAppsReleases.DbVersion)
                {
                    logger.Error($"Unable to force database version because version is less than or equal to current database version. \n" +
                                 $"Forced version: {packOptions.DbVersion}.\n" +
                                 $"Current database version: {snapAppsReleases.DbVersion}.");
                    return(1);
                }

                forcedDbVersion = packOptions.DbVersion;

                logger.Info($"Database version is forced because of '--db-version' option. Initial database version: {forcedDbVersion}.");
            }
            else if (fullOrDeltaSnapApp.IsGenesis &&
                     fullOrDeltaSnapApp.Version.Major > snapAppsReleases.Version.Major)
            {
                forcedDbVersion = fullOrDeltaSnapApp.Version.Major;
                logger.Info($"Database version is forced because genesis nupkg detected. Initial database version: {forcedDbVersion}");
            }

            logger.Info($"Building releases nupkg. Current database version: {snapAppsReleases.Version}.");

            var releasesMemoryStream      = snapPack.BuildReleasesPackage(fullOrDeltaSnapApp, snapAppsReleases, forcedDbVersion);
            var releasesNupkgAbsolutePath = snapOs.Filesystem.PathCombine(snapReleasesPackageDirectory, fullOrDeltaSnapApp.BuildNugetReleasesFilename());
            var releasesNupkgFilename     = filesystem.PathGetFileName(releasesNupkgAbsolutePath);
            await snapOs.Filesystem.FileWriteAsync(releasesMemoryStream, releasesNupkgAbsolutePath, cancellationToken);

            pushPackages.Add(releasesNupkgAbsolutePath);

            logger.Info("Finished building releases nupkg.\n" +
                        $"Filename: {releasesNupkgFilename}.\n" +
                        $"Size: {releasesMemoryStream.Length.BytesAsHumanReadable()}.\n" +
                        $"New database version: {snapAppsReleases.Version}.\n" +
                        $"Pack id: {snapAppsReleases.PackId:N}.");

            logger.Info('-'.Repeat(TerminalBufferWidth));

            await using (releasesMemoryStream)
            {
                if (!packOptions.SkipInstallers && fullOrDeltaSnapApp.Target.Installers.Any())
                {
                    var channels = fullOrDeltaSnapApp.IsGenesis ? fullOrDeltaSnapApp.Channels : new List <SnapChannel> {
                        snapAppChannel
                    };

                    foreach (var channel in channels)
                    {
                        var snapAppInstaller = new SnapApp(fullOrDeltaSnapApp);
                        snapAppInstaller.SetCurrentChannel(channel.Name);

                        if (fullOrDeltaSnapApp.Target.Installers.Any(x => x.HasFlag(SnapInstallerType.Offline)))
                        {
                            logger.Info('-'.Repeat(TerminalBufferWidth));

                            var(installerOfflineSuccess, canContinueIfError, installerOfflineExeAbsolutePath) = await BuildInstallerAsync(logger, snapOs, snapxEmbeddedResources, snapAppWriter, snapAppInstaller, coreRunLib,
                                                                                                                                          installersDirectory, fullNupkgAbsolutePath, releasesNupkgAbsolutePath,
                                                                                                                                          true, cancellationToken);

                            if (!installerOfflineSuccess)
                            {
                                if (!canContinueIfError ||
                                    !logger.Prompt("y|yes", "Installer was not built. Do you still want to continue? (y|n)",
                                                   infoOnly: packOptions.YesToAllPrompts))
                                {
                                    logger.Info('-'.Repeat(TerminalBufferWidth));
                                    logger.Error("Unknown error building offline installer.");
                                    return(1);
                                }
                            }
                            else
                            {
                                var installerOfflineExeStat = snapOs.Filesystem.FileStat(installerOfflineExeAbsolutePath);
                                logger.Info($"Successfully built offline installer. File size: {installerOfflineExeStat.Length.BytesAsHumanReadable()}.");
                            }
                        }

                        if (fullOrDeltaSnapApp.Target.Installers.Any(x => x.HasFlag(SnapInstallerType.Web)))
                        {
                            logger.Info('-'.Repeat(TerminalBufferWidth));

                            var(installerWebSuccess, canContinueIfError, installerWebExeAbsolutePath) = await BuildInstallerAsync(logger, snapOs, snapxEmbeddedResources, snapAppWriter, snapAppInstaller, coreRunLib,
                                                                                                                                  installersDirectory, null, releasesNupkgAbsolutePath,
                                                                                                                                  false, cancellationToken);

                            if (!installerWebSuccess)
                            {
                                if (!canContinueIfError ||
                                    !logger.Prompt("y|yes", "Installer was not built. Do you still want to continue? (y|n)",
                                                   infoOnly: packOptions.YesToAllPrompts))
                                {
                                    logger.Info('-'.Repeat(TerminalBufferWidth));
                                    logger.Error("Unknown error building offline installer.");
                                    return(1);
                                }
                            }
                            else
                            {
                                var installerWebExeStat = snapOs.Filesystem.FileStat(installerWebExeAbsolutePath);
                                logger.Info($"Successfully built web installer. File size: {installerWebExeStat.Length.BytesAsHumanReadable()}.");
                            }
                        }
                    }
                }
            }

            if (snapApps.Generic.PackStrategy == SnapAppsPackStrategy.push)
            {
                await PushPackagesAsync(packOptions, logger, filesystem, nugetService,
                                        snapPackageManager, distributedMutex, snapAppsReleases, fullOrDeltaSnapApp, snapAppChannel, pushPackages, cancellationToken);
            }

            logger.Info($"Fetching releases overview from feed {updateFeedPackageSource.Name}.");

            await CommandListAsync(new ListOptions { Id = fullOrDeltaSnapApp.Id }, filesystem, snapAppReader,
                                   nuGetPackageSources, nugetService, snapExtractor, snapPackageManager, logger, workingDirectory, cancellationToken);

            logger.Info('-'.Repeat(TerminalBufferWidth));
            logger.Info($"Pack completed in {stopwatch.Elapsed.TotalSeconds:F1}s.");

            return(0);
        }
Esempio n. 21
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());
        }
Esempio n. 22
0
        public async Task TestInstall_Offline_Using_Local_PackageSource()
        {
            await using var specialFoldersAnyOs = new SnapOsSpecialFoldersUnitTest(_snapFilesystem, _baseFixturePackaging.WorkingDirectory);
            await using var packagesDirectory   = new DisposableDirectory(specialFoldersAnyOs.WorkingDirectory, _snapFilesystem);
            using var cts = new CancellationTokenSource();

            SetupSnapOsMock(specialFoldersAnyOs);

            var snapInstallerIoEnvironment = new SnapInstallerIoEnvironment
            {
                WorkingDirectory        = specialFoldersAnyOs.WorkingDirectory,
                ThisExeWorkingDirectory = specialFoldersAnyOs.WorkingDirectory,
                SpecialFolders          = specialFoldersAnyOs,
            };

            var snapAppsReleases = new SnapAppsReleases();
            var genesisSnapApp   = _baseFixturePackaging.BuildSnapApp(localPackageSourceDirectory: packagesDirectory);

            Assert.True(genesisSnapApp.Channels.Count >= 2);

            using var genesisSnapReleaseBuilder = _baseFixturePackaging.WithSnapReleaseBuilder(packagesDirectory, snapAppsReleases, genesisSnapApp, _snapReleaseBuilderContext);
            var mainAssemblyDefinition = _baseFixturePackaging.BuildSnapExecutable(genesisSnapApp);

            genesisSnapReleaseBuilder
            .AddNuspecItem(mainAssemblyDefinition)
            .AddSnapDll();

            using var genesisPackageContext = await _baseFixturePackaging.BuildPackageAsync(genesisSnapReleaseBuilder, cts.Token);

            var releasesFilename   = _snapFilesystem.PathCombine(snapInstallerIoEnvironment.ThisExeWorkingDirectory, genesisPackageContext.FullPackageSnapApp.BuildNugetReleasesFilename());
            var snapAppDllFilename = _snapFilesystem.PathCombine(snapInstallerIoEnvironment.ThisExeWorkingDirectory, SnapConstants.SnapAppDllFilename);
            var setupNupkgFilename = _snapFilesystem.PathCombine(snapInstallerIoEnvironment.ThisExeWorkingDirectory, SnapConstants.SetupNupkgFilename);

            await using var releasePackageMemoryStream = _snapPack.BuildReleasesPackage(genesisSnapApp, snapAppsReleases);

            await _snapFilesystem.FileWriteAsync(genesisPackageContext.FullPackageMemoryStream, setupNupkgFilename, cts.Token);

            await _snapFilesystem.FileWriteAsync(releasePackageMemoryStream, releasesFilename, cts.Token);

            using var snapAppDllAssemblyDefinition = _snapAppWriter.BuildSnapAppAssembly(genesisSnapApp);
            snapAppDllAssemblyDefinition.Write(snapAppDllFilename);

            var(exitCode, installerType) = await Program.MainImplAsync(new[] { "--headless" }, LogLevel.Info, cts, _snapOsMock.Object, x =>
            {
                x.Register <ISnapInstallerIoEnvironment>(_ => snapInstallerIoEnvironment);
                return((ISnapInstallerEnvironment)x.GetInstance(typeof(ISnapInstallerEnvironment)));
            });

            Assert.Equal(0, exitCode);
            Assert.Equal(SnapInstallerType.Offline, installerType);

            var appInstallDirectory = _snapInstaller.GetApplicationDirectory(
                _snapFilesystem.PathCombine(specialFoldersAnyOs.LocalApplicationData, genesisSnapApp.Id), genesisSnapApp.Version);

            var files = _snapFilesystem.DirectoryGetAllFiles(appInstallDirectory).OrderBy(x => x, new OrdinalIgnoreCaseComparer()).ToList();

            Assert.Equal(3, files.Count);
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                Assert.EndsWith($"{genesisSnapApp.Id}.exe", files[0]);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                Assert.EndsWith(genesisSnapApp.Id, files[0]);
            }
            else
            {
                throw new PlatformNotSupportedException();
            }
            Assert.EndsWith(SnapConstants.SnapAppDllFilename, files[1]);
            Assert.EndsWith(SnapConstants.SnapDllFilename, files[2]);
        }
Esempio n. 23
0
        public async Task TestInstallAsync()
        {
            var snapAppsReleases = new SnapAppsReleases();
            var genesisSnapApp   = _baseFixture.BuildSnapApp();

            Assert.True(genesisSnapApp.Channels.Count >= 2);

            using var testDirectory             = new DisposableDirectory(_baseFixture.WorkingDirectory, _snapFilesystem);
            using var genesisSnapReleaseBuilder = _baseFixture.WithSnapReleaseBuilder(testDirectory, snapAppsReleases, genesisSnapApp, _snapReleaseBuilderContext);
            var mainAssemblyDefinition = _baseFixture.BuildSnapExecutable(genesisSnapApp);

            genesisSnapReleaseBuilder
            .AddNuspecItem(mainAssemblyDefinition)
            .AddNuspecItem(mainAssemblyDefinition.BuildRuntimeConfigFilename(_snapFilesystem), mainAssemblyDefinition.BuildRuntimeConfig())
            .AddNuspecItem(_baseFixture.BuildLibrary("test1"))
            .AddSnapDll();

            using var genesisPackageContext = await _baseFixture.BuildPackageAsync(genesisSnapReleaseBuilder);

            var anyOs = SnapOs.AnyOs;

            Assert.NotNull(anyOs);

            var loggerMock = new Mock <ILog>();

            var progressSource = new Mock <ISnapProgressSource>();

            progressSource.
            Setup(x => x.Raise(It.IsAny <int>()));

            var failedRunAsyncReturnValues = new List <(int exitCode, string stdOut)>();

            var snapOsProcessManager = new Mock <ISnapOsProcessManager>();

            snapOsProcessManager
            .Setup(x => x.RunAsync(It.IsAny <ProcessStartInfoBuilder>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync((ProcessStartInfoBuilder builder, CancellationToken cancellationToken) =>
            {
                var result = _snapOsProcessManager.RunAsync(builder, cancellationToken).GetAwaiter().GetResult();
                if (result.exitCode != 0)
                {
                    failedRunAsyncReturnValues.Add(result);
                }
                return(result);
            });
            snapOsProcessManager
            .Setup(x => x.StartNonBlocking(It.IsAny <ProcessStartInfoBuilder>()))
            .Returns((ProcessStartInfoBuilder builder) => _snapOsProcessManager.StartNonBlocking(builder));
            snapOsProcessManager
            .Setup(x => x.ChmodExecuteAsync(It.IsAny <string>(), It.IsAny <CancellationToken>()))
            .Returns((string filename, CancellationToken cancellationToken) => _snapOsProcessManager.ChmodExecuteAsync(filename, cancellationToken));
            _snapOsMock
            .Setup(x => x.Filesystem)
            .Returns(_snapFilesystem);
            _snapOsMock
            .Setup(x => x.ProcessManager)
            .Returns(snapOsProcessManager.Object);
            _snapOsMock
            .Setup(x => x.CreateShortcutsForExecutableAsync(
                       It.IsAny <SnapOsShortcutDescription>(),
                       It.IsAny <ILog>(),
                       It.IsAny <CancellationToken>()))
            .Returns(Task.CompletedTask);

            using var baseDirectory = _baseFixture.WithDisposableTempDirectory(_snapFilesystem);
            using var installCts    = new CancellationTokenSource();
            var snapCurrentChannel = genesisPackageContext.FullPackageSnapApp.GetCurrentChannelOrThrow();

            await _snapInstaller.InstallAsync(
                genesisPackageContext.FullPackageAbsolutePath,
                baseDirectory.WorkingDirectory,
                genesisPackageContext.FullPackageSnapRelease,
                snapCurrentChannel,
                progressSource.Object,
                loggerMock.Object,
                installCts.Token);

            var appDirectory          = _snapFilesystem.PathCombine(baseDirectory.WorkingDirectory, $"app-{genesisPackageContext.FullPackageSnapApp.Version}");
            var snapAppUpdated        = appDirectory.GetSnapAppFromDirectory(_snapFilesystem, _snapAppReader);
            var snapAppUpdatedChannel = snapAppUpdated.GetCurrentChannelOrThrow();

            Assert.Equal(snapCurrentChannel.Name, snapAppUpdatedChannel.Name);

#if !PLATFORM_WINDOWS || NETFULLFRAMEWORK
            // TODO: ENABLE ME! Unable to run a dotnet 2.2 (shared) executable on Windows.
            Assert.Empty(failedRunAsyncReturnValues);
#endif

            var coreRunExe = _snapFilesystem.PathCombine(baseDirectory.WorkingDirectory,
                                                         _snapEmbeddedResources.GetCoreRunExeFilenameForSnapApp(genesisPackageContext.FullPackageSnapApp));
            var appExe = _snapFilesystem.PathCombine(baseDirectory.WorkingDirectory,
                                                     $"app-{genesisSnapReleaseBuilder.SnapApp.Version}", genesisSnapReleaseBuilder.CoreRunExe);
            var snapInstalledArguments = $"--snapx-installed {genesisPackageContext.FullPackageSnapApp.Version.ToNormalizedString()}";
            var snapFirstRunArguments  = $"--snapx-first-run {genesisPackageContext.FullPackageSnapApp.Version.ToNormalizedString()}";

            progressSource.Verify(x => x.Raise(It.Is <int>(v => v == 100)), Times.Once);

            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                snapOsProcessManager.Verify(x => x.ChmodExecuteAsync(
                                                It.IsAny <string>(), It.IsAny <CancellationToken>()), Times.Exactly(2));
                snapOsProcessManager.Verify(x => x.ChmodExecuteAsync(
                                                It.Is <string>(v => v == coreRunExe), It.Is <CancellationToken>(v => v == installCts.Token)), Times.Once);
                snapOsProcessManager.Verify(x => x.ChmodExecuteAsync(
                                                It.Is <string>(v => v == appExe), It.Is <CancellationToken>(v => v == installCts.Token)), Times.Once);
            }

            _snapOsMock.Verify(x => x.KillAllProcessesInsideDirectory(
                                   It.Is <string>(v => v == baseDirectory.WorkingDirectory)), Times.Once);

            _snapOsMock.Verify(x => x.CreateShortcutsForExecutableAsync(
                                   It.IsAny <SnapOsShortcutDescription>(), It.IsAny <ILog>(),
                                   It.IsAny <CancellationToken>()), Times.Once);

            _snapOsMock.Verify(x => x.CreateShortcutsForExecutableAsync(
                                   It.Is <SnapOsShortcutDescription>(v => v.ExeAbsolutePath == coreRunExe),
                                   It.Is <ILog>(v => v != null), It.Is <CancellationToken>(v => v == installCts.Token)), Times.Once);

            snapOsProcessManager.Verify(x => x.RunAsync(
                                            It.IsAny <ProcessStartInfoBuilder>(), It.IsAny <CancellationToken>()), Times.Once);

            snapOsProcessManager.Verify(x => x.RunAsync(
                                            It.Is <ProcessStartInfoBuilder>(v => v.Filename == appExe &&
                                                                            v.Arguments == snapInstalledArguments),
                                            It.Is <CancellationToken>(v => v == installCts.Token)), Times.Once);

            snapOsProcessManager.Verify(x => x.StartNonBlocking(
                                            It.Is <ProcessStartInfoBuilder>(v => v.Filename == appExe
                                                                            & v.Arguments == snapFirstRunArguments)), Times.Once);
            snapOsProcessManager.Verify(x => x.StartNonBlocking(
                                            It.IsAny <ProcessStartInfoBuilder>()), Times.Once);
        }
Esempio n. 24
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);
        }