Exemplo n.º 1
0
        internal async Task <BuildPackageContext> BuildPackageAsync([NotNull] SnapReleaseBuilder releaseBuilder, CancellationToken cancellationToken = default)
        {
            if (releaseBuilder == null)
            {
                throw new ArgumentNullException(nameof(releaseBuilder));
            }

            var(coreRunMemoryStream, _, _) =
                releaseBuilder.SnapEmbeddedResources.GetCoreRunForSnapApp(releaseBuilder.SnapApp, releaseBuilder.SnapFilesystem, releaseBuilder.CoreRunLib);
            coreRunMemoryStream.Dispose();

            var nuspecBaseDirectory = releaseBuilder.SnapFilesystem.PathCombine(releaseBuilder.NugetPackagingDirectory, "content");

            releaseBuilder.SnapFilesystem.DirectoryCreate(nuspecBaseDirectory);

            var snapPackDetails = new SnapPackageDetails
            {
                NuspecBaseDirectory = nuspecBaseDirectory,
                PackagesDirectory   = releaseBuilder.SnapAppPackagesDirectory,
                SnapApp             = releaseBuilder.SnapApp,
                SnapAppsReleases    = releaseBuilder.SnapAppsReleases
            };

            foreach (var kv in releaseBuilder.GetNuspecItems())
            {
                var targetPath           = kv.Key;
                var value                = kv.Value;
                var destinationFilename  = releaseBuilder.SnapFilesystem.PathCombine(snapPackDetails.NuspecBaseDirectory, targetPath);
                var destinationDirectory = releaseBuilder.SnapFilesystem.PathGetDirectoryName(destinationFilename);
                releaseBuilder.SnapFilesystem.DirectoryCreateIfNotExists(destinationDirectory);

                switch (value)
                {
                case AssemblyDefinition assemblyDefinition:
                    assemblyDefinition.Write(destinationFilename);
                    break;

                case MemoryStream memoryStream:
                    await releaseBuilder.SnapFilesystem.FileWriteAsync(memoryStream, destinationFilename, cancellationToken);

                    memoryStream.Seek(0, SeekOrigin.Begin);
                    break;

                default:
                    throw new NotSupportedException($"{targetPath}: {value?.GetType().FullName}");
                }
            }

            var(fullPackageMemoryStream, fullSnapApp, fullPackageSnapRelease, deltaPackageMemoryStream, deltaSnapApp, deltaSnapRelease) = await releaseBuilder.SnapPack
                                                                                                                                          .BuildPackageAsync(snapPackDetails, releaseBuilder.CoreRunLib, cancellationToken);

            var fullPackageAbsolutePath = await releaseBuilder.WritePackageAsync(fullPackageMemoryStream, fullPackageSnapRelease, cancellationToken);

            string deltaPackageAbsolutePath = null;

            if (deltaSnapApp != null &&
                deltaPackageMemoryStream != null &&
                deltaSnapRelease != null)
            {
                deltaPackageAbsolutePath = await releaseBuilder.WritePackageAsync(deltaPackageMemoryStream, deltaSnapRelease, cancellationToken);
            }

            return(new BuildPackageContext
            {
                FullPackageMemoryStream = fullPackageMemoryStream,
                FullPackageSnapApp = fullSnapApp,
                FullPackageSnapRelease = fullPackageSnapRelease,
                FullPackageAbsolutePath = fullPackageAbsolutePath,
                DeltaPackageMemoryStream = deltaPackageMemoryStream,
                DeltaPackageSnapApp = deltaSnapApp,
                DeltaPackageSnapRelease = deltaSnapRelease,
                DeltaPackageAbsolutePath = deltaPackageAbsolutePath
            });
        }
Exemplo n.º 2
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);
        }