Esempio n. 1
0
        public override void Log(ILogMessage message)
        {
            switch (message.Level)
            {
            case LogLevel.Verbose: _logger.Detailed(message.Message);
                break;

            case LogLevel.Debug: _logger.Detailed(message.Message);
                break;

            case LogLevel.Information: _logger.Detailed(message.Message);
                break;

            case LogLevel.Minimal: _logger.Normal(message.Message);
                break;

            case LogLevel.Warning: _logger.Normal(message.Message);
                break;

            case LogLevel.Error:
                _logger.Error(message.Message);
                break;

            default:
                throw new ArgumentOutOfRangeException($"Invalid log level {message.Level}");
            }
        }
        public async Task Invoke(PackageInProject currentPackage,
                                 NuGetVersion newVersion, PackageSource packageSource, NuGetSources allSources)
        {
            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                _logger.Normal("Cannot run NuGet.exe package update as OS Platform is not Windows");
                return;
            }

            var dirName = currentPackage.Path.Info.DirectoryName;

            var nuget = NuGetPath.FindExecutable();

            if (string.IsNullOrWhiteSpace(nuget))
            {
                _logger.Normal("Cannot find NuGet exe for package update");
                return;
            }

            var sources   = allSources.CommandLine("-Source");
            var arguments = $"update packages.config -Id {currentPackage.Id} -Version {newVersion} {sources}";

            _logger.Detailed(arguments);

            await _externalProcess.Run(dirName, nuget, arguments, true);
        }
Esempio n. 3
0
        public async Task Invoke(FileInfo file, NuGetSources sources)
        {
            _logger.Normal($"Nuget restore on {file.DirectoryName} {file.Name}");

            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                _logger.Normal("Cannot run NuGet.exe file restore as OS Platform is not Windows");
                return;
            }

            var nuget = NuGetPath.FindExecutable();

            if (string.IsNullOrWhiteSpace(nuget))
            {
                _logger.Normal("Cannot find NuGet exe for solution restore");
                return;
            }

            var sourcesCommandLine = sources.CommandLine("-Source");

            var arguments = $"restore {file.Name} {sourcesCommandLine}";

            _logger.Detailed($"{nuget} {arguments}");

            var processOutput = await _externalProcess.Run(file.DirectoryName, nuget, arguments, ensureSuccess : false);

            if (processOutput.Success)
            {
                _logger.Detailed($"Nuget restore on {file.Name} complete");
            }
            else
            {
                _logger.Detailed($"Nuget restore failed on {file.DirectoryName} {file.Name}:\n{processOutput.Output}\n{processOutput.ErrorOutput}");
            }
        }
Esempio n. 4
0
        private async Task <IReadOnlyCollection <PackageUpdateSet> > ApplyFilters(
            IReadOnlyCollection <PackageUpdateSet> all,
            Func <PackageUpdateSet, Task <bool> > remoteCheck)
        {
            var filteredByInOut = FilteredByIncludeExclude(all);

            var filteredLocally = filteredByInOut
                                  .Where(MatchesMinAge)
                                  .ToList();

            if (filteredLocally.Count < filteredByInOut.Count)
            {
                var agoFormat = TimeSpanFormat.Ago(_settings.MinimumAge);
                _logger.Normal($"Filtered by minimum package age '{agoFormat}' from {filteredByInOut.Count} to {filteredLocally.Count}");
            }

            var remoteFiltered = await ApplyRemoteFilter(filteredLocally, remoteCheck);

            if (remoteFiltered.Count < filteredLocally.Count)
            {
                _logger.Normal($"Filtered by remote branch check branch from {filteredLocally.Count} to {remoteFiltered.Count}");
            }

            return(remoteFiltered);
        }
Esempio n. 5
0
        private PackageInProject XmlToPackage(XElement el, PackagePath path, XNamespace ns)
        {
            try
            {
                var id      = el.Attribute("Include")?.Value;
                var version = el.Attribute("Version")?.Value ?? el.Element(ns + "Version")?.Value;

                if (string.IsNullOrWhiteSpace(version))
                {
                    _logger.Normal($"Skipping package '{id}' with no version specified.");
                    return(null);
                }

                var versionParseSuccess = NuGetVersion.TryParse(version, out var nugetVersion);
                if (!versionParseSuccess)
                {
                    _logger.Normal($"Skipping package '{id}' with version '{version}' that could not be parsed.");
                    return(null);
                }

                return(new PackageInProject(new PackageIdentity(id, nugetVersion), path));
            }
            catch (Exception ex)
            {
                _logger.Error($"Could not read package from {el} in file {path.FullName}", ex);
                return(null);
            }
        }
Esempio n. 6
0
        private ISettingsReader FindPlatformSettingsReader(
            Platform?platformFromSettings, Uri apiEndpoint)
        {
            if (platformFromSettings.HasValue)
            {
                var reader = _settingReaders
                             .FirstOrDefault(s => s.Platform == platformFromSettings.Value);

                if (reader != null)
                {
                    _nuKeeperLogger.Normal($"Collaboration platform specified as '{reader.Platform}'");
                }

                return(reader);
            }
            else
            {
                var reader = _settingReaders
                             .FirstOrDefault(s => s.CanRead(apiEndpoint));

                if (reader != null)
                {
                    _nuKeeperLogger.Normal($"Matched uri '{apiEndpoint}' to collaboration platform '{reader.Platform}'");
                }

                return(reader);
            }
        }
        public void Report(PackageLookupResult lookupResult)
        {
            var highestVersion = lookupResult.Major?.Identity?.Version;

            if (highestVersion == null)
            {
                return;
            }

            var allowing = lookupResult.AllowedChange == VersionChange.Major
                ? string.Empty
                : $" Allowing {lookupResult.AllowedChange} version updates.";

            var highestMatchVersion = lookupResult.Selected()?.Identity?.Version;

            var packageId = lookupResult.Major.Identity.Id;

            if (highestMatchVersion == null)
            {
                _logger.Normal($"Package {packageId} version {highestVersion} is available but is not allowed.{allowing}");
                return;
            }

            if (highestVersion > highestMatchVersion)
            {
                _logger.Normal($"Selected update of package {packageId} to version {highestMatchVersion}, but version {highestVersion} is also available.{allowing}");
            }
            else
            {
                _logger.Detailed($"Selected update of package {packageId} to highest version, {highestMatchVersion}.{allowing}");
            }
        }
Esempio n. 8
0
        public async Task <IReadOnlyList <Organization> > GetOrganizations()
        {
            var groups = await _client.GetAllGroups();

            _logger.Normal($"Read {groups.Count} groups");

            return(groups.Select(grp => new Organization(grp.Path ?? grp.Name)).ToList());
        }
Esempio n. 9
0
        public void Clone(Uri pullEndpoint)
        {
            _logger.Normal($"Git clone {pullEndpoint} to {WorkingFolder.FullPath}");

            Repository.Clone(pullEndpoint.AbsoluteUri, WorkingFolder.FullPath,
                             new CloneOptions
            {
                CredentialsProvider = UsernamePasswordCredentials,
                OnTransferProgress  = OnTransferProgress
            });

            _logger.Detailed("Git clone complete");
        }
Esempio n. 10
0
        public async Task Invoke(FileInfo file, NuGetSources sources)
        {
            _logger.Normal($"Nuget restore on {file.DirectoryName} {file.Name}");

            var nuget = _nuGetPath.Executable;

            if (string.IsNullOrWhiteSpace(nuget))
            {
                _logger.Normal("Cannot find NuGet.exe for solution restore");
                return;
            }

            var sourcesCommandLine = sources.CommandLine("-Source");

            var restoreCommand = $"restore {file.Name} {sourcesCommandLine}  -NonInteractive";

            ProcessOutput processOutput;

            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                if (await _monoExecutor.CanRun())
                {
                    processOutput = await _monoExecutor.Run(file.DirectoryName,
                                                            nuget,
                                                            restoreCommand,
                                                            ensureSuccess : false);
                }
                else
                {
                    _logger.Error("Cannot run NuGet.exe. It requires either Windows OS Platform or Mono installation");
                    return;
                }
            }
            else
            {
                processOutput = await _externalProcess.Run(file.DirectoryName,
                                                           nuget,
                                                           restoreCommand,
                                                           ensureSuccess : false);
            }

            if (processOutput.Success)
            {
                _logger.Detailed($"Nuget restore on {file.Name} complete");
            }
            else
            {
                _logger.Detailed(
                    $"Nuget restore failed on {file.DirectoryName} {file.Name}:\n{processOutput.Output}\n{processOutput.ErrorOutput}");
            }
        }
Esempio n. 11
0
        public void Clone(Uri pullEndpoint, string branchName)
        {
            _logger.Normal($"Git clone {pullEndpoint}, branch {branchName ?? "default"}, to {WorkingFolder.FullPath}");

            Repository.Clone(pullEndpoint.AbsoluteUri, WorkingFolder.FullPath,
                             new CloneOptions
            {
                CredentialsProvider = UsernamePasswordCredentials,
                OnTransferProgress  = OnTransferProgress,
                BranchName          = branchName
            });

            _logger.Detailed("Git clone complete");
        }
Esempio n. 12
0
        public async Task Invoke(PackageInProject currentPackage,
                                 NuGetVersion newVersion, PackageSource packageSource, NuGetSources allSources)
        {
            var projectPath = currentPackage.Path.Info.DirectoryName;

            var nuget = _nuGetPath.Executable;

            if (string.IsNullOrWhiteSpace(nuget))
            {
                _logger.Normal("Cannot find NuGet.exe for package update");
                return;
            }

            var sources       = allSources.CommandLine("-Source");
            var updateCommand = $"update packages.config -Id {currentPackage.Id} -Version {newVersion} {sources} -NonInteractive";

            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                if (await _monoExecutor.CanRun())
                {
                    await _monoExecutor.Run(projectPath, nuget, updateCommand, true);
                }
                else
                {
                    _logger.Error("Cannot run NuGet.exe. It requires either Windows OS Platform or Mono installation");
                }
            }
            else
            {
                await _externalProcess.Run(projectPath, nuget, updateCommand, true);
            }
        }
        public IEnumerable <GitRemote> GetRemotes(Uri repositoryUri)
        {
            if (!IsGitRepo(repositoryUri))
            {
                return(Enumerable.Empty <GitRemote>());
            }

            var discover = Repository.Discover(repositoryUri.AbsolutePath);

            var gitRemotes = new List <GitRemote>();

            using (var repo = new Repository(discover))
            {
                foreach (var remote in repo.Network.Remotes)
                {
                    Uri.TryCreate(remote.Url, UriKind.Absolute, out repositoryUri);

                    if (repositoryUri != null)
                    {
                        var gitRemote = new GitRemote
                        {
                            Name = remote.Name,
                            Url  = repositoryUri
                        };
                        gitRemotes.Add(gitRemote);
                    }
                    else
                    {
                        _logger.Normal($"Cannot parse {remote.Url} to URI. SSH remote is currently not supported");
                    }
                }

                return(gitRemotes);
            }
        }
Esempio n. 14
0
        private async Task <int> DoTargetUpdates(
            IGitDriver git, RepositoryData repository,
            IReadOnlyCollection <PackageUpdateSet> targetUpdates,
            NuGetSources sources,
            SettingsContainer settings)
        {
            if (targetUpdates.Count == 0)
            {
                _logger.Minimal("No updates can be applied. Exiting.");
                return(0);
            }

            await _solutionsRestore.CheckRestore(targetUpdates, settings.WorkingFolder ?? git.WorkingFolder, sources);

            var updatesDone = await _packageUpdater.MakeUpdatePullRequests(git, repository, targetUpdates, sources, settings);

            if (updatesDone < targetUpdates.Count)
            {
                _logger.Minimal($"Attempted {targetUpdates.Count} updates and did {updatesDone}");
            }
            else
            {
                _logger.Normal($"Done {updatesDone} updates");
            }

            return(updatesDone);
        }
Esempio n. 15
0
        private async Task <int> MakeUpdatePullRequests(
            IGitDriver git, RepositoryData repository,
            NuGetSources sources, SettingsContainer settings,
            IReadOnlyCollection <PackageUpdateSet> updates)
        {
            _logger.Normal(UpdatesLogger.OldVersionsToBeUpdated(updates));

            git.Checkout(repository.DefaultBranch);

            // branch
            var branchName = BranchNamer.MakeName(updates);

            _logger.Detailed($"Using branch name: '{branchName}'");
            git.CheckoutNewBranch(branchName);

            foreach (var updateSet in updates)
            {
                await _updateRunner.Update(updateSet, sources);

                var commitMessage = CommitWording.MakeCommitMessage(updateSet);
                git.Commit(commitMessage);
            }

            git.Push("nukeeper_push", branchName);

            var title = CommitWording.MakePullRequestTitle(updates);
            var body  = CommitWording.MakeCommitDetails(updates);
            await _gitHub.CreatePullRequest(repository, title, body, branchName,
                                            settings.SourceControlServerSettings.Labels);

            git.Checkout(repository.DefaultBranch);
            return(updates.Count);
        }
        private GitRemote CreateGitRemoteFromString(string remote)
        {
            var linkParser = new Regex(@"\b(?:https?://|www\.)\S+\b", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            var match      = linkParser.Match(remote);

            if (match.Success)
            {
                if (Uri.TryCreate(match.Value, UriKind.Absolute, out Uri repositoryUri))
                {
                    var remoteName = remote.Split(new [] { "\t" }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
                    if (!string.IsNullOrWhiteSpace(remoteName))
                    {
                        return(new GitRemote
                        {
                            Name = remoteName,
                            Url = repositoryUri
                        });
                    }
                }
                else
                {
                    _logger.Normal($"Cannot parse {match.Value} to URI. SSH remote is currently not supported");
                }
            }

            return(null);
        }
Esempio n. 17
0
        private async Task <(int UpdatesMade, bool?ThresholdReached)> DoTargetUpdates(
            IGitDriver git, RepositoryData repository,
            IReadOnlyCollection <PackageUpdateSet> targetUpdates,
            NuGetSources sources,
            SettingsContainer settings
            )
        {
            if (targetUpdates.Count == 0)
            {
                return(0, null);
            }

            await _solutionRestore.CheckRestore(targetUpdates, settings.WorkingFolder ?? git.WorkingFolder, sources);

            var(updatesDone, thresholdReached) = await _packageUpdater.MakeUpdatePullRequests(git, repository, targetUpdates, sources, settings);

            if (updatesDone < targetUpdates.Count)
            {
                _logger.Minimal($"Attempted {targetUpdates.Count} updates and did {updatesDone}");
            }
            else
            {
                _logger.Normal($"Done {updatesDone} updates");
            }

            return(updatesDone, thresholdReached);
        }
Esempio n. 18
0
        public async Task <int> Run(
            IGitDriver git,
            RepositoryData repository,
            SettingsContainer settings)
        {
            GitInit(git, repository);

            var sources = _nugetSourcesReader.Read(git.WorkingFolder, settings.UserSettings.NuGetSources);

            var updates = await _updateFinder.FindPackageUpdateSets(
                git.WorkingFolder, sources, settings.UserSettings.AllowedChange);

            _logger.Detailed($"Report mode is {settings.UserSettings.ReportMode}");
            switch (settings.UserSettings.ReportMode)
            {
            case ReportMode.Off:
                break;

            case ReportMode.On:
                // report and continue
                _availableUpdatesReporter.Report(repository.Pull.Name, updates);
                break;

            case ReportMode.ReportOnly:
                // report and exit
                _availableUpdatesReporter.Report(repository.Pull.Name, updates);
                _logger.Normal("Exiting after reports only");
                return(0);

            default:
                throw new Exception($"Unknown report mode: '{settings.UserSettings.ReportMode}'");
            }

            if (updates.Count == 0)
            {
                _logger.Minimal("No potential updates found. Well done. Exiting.");
                return(0);
            }

            var targetUpdates = await _updateSelection.SelectTargets(
                repository.Push, updates, settings.PackageFilters);

            return(await DoTargetUpdates(git, repository, targetUpdates,
                                         sources, settings));
        }
Esempio n. 19
0
        public async Task Clone(Uri pullEndpoint, string branchName)
        {
            _logger.Normal($"Git clone {pullEndpoint}, branch {branchName ?? "default"}, to {WorkingFolder.FullPath}");
            var branchparam = branchName == null ? "" : $" -b {branchName}";

            await StartGitProcess($"clone{branchparam} {CreateCredentialsUri(pullEndpoint, _gitCredentials)} .", true); // Clone into current folder

            _logger.Detailed("Git clone complete");
        }
        public PackageInProject?Read(
            string?id, string?version,
            PackagePath path,
            IEnumerable <string>?projectReferences)
        {
            if (path == null)
            {
                return(null);
            }

            if (string.IsNullOrWhiteSpace(id))
            {
                _logger.Normal($"Skipping package with no id specified in file '{path.FullName}'.");
                return(null);
            }

            if (string.IsNullOrWhiteSpace(version))
            {
                _logger.Normal($"Skipping package '{id}' with no version specified in file '{path.FullName}'.");
                return(null);
            }

            var packageVersionRange = PackageVersionRange.Parse(id, version);

            if (packageVersionRange == null)
            {
                _logger.Normal($"Skipping package '{id}' with version '{version}' that could not be parsed in file '{path.FullName}'.");
                return(null);
            }

            var pip = new PackageInProject(packageVersionRange, path, projectReferences);

            var singleVersion = pip.Identity;

            if (singleVersion == null)
            {
                _logger.Normal($"Skipping package '{id}' with version range '{version}' that is not a single version in file '{path.FullName}'.");
                return(null);
            }

            return(pip);
        }
Esempio n. 21
0
        private async Task <ForkData> FindUserForkOrUpstream(string userName, ForkData pullFork)
        {
            var userFork = await TryFindUserFork(userName, pullFork);

            if (userFork != null)
            {
                return(userFork);
            }

            // as a fallback, we want to pull and push from the same origin repo.
            var canUseOriginRepo = await IsPushableRepo(pullFork);

            if (canUseOriginRepo)
            {
                _logger.Normal($"No fork for user {userName}. Using upstream fork for user {pullFork.Owner} at {pullFork.Uri}");
                return(pullFork);
            }

            NoPushableForkFound(pullFork.Name);
            return(null);
        }
        public async Task <ProcessOutput> Run(string workingDirectory, string command, string arguments, bool ensureSuccess)
        {
            _logger.Normal($"Using Mono to run '{command}'");

            if (!await CanRun())
            {
                _logger.Error($"Cannot run '{command}' on Mono since Mono installation was not found");
                throw new InvalidOperationException("Mono installation was not found");
            }

            return(await _externalProcess.Run(workingDirectory, "mono", $"{command} {arguments}", ensureSuccess));
        }
Esempio n. 23
0
        public static void Log(this INuKeeperLogger logger, LogData data)
        {
            if (!string.IsNullOrWhiteSpace(data.Terse))
            {
                logger.Minimal(data.Terse);
            }

            if (!string.IsNullOrWhiteSpace(data.Info))
            {
                logger.Normal(data.Info);
            }
        }
Esempio n. 24
0
        public async Task <IReadOnlyList <Organization> > GetOrganizations()
        {
            var orgs = await _client.Organization.GetAll();

            _logger.Normal($"Read {orgs.Count} organisations");
            return(orgs);
        }
Esempio n. 25
0
        private async Task <int> MakeUpdatePullRequests(
            IGitDriver git, RepositoryData repository,
            NuGetSources sources, SettingsContainer settings,
            IReadOnlyCollection <PackageUpdateSet> updates)
        {
            _logger.Normal(UpdatesLogger.OldVersionsToBeUpdated(updates));

            await git.Checkout(repository.DefaultBranch);

            // branch
            var branchWithChanges = BranchNamer.MakeName(updates, settings.BranchSettings.BranchNamePrefix);

            _logger.Detailed($"Using branch name: '{branchWithChanges}'");
            await git.CheckoutNewBranch(branchWithChanges);

            foreach (var updateSet in updates)
            {
                await _updateRunner.Update(updateSet, sources);

                var commitMessage = _collaborationFactory.CommitWorder.MakeCommitMessage(updateSet);
                await git.Commit(commitMessage);
            }

            await git.Push(repository.Remote, branchWithChanges);

            var title = _collaborationFactory.CommitWorder.MakePullRequestTitle(updates);
            var body  = _collaborationFactory.CommitWorder.MakeCommitDetails(updates);

            string qualifiedBranch;

            if (!repository.IsFork) //check if we are on a fork, if so qualify the branch name
            {
                qualifiedBranch = branchWithChanges;
            }
            else
            {
                qualifiedBranch = repository.Push.Owner + ":" + branchWithChanges;
            }

            var pullRequestRequest = new PullRequestRequest(qualifiedBranch, title, repository.DefaultBranch, settings.BranchSettings.DeleteBranchAfterMerge)
            {
                Body = body
            };

            await _collaborationFactory.CollaborationPlatform.OpenPullRequest(repository.Pull, pullRequestRequest, settings.SourceControlServerSettings.Labels);


            await git.Checkout(repository.DefaultBranch);

            return(updates.Count);
        }
Esempio n. 26
0
        public PackageInProject Read(
            string id, string version,
            PackagePath path,
            IEnumerable <string> projectReferences)
        {
            if (string.IsNullOrWhiteSpace(id))
            {
                _logger.Normal($"Skipping package with no id specified in file '{path.FullName}'.");
                return(null);
            }

            if (string.IsNullOrWhiteSpace(version))
            {
                // TODO this is very spammy when using MSBuild Directory.build.props or .targets files for versioning NuGets. Should find a better way for this.
                _logger.Detailed($"Skipping package '{id}' with no version specified in file '{path.FullName}'.");
                return(null);
            }

            var packageVersionRange = PackageVersionRange.Parse(id, version);

            if (packageVersionRange == null)
            {
                _logger.Normal($"Skipping package '{id}' with version '{version}' that could not be parsed in file '{path.FullName}'.");
                return(null);
            }

            var pip = new PackageInProject(packageVersionRange, path, projectReferences);

            var singleVersion = pip.Identity;

            if (singleVersion == null)
            {
                _logger.Normal($"Skipping package '{id}' with version range '{version}' that is not a single version in file '{path.FullName}'.");
                return(null);
            }

            return(pip);
        }
Esempio n. 27
0
        public async Task <IReadOnlyCollection <PackageUpdateSet> > FindPackageUpdateSets(
            IFolder workingFolder,
            NuGetSources sources,
            VersionChange allowedChange,
            UsePrerelease usePrerelease,
            Regex includes = null,
            Regex excludes = null)
        {
            var packages = FindPackages(workingFolder);

            _logger.Normal($"Found {packages.Count} packages");

            var filtered = FilteredByIncludeExclude(packages, includes, excludes);

            _logger.Log(PackagesFoundLogger.Log(filtered));

            // look for updates to these packages
            var updates = await _packageUpdatesLookup.FindUpdatesForPackages(
                filtered, sources, allowedChange, usePrerelease);

            _logger.Log(UpdatesLogger.Log(updates));
            return(updates);
        }
Esempio n. 28
0
        private async Task <ForkData> FindUpstreamRepoOnly(ForkData pullFork)
        {
            // Only want to pull and push from the same origin repo.
            var canUseOriginRepo = await IsPushableRepo(pullFork);

            if (canUseOriginRepo)
            {
                _logger.Normal($"Using upstream fork as push, for project {pullFork.Owner} at {pullFork.Uri}");
                return(pullFork);
            }

            NoPushableForkFound(pullFork.Name);
            return(null);
        }
Esempio n. 29
0
        private async Task <RepositoryData> BuildGitRepositorySpec(
            RepositorySettings repository,
            string userName)
        {
            var pullFork = new ForkData(repository.RepositoryUri, repository.RepositoryOwner, repository.RepositoryName);
            var pushFork = await _collaborationFactory.ForkFinder.FindPushFork(userName, pullFork);

            if (pushFork == null)
            {
                _logger.Normal($"No pushable fork found for {repository.RepositoryUri}");
                return(null);
            }

            return(new RepositoryData(pullFork, pushFork));
        }
Esempio n. 30
0
        private IReadOnlyCollection <PackageUpdateSet> ApplyFilters(
            IReadOnlyCollection <PackageUpdateSet> all)
        {
            var filtered = all
                           .Where(MatchesMinAge)
                           .ToList();

            if (filtered.Count < all.Count)
            {
                var agoFormat = TimeSpanFormat.Ago(_settings.MinimumAge);
                _logger.Normal($"Filtered by minimum package age '{agoFormat}' from {all.Count} to {filtered.Count}");
            }

            return(filtered);
        }