/// <summary>
        /// Enable package retention and update settings.
        /// </summary>
        public static async Task <bool> UpdateRetentionSettings(SleetContext context, int stableVersionMax, int prereleaseVersionMax)
        {
            var exitCode = true;
            var log      = context.Log;

            var feedSettings = context.SourceSettings;

            feedSettings.RetentionMaxStableVersions     = stableVersionMax;
            feedSettings.RetentionMaxPrereleaseVersions = prereleaseVersionMax;

            await FeedSettingsUtility.SaveSettings(context.Source, feedSettings, log, context.Token);

            // Commit changes to source
            exitCode = await context.Source.Commit(log, context.Token);

            if (exitCode)
            {
                await log.LogAsync(LogLevel.Minimal, $"Successfully updated package retention settings. Stable: {stableVersionMax} Prerelease: {prereleaseVersionMax}.");

                await log.LogAsync(LogLevel.Minimal, $"Run prune to apply the new settings and remove packages from the feed.");
            }
            else
            {
                await log.LogAsync(LogLevel.Error, "Failed to update package retention settings.");
            }

            return(exitCode);
        }
示例#2
0
        /// <summary>
        /// Build pipeline steps.
        /// </summary>
        private static List <SleetStep> GetSteps(SleetContext context)
        {
            var result = new List <SleetStep>();

            var catalog = new SleetStep(GetCatalogService(context), SleetPerfGroup.Catalog);

            result.Add(catalog);

            if (context.SourceSettings.SymbolsEnabled)
            {
                result.Add(new SleetStep(new Symbols(context), SleetPerfGroup.Symbols));
            }

            result.Add(new SleetStep(new FlatContainer(context), SleetPerfGroup.FlatContainer));
            result.Add(new SleetStep(new AutoComplete(context), SleetPerfGroup.AutoComplete));
            result.Add(new SleetStep(new PackageIndex(context), SleetPerfGroup.PackageIndex));

            // Registration depends on catalog pages
            var registrations = new SleetStep(new Registrations(context), SleetPerfGroup.Registration, catalog);

            result.Add(registrations);

            // Search depends on registation files
            var search = new SleetStep(new Search(context), SleetPerfGroup.Search, registrations);

            result.Add(search);

            return(result);
        }
示例#3
0
        /// <summary>
        /// Update all feed badges
        /// </summary>
        public static Task UpdateBadges(SleetContext context, ISet <PackageIdentity> before, ISet <PackageIdentity> after)
        {
            var stable = GetChanges(before, after, preRel: false);
            var pre    = GetChanges(before, after, preRel: true);

            return(Task.WhenAll(UpdateBadges(context, stable, preRel: false), UpdateBadges(context, pre, preRel: true)));
        }
示例#4
0
        public static Uri GetIconPath(SleetContext context, PackageIdentity package)
        {
            var id      = package.Id;
            var version = package.Version.ToIdentityString();

            return(context.Source.GetPath($"/flatcontainer/{id}/{version}/icon".ToLowerInvariant()));
        }
示例#5
0
        /// <summary>
        /// Run prune
        /// 1. Lock the feed
        /// 2. Verify client compat
        /// 3. Prune packges
        /// 4. Commit
        /// </summary>
        public static async Task <bool> RunAsync(LocalSettings settings, ISleetFileSystem source, RetentionPruneCommandContext pruneContext, ILogger log)
        {
            var exitCode = true;

            log.LogMinimal($"Pruning packages in {source.BaseURI}");
            var token = CancellationToken.None;

            // Check if already initialized
            using (var feedLock = await SourceUtility.VerifyInitAndLock(settings, source, "Prune", log, token))
            {
                // Validate source
                await UpgradeUtility.EnsureCompatibility(source, log, token);

                // Get sleet.settings.json
                var sourceSettings = await FeedSettingsUtility.GetSettingsOrDefault(source, log, token);

                // Settings context used for all operations
                var context = new SleetContext()
                {
                    LocalSettings  = settings,
                    SourceSettings = sourceSettings,
                    Log            = log,
                    Source         = source,
                    Token          = token
                };

                exitCode = await PrunePackages(context, pruneContext);
            }

            return(exitCode);
        }
示例#6
0
        public static IReadOnlyList <ISleetService> GetServices(SleetContext context)
        {
            // Order is important here
            // Packages must be added to flat container, then the catalog, then registrations.
            var services = new List <ISleetService>
            {
                new FlatContainer(context)
            };

            if (context.SourceSettings.CatalogEnabled)
            {
                // Catalog on disk
                services.Add(new Catalog(context));
            }
            else
            {
                // In memory catalog
                services.Add(new VirtualCatalog(context));
            }

            services.Add(new Registrations(context));
            services.Add(new AutoComplete(context));
            services.Add(new Search(context));
            services.Add(new PackageIndex(context));

            // Symbols
            if (context.SourceSettings.SymbolsEnabled)
            {
                services.Add(new Symbols(context));
            }

            return(services);
        }
示例#7
0
 /// <summary>
 /// Update feed badges for stable or prerelease
 /// </summary>
 public static async Task UpdateBadges(SleetContext context, ISet <PackageIdentity> updates, bool preRel)
 {
     foreach (var package in updates)
     {
         await UpdateOrRemoveBadge(context, package, preRel);
     }
 }
示例#8
0
        /// <summary>
        /// Prune feed packages without committing
        /// </summary>
        public static async Task <HashSet <PackageIdentity> > PrunePackagesNoCommit(SleetContext context, RetentionPruneCommandContext pruneContext)
        {
            var packageIndex        = new PackageIndex(context);
            var existingPackageSets = await packageIndex.GetPackageSetsAsync();

            var allPackages = await RetentionUtility.ResolvePackageSets(existingPackageSets);

            var stableMax = pruneContext.StableVersionMax == null ? context.SourceSettings.RetentionMaxStableVersions : pruneContext.StableVersionMax;
            var prerelMax = pruneContext.PrereleaseVersionMax == null ? context.SourceSettings.RetentionMaxPrereleaseVersions : pruneContext.PrereleaseVersionMax;

            if (stableMax == null || stableMax < 1)
            {
                throw new ArgumentException("Package retention must specify a maximum number of stable versions that is > 0");
            }

            if (prerelMax == null || prerelMax < 1)
            {
                throw new ArgumentException("Package retention must specify a maximum number of prerelease versions that is > 0");
            }

            if (pruneContext.PackageIds?.Count > 0)
            {
                allPackages.RemoveWhere(e => !pruneContext.PackageIds.Contains(e.Id));
            }

            var toPrune = RetentionUtility.GetPackagesToPrune(allPackages, pruneContext.PinnedPackages, (int)stableMax, (int)prerelMax);

            await RemovePackages(context, existingPackageSets, toPrune, pruneContext.DryRun, context.Log);

            return(toPrune);
        }
        /// <summary>
        /// Remove package retention settings.
        /// </summary>
        public static async Task <bool> DisableRetention(SleetContext context)
        {
            var exitCode = true;
            var log      = context.Log;

            var feedSettings = context.SourceSettings;

            feedSettings.RetentionMaxStableVersions     = null;
            feedSettings.RetentionMaxPrereleaseVersions = null;

            await FeedSettingsUtility.SaveSettings(context.Source, feedSettings, log, context.Token);

            // Commit changes to source
            exitCode = await context.Source.Commit(log, context.Token);

            if (exitCode)
            {
                await log.LogAsync(LogLevel.Minimal, "Package retention disabled.");
            }
            else
            {
                await log.LogAsync(LogLevel.Error, "Failed to disable retention settings.");
            }

            return(exitCode);
        }
示例#10
0
        public Catalog(SleetContext context, Uri catalogBaseURI)
        {
            _context = context;

            var fileSystemBase = (FileSystemBase)context.Source;

            CatalogBaseURI = fileSystemBase.FeedSubPath == null ? catalogBaseURI : UriUtility.GetPath(context.Source.BaseURI, fileSystemBase.FeedSubPath, "catalog/");
        }
示例#11
0
        /// <summary>
        /// Update all feed badges
        /// </summary>
        public static async Task UpdateBadges(SleetContext context, ISet <PackageIdentity> before, ISet <PackageIdentity> after)
        {
            var stable = GetChanges(before, after, preRel: false);
            var pre    = GetChanges(before, after, preRel: true);

            await UpdateBadges(context, stable, preRel : false);
            await UpdateBadges(context, pre, preRel : true);
        }
示例#12
0
        /// <summary>
        /// Returns the nupkg URI. This will be different for Symbols packages.
        /// </summary>
        public Uri GetNupkgUri(SleetContext context)
        {
            if (IsSymbolsPackage)
            {
                return(context.Source.Get(SymbolsIndexUtility.GetSymbolsNupkgPath(Identity)).EntityUri);
            }

            return(FlatContainer.GetNupkgPath(context, Identity));
        }
示例#13
0
        public static Dictionary <string, IFeedSettingHandler> GetSettingHandlers(SleetContext context)
        {
            var search = new ExternalSearchHandler(context);

            return(new Dictionary <string, IFeedSettingHandler>()
            {
                { search.Name, search }
            });
        }
示例#14
0
        /// <summary>
        /// Add a package to all services.
        /// </summary>
        public static async Task AddPackage(SleetContext context, PackageInput package)
        {
            var services = GetServices(context);

            foreach (var service in services)
            {
                await service.AddPackage(package);
            }
        }
示例#15
0
        /// <summary>
        /// Remove a package from all services.
        /// </summary>
        public static async Task RemovePackage(SleetContext context, PackageIdentity package)
        {
            var services = GetServices(context);

            foreach (var service in services)
            {
                await service.RemovePackage(package);
            }
        }
示例#16
0
        private static async Task RemovePackages(bool force, ILogger log, SleetContext context, PackageSets existingPackageSets, HashSet <PackageIdentity> packages)
        {
            var toRemove        = new HashSet <PackageIdentity>();
            var toRemoveSymbols = new HashSet <PackageIdentity>();

            foreach (var package in packages)
            {
                var exists        = existingPackageSets.Packages.Exists(package);
                var symbolsExists = existingPackageSets.Symbols.Exists(package);

                if (!exists && !symbolsExists)
                {
                    log.LogInformation($"{package.ToString()} does not exist.");

                    if (force)
                    {
                        // ignore failures
                        continue;
                    }
                    else
                    {
                        throw new InvalidOperationException($"Package does not exists: {package.ToString()}");
                    }
                }

                if (exists)
                {
                    toRemove.Add(package);
                }

                if (symbolsExists)
                {
                    toRemoveSymbols.Add(package);
                }

                var message = $"Removing {package.ToString()}";

                if (exists && symbolsExists)
                {
                    message = $"Removing {package.ToString()} and symbols package for {package.ToString()}";
                }
                else if (symbolsExists)
                {
                    message = $"Removing symbols package {package.ToString()}";
                }

                await log.LogAsync(LogLevel.Information, message);
            }

            // Update feed
            await log.LogAsync(LogLevel.Information, "Removing packages from feed locally");

            // Add/Remove packages
            var changeContext = SleetOperations.CreateDelete(existingPackageSets, toRemove, toRemoveSymbols);
            await SleetUtility.ApplyPackageChangesAsync(context, changeContext);
        }
示例#17
0
        /// <summary>
        /// Lock the feed and delete all packages given.
        /// </summary>
        public static async Task <bool> DeletePackagesAsync(LocalSettings settings, ISleetFileSystem source, IEnumerable <PackageIdentity> packagesToDelete, string reason, bool force, ILogger log)
        {
            var success = true;

            var token = CancellationToken.None;

            log.LogMinimal($"Reading feed {source.BaseURI.AbsoluteUri}");

            // Check if already initialized
            using (var feedLock = await SourceUtility.VerifyInitAndLock(settings, source, "Delete", log, token))
            {
                // Validate source
                await SourceUtility.ValidateFeedForClient(source, log, token);

                // Get sleet.settings.json
                var sourceSettings = await FeedSettingsUtility.GetSettingsOrDefault(source, log, token);

                // Settings context used for all operations
                var context = new SleetContext()
                {
                    LocalSettings  = settings,
                    SourceSettings = sourceSettings,
                    Log            = log,
                    Source         = source,
                    Token          = token
                };

                var packageIndex        = new PackageIndex(context);
                var existingPackageSets = await packageIndex.GetPackageSetsAsync();

                var packages = new HashSet <PackageIdentity>(packagesToDelete);

                if (string.IsNullOrEmpty(reason))
                {
                    reason = string.Empty;
                }

                await RemovePackages(force, log, context, existingPackageSets, packages);

                // Save all
                log.LogMinimal($"Committing changes to {source.BaseURI.AbsoluteUri}");

                success &= await source.Commit(log, token);
            }

            if (success)
            {
                log.LogMinimal($"Successfully deleted packages.");
            }
            else
            {
                log.LogError($"Failed to delete packages.");
            }

            return(success);
        }
示例#18
0
        public static async Task <int> RunCore(LocalSettings settings, ISleetFileSystem source, List <string> inputs, bool force, ILogger log)
        {
            var exitCode = 0;

            var token = CancellationToken.None;
            var now   = DateTimeOffset.UtcNow;

            // Get packages
            var packages = GetPackageInputs(inputs, now, log);

            // Check if already initialized
            using (var feedLock = await SourceUtility.VerifyInitAndLock(source, log, token))
            {
                // Validate source
                await UpgradeUtility.UpgradeIfNeeded(source, log, token);

                // Get sleet.settings.json
                var sourceSettings = new SourceSettings();

                // Settings context used for all operations
                var context = new SleetContext()
                {
                    LocalSettings  = settings,
                    SourceSettings = sourceSettings,
                    Log            = log,
                    Source         = source,
                    Token          = token
                };

                var packageIndex = new PackageIndex(context);

                foreach (var package in packages)
                {
                    if (await packageIndex.Exists(package.Identity))
                    {
                        if (force)
                        {
                            log.LogInformation($"Package already exists, removing {package.ToString()}");
                            await SleetUtility.RemovePackage(context, package.Identity);
                        }
                        else
                        {
                            throw new InvalidOperationException($"Package already exists: '{package.Identity}'.");
                        }
                    }

                    log.LogInformation($"Adding {package.Identity.ToString()}");
                    await SleetUtility.AddPackage(context, package);
                }

                // Save all
                await source.Commit(log, token);
            }

            return(exitCode);
        }
示例#19
0
        /// <summary>
        /// Main entry point for updating the feed.
        /// All add/remove operations can be added to a single changeContext and applied in
        /// a single call using this method.
        /// </summary>
        public static async Task ApplyPackageChangesAsync(SleetContext context, SleetOperations changeContext)
        {
            var steps = GetSteps(context);
            var tasks = steps.Select(e => new Func <Task>(() => e.RunAsync(changeContext))).ToList();

            // Run each service on its own thread and in parallel
            // Services with depenencies will pre-fetch files that will be used later
            // and then wait until the other services have completed.
            await TaskUtils.RunAsync(tasks, useTaskRun : true, maxThreads : steps.Count, token : CancellationToken.None);
        }
示例#20
0
        /// <summary>
        /// Remove a symbols package from all services.
        /// </summary>
        public static async Task RemoveSymbolsPackage(SleetContext context, PackageIdentity package)
        {
            var services       = GetServices(context);
            var symbolsEnabled = context.SourceSettings.SymbolsEnabled;

            if (symbolsEnabled)
            {
                foreach (var symbolsService in services.Select(e => e as ISymbolsAddRemovePackages).Where(e => e != null))
                {
                    await symbolsService.RemoveSymbolsPackageAsync(package);
                }
            }
        }
示例#21
0
        /// <summary>
        /// Main entry point for updating the feed.
        /// All add/remove operations can be added to a single changeContext and applied in
        /// a single call using this method.
        /// </summary>
        public static async Task ApplyPackageChangesAsync(SleetContext context, SleetOperations changeContext)
        {
            using (var timer = PerfEntryWrapper.CreateSummaryTimer("Updated all files locally. Total time: {0}", context.PerfTracker))
            {
                var steps = GetSteps(context);
                var tasks = steps.Select(e => new Func <Task>(() => e.RunAsync(changeContext, context.PerfTracker))).ToList();

                // Run each service on its own thread and in parallel
                // Services with depenencies will pre-fetch files that will be used later
                // and then wait until the other services have completed.
                await TaskUtils.RunAsync(tasks, useTaskRun : true, maxThreads : steps.Count, token : CancellationToken.None);
            }
        }
示例#22
0
        /// <summary>
        /// Push packages to a feed.
        /// This assumes the feed is already locked.
        /// </summary>
        public static async Task <bool> PushPackages(LocalSettings settings, ISleetFileSystem source, List <PackageInput> packages, bool force, bool skipExisting, ILogger log, CancellationToken token)
        {
            var exitCode = true;
            var now      = DateTimeOffset.UtcNow;

            // Verify no duplicate packages
            CheckForDuplicates(packages);

            // Get sleet.settings.json
            await log.LogAsync(LogLevel.Minimal, "Reading feed");

            var sourceSettings = await FeedSettingsUtility.GetSettingsOrDefault(source, log, token);

            // Settings context used for all operations
            var context = new SleetContext()
            {
                LocalSettings  = settings,
                SourceSettings = sourceSettings,
                Log            = log,
                Source         = source,
                Token          = token,
                PerfTracker    = source.LocalCache.PerfTracker
            };

            await log.LogAsync(LogLevel.Verbose, "Reading existing package index");

            // Push
            var packageIndex        = new PackageIndex(context);
            var existingPackageSets = await packageIndex.GetPackageSetsAsync();

            await PushPackages(packages, context, existingPackageSets, force, skipExisting, log);

            // Prune packages
            await PrunePackages(packages, context);

            // Save all
            await log.LogAsync(LogLevel.Minimal, $"Committing changes to {source.BaseURI.AbsoluteUri}");

            await source.Commit(log, token);

            if (exitCode)
            {
                await log.LogAsync(LogLevel.Minimal, "Successfully pushed packages.");
            }
            else
            {
                await log.LogAsync(LogLevel.Error, "Failed to push packages.");
            }

            return(exitCode);
        }
示例#23
0
 public static IReadOnlyList <ISleetService> GetServices(SleetContext context)
 {
     // Order is important here
     // Packages must be added to flat container, then the catalog, then registrations.
     return(new List <ISleetService>()
     {
         new FlatContainer(context),
         new Catalog(context),
         new Registrations(context),
         new AutoComplete(context),
         new Search(context),
         new PackageIndex(context),
     });
 }
示例#24
0
 private static async Task PrunePackages(List <PackageInput> packages, SleetContext context)
 {
     // Run only if the package retention settings are present for the feed
     if (context.SourceSettings.RetentionMaxStableVersions > 0 && context.SourceSettings.RetentionMaxPrereleaseVersions > 0)
     {
         // Prune if prune is enabled for the feed
         var pruneContext = new RetentionPruneCommandContext();
         // Do not prune packages that were just pushed
         pruneContext.PinnedPackages.UnionWith(packages.Select(e => e.Identity));
         // Only prune package ids that were pushed
         pruneContext.PackageIds.UnionWith(packages.Select(e => e.Identity.Id));
         // Run prune against the local files
         await RetentionPruneCommand.PrunePackagesNoCommit(context, pruneContext);
     }
 }
示例#25
0
        /// <summary>
        /// Returns the nupkg icon URI..
        /// </summary>
        public Uri GetIconUri(SleetContext context)
        {
            Uri result = null;

            // Ignore symbol packages, icons from these are not used
            if (!IsSymbolsPackage)
            {
                // Ignore legacy iconURL nuspec properties
                if (!string.IsNullOrEmpty(Nuspec.GetIcon()))
                {
                    result = FlatContainer.GetIconPath(context, Identity);
                }
            }

            return(result);
        }
示例#26
0
        private static async Task <bool> CreatePackageIndexAsync(SleetContext context, JObject serviceIndexJson)
        {
            var packageIndex = context.Source.Get("sleet.packageindex.json");

            AddServiceIndexEntry(context.Source.BaseURI, "sleet.packageindex.json", "http://schema.emgarten.com/sleet#PackageIndex/1.0.0", "Sleet package index.", serviceIndexJson);

            if (!await packageIndex.Exists(context.Log, context.Token))
            {
                var index = new PackageIndex(context);
                await index.InitAsync();

                return(true);
            }

            return(false);
        }
示例#27
0
        /// <summary>
        /// Retrieve the catalog service based on the settings.
        /// </summary>
        private static ISleetService GetCatalogService(SleetContext context)
        {
            ISleetService catalog = null;

            if (context.SourceSettings.CatalogEnabled)
            {
                // Full catalog that is written to the feed
                catalog = new Catalog(context);
            }
            else
            {
                // In memory catalog that is not written to the feed
                catalog = new VirtualCatalog(context);
            }

            return(catalog);
        }
示例#28
0
        /// <summary>
        /// Write or remove badge file from the feed.
        /// </summary>
        public static async Task UpdateOrRemoveBadge(SleetContext context, PackageIdentity package, bool preRel)
        {
            var svgPath  = $"badges/{(preRel ? "vpre" : "v")}/{package.Id}.svg".ToLowerInvariant();
            var jsonPath = $"badges/{(preRel ? "vpre" : "v")}/{package.Id}.json".ToLowerInvariant();

            var svgFile  = context.Source.Get(svgPath);
            var jsonFile = context.Source.Get(jsonPath);

            // If the identity doesn't have it version then it should be removed
            if (package.HasVersion)
            {
                await jsonFile.Write(GetJsonBadge(package), context.Log, context.Token);
            }
            else
            {
                svgFile.Delete(context.Log, context.Token);
                jsonFile.Delete(context.Log, context.Token);
            }
        }
示例#29
0
        public static async Task <int> RunCore(LocalSettings settings, ISleetFileSystem source, ILogger log)
        {
            var exitCode = 0;

            log.LogMinimal($"Stats for {source.BaseURI}");

            var token = CancellationToken.None;

            // Check if already initialized
            using (var feedLock = await SourceUtility.VerifyInitAndLock(source, log, token))
            {
                // Validate source
                await UpgradeUtility.UpgradeIfNeeded(source, log, token);

                // Get sleet.settings.json
                var sourceSettings = new SourceSettings();

                // Settings context used for all operations
                var context = new SleetContext()
                {
                    LocalSettings  = settings,
                    SourceSettings = sourceSettings,
                    Log            = log,
                    Source         = source,
                    Token          = token
                };

                var catalog = new Catalog(context);

                var existingEntries = await catalog.GetExistingPackagesIndex();

                var packages  = existingEntries.Select(e => e.PackageIdentity).ToList();
                var uniqueIds = packages.Select(e => e.Id).Distinct(StringComparer.OrdinalIgnoreCase);

                var catalogEntries = await catalog.GetIndexEntries();

                log.LogMinimal($"Catalog entries: {catalogEntries.Count}");
                log.LogMinimal($"Packages: {existingEntries.Count}");
                log.LogMinimal($"Unique package ids: {uniqueIds.Count()}");
            }

            return(exitCode);
        }
示例#30
0
        public static async Task <bool> RunAsync(LocalSettings settings, ISleetFileSystem source, ILogger log)
        {
            var exitCode = true;

            log.LogMinimal($"Stats for {source.BaseURI}");

            var token = CancellationToken.None;

            // Check if already initialized
            using (var feedLock = await SourceUtility.VerifyInitAndLock(settings, source, "Stats", log, token))
            {
                // Validate source
                await UpgradeUtility.EnsureFeedVersionMatchesTool(source, log, token);

                // Get sleet.settings.json
                var sourceSettings = await FeedSettingsUtility.GetSettingsOrDefault(source, log, token);

                // Settings context used for all operations
                var context = new SleetContext()
                {
                    LocalSettings  = settings,
                    SourceSettings = sourceSettings,
                    Log            = log,
                    Source         = source,
                    Token          = token
                };

                var packageIndex        = new PackageIndex(context);
                var existingPackageSets = await packageIndex.GetPackageSetsAsync();

                var uniqueIds = existingPackageSets.Packages.Index
                                .Concat(existingPackageSets.Symbols.Index)
                                .Select(e => e.Id).Distinct(StringComparer.OrdinalIgnoreCase);

                log.LogMinimal($"Packages: {existingPackageSets.Packages.Index.Count}");
                log.LogMinimal($"Symbols Packages: {existingPackageSets.Symbols.Index.Count}");
                log.LogMinimal($"Unique package ids: {uniqueIds.Count()}");
            }

            return(exitCode);
        }