예제 #1
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)..];
예제 #2
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);
        }
예제 #3
0
        async Task InvokePostInstall(SnapApp snapApp, NuspecReader nuspecReader,
                                     string baseDirectory, string appDirectory, SemanticVersion currentVersion,
                                     bool isInitialInstall, ILog logger = null, CancellationToken cancellationToken = default)
        {
            var chmod = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

            var coreRunExeAbsolutePath = _snapOs.Filesystem
                                         .PathCombine(baseDirectory, _snapEmbeddedResources.GetCoreRunExeFilenameForSnapApp(snapApp));
            var mainExeAbsolutePath = _snapOs.Filesystem
                                      .PathCombine(appDirectory, _snapEmbeddedResources.GetCoreRunExeFilenameForSnapApp(snapApp));
            var iconAbsolutePath = !RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && snapApp.Target.Icon != null?
                                   _snapOs.Filesystem.PathCombine(appDirectory, snapApp.Target.Icon) : null;

            var snapChannel = snapApp.GetCurrentChannelOrThrow();

            logger?.Debug($"{nameof(coreRunExeAbsolutePath)}: {coreRunExeAbsolutePath}");
            logger?.Debug($"{nameof(mainExeAbsolutePath)}: {mainExeAbsolutePath}");
            logger?.Debug($"{nameof(iconAbsolutePath)}: {iconAbsolutePath}");

            async Task ChmodAsync(string exePath)
            {
                if (exePath == null)
                {
                    throw new ArgumentNullException(nameof(exePath));
                }
                logger?.Info($"Attempting to change file permission for executable: {exePath}.");
                var chmodSuccess = await _snapOs.ProcessManager.ChmodExecuteAsync(exePath, cancellationToken);

                logger?.Info($"Permissions changed successfully: {(chmodSuccess ? "true" : "false")}.");
            }

            if (chmod)
            {
                await ChmodAsync(coreRunExeAbsolutePath);
                await ChmodAsync(mainExeAbsolutePath);
            }

            var coreRunExeFilename = _snapOs.Filesystem.PathGetFileName(coreRunExeAbsolutePath);

            if (!snapApp.Target.Shortcuts.Any())
            {
                logger?.Warn("This application does not specify any shortcut locations.");
            }
            else
            {
                var shortcutLocations = snapApp.Target.Shortcuts.First();
                snapApp.Target.Shortcuts.Skip(1).ForEach(x => shortcutLocations |= x);
                logger?.Info($"Shortcuts will be created in the following locations: {string.Join(", ", shortcutLocations)}");
                try
                {
                    var shortcutDescription = new SnapOsShortcutDescription
                    {
                        SnapApp           = snapApp,
                        UpdateOnly        = isInitialInstall == false,
                        NuspecReader      = nuspecReader,
                        ShortcutLocations = shortcutLocations,
                        ExeAbsolutePath   = coreRunExeAbsolutePath,
                        IconAbsolutePath  = iconAbsolutePath
                    };

                    await _snapOs.CreateShortcutsForExecutableAsync(shortcutDescription, logger, cancellationToken);
                }
                catch (Exception e)
                {
                    logger?.ErrorException($"Exception thrown while creating shortcut for exe: {coreRunExeFilename}", e);
                }
            }

            var snapAppDllAbsolutePath = _snapOs.Filesystem.PathCombine(appDirectory, SnapConstants.SnapAppDllFilename);

            try
            {
                logger?.Info($"Updating {snapAppDllAbsolutePath}. Current channel is: {snapChannel.Name}.");

                using var snapAppDllAssemblyDefinition = _snapAppWriter.BuildSnapAppAssembly(snapApp);
                using var snapAPpDllDestinationStream  = _snapOs.Filesystem.FileWrite(snapAppDllAbsolutePath);
                snapAppDllAssemblyDefinition.Write(snapAPpDllDestinationStream);
            }
            catch (Exception e)
            {
                logger?.ErrorException($"Unknown error updating {snapAppDllAbsolutePath}", e);
            }

            var allSnapAwareApps = new List <string>
            {
                mainExeAbsolutePath
            }.Select(x =>
            {
                var installOrUpdateTxt = isInitialInstall ? "--snapx-installed" : "--snapx-updated";
                return(new ProcessStartInfoBuilder(x)
                       .Add(installOrUpdateTxt)
                       .Add(currentVersion.ToNormalizedString()));
            })
            .ToList();

            await InvokeSnapApps(allSnapAwareApps, TimeSpan.FromSeconds(15), isInitialInstall, currentVersion, logger, cancellationToken);
        }