static async Task Main(string[] args) { Console.WriteLine(Environment.CurrentDirectory); // There's probably an idiomatic way to tuck these into the DI container as referenceable values... var dosBuffer = TimeSpan.FromSeconds(0.1); var loopDelay = TimeSpan.FromHours(24); // Also include: cache path, scratch dir, etc. All the static readonlys above except the CTS, probably var configuration = new ConfigurationBuilder() .AddEnvironmentVariables() .AddJsonFile(_configPath, optional: false, reloadOnChange: true) .Build(); var serviceCollection = new ServiceCollection() .Configure <PullRequestConstants>(RepositoryKind.GitHub.ToString(), configuration.GetSection("PRConstants:GitHub")) .Configure <PullRequestConstants>(RepositoryKind.BitBucketCloud.ToString(), configuration.GetSection("PRConstants:BitBucketCloud")) .AddTransient(sp => { var prConstantsAccessor = sp.GetRequiredService <IOptionsSnapshot <PullRequestConstants> >(); var prConstants = prConstantsAccessor.Get(RepositoryKind.GitHub.ToString()); return(new GitHubApprovalAnalyzer( prConstants.ExplicitApprovals, prConstants.ExplicitNonApprovals, prConstants.ImplicitApprovals)); }).AddTransient(sp => { var prConstantsAccessor = sp.GetRequiredService <IOptionsSnapshot <PullRequestConstants> >(); var prConstants = prConstantsAccessor.Get(RepositoryKind.BitBucketServer.ToString()); return(new BitBucketApprovalAnalyzer( prConstants.ExplicitApprovals, prConstants.ExplicitNonApprovals, prConstants.ImplicitApprovals)); }) // Reusable building blocks .AddSingleton <IFilesystem>(sp => new Filesystem()) .AddSingleton <IClock, Clock>() .AddSingleton <IWordCounter, WordCounter>() .AddSingleton <INormalizer, HtmlCommentStripper>() .AddSingleton(sp => GetKnownScorers( approvalAnalyzer: sp.GetRequiredService <GitHubApprovalAnalyzer>(), wc: sp.GetRequiredService <IWordCounter>())) .AddSingleton(sp => new ScorerConverter(sp.GetRequiredService <IScorerFactory>())) .AddSingleton(sp => GetDebugJsonSerializerSettings(sp.GetRequiredService <IScorerFactory>())) .AddSingleton(sp => new FilesystemDataProvider(sp.GetRequiredService <IFilesystem>(), _scratchDir, sp.GetRequiredService <JsonSerializerSettings>())) .AddSingleton <IPullRequestCacheManager>(sp => sp.GetRequiredService <FilesystemDataProvider>()) // Do this so that we *can* modify our Markdown pipeline later on if we need to. .AddSingleton(new MarkdownPipelineBuilder().Build()) .AddSingleton <IHistoricalAnalysisManager>(sp => sp.GetRequiredService <FilesystemDataProvider>()) // CommentScorers .AddSingleton <UrlScorer>() .AddSingleton <CodeFenceScorer>() .AddSingleton <CodeFragmentScorer>() .AddSingleton <GitHubIssueLinkScorer>() .AddSingleton <Scorer>(sp => sp.GetRequiredService <UrlScorer>()) .AddSingleton <Scorer>(sp => sp.GetRequiredService <CodeFenceScorer>()) .AddSingleton <Scorer>(sp => sp.GetRequiredService <CodeFragmentScorer>()) .AddSingleton <Scorer>(sp => sp.GetRequiredService <GitHubIssueLinkScorer>()) // PullRequestScorers .AddSingleton <WordCountScorer>() .AddSingleton <BusinessDaysScorer>() .AddSingleton <UniqueCommenterScorer>() .AddSingleton(sp => new CommentCountScorer(sp.GetRequiredService <IWordCounter>())) .AddSingleton(sp => new ApprovalScorer(sp.GetRequiredService <GitHubApprovalAnalyzer>())) .AddSingleton(sp => new CommentCountScorer(sp.GetRequiredService <IWordCounter>())) .AddSingleton <Scorer>(sp => sp.GetRequiredService <ApprovalScorer>()) .AddSingleton <Scorer>(sp => sp.GetRequiredService <BusinessDaysScorer>()) .AddSingleton <Scorer>(sp => sp.GetRequiredService <CommentCountScorer>()) .AddSingleton <Scorer>(sp => sp.GetRequiredService <UniqueCommenterScorer>()) .AddSingleton <Scorer>(sp => sp.GetRequiredService <WordCountScorer>()) // Roll it all up into the orchestrators and factories... .AddSingleton <IPullRequestAnalyzer>(sp => new PullRequestAnalyzer(sp.GetServices <Scorer>())) .AddSingleton <IRepositoryAnalyzer>(sp => new RepositoryAnalyzer(sp.GetRequiredService <IClock>())) .AddSingleton(sp => new GitHubPullRequestReaderFactory("repoman-health-metrics", sp.GetRequiredService <INormalizer>())) .AddSingleton(sp => new BitBucketCloudPullRequestReaderFactory( sp.GetRequiredService <IClock>(), sp.GetRequiredService <JsonSerializerSettings>(), httpConnectionLifespan: TimeSpan.FromSeconds(120), GetLogger <BitBucketCloudPullRequestReaderFactory>())) .AddSingleton <IPullRequestReaderFactory, PullRequestReaderFactory>(sp => new PullRequestReaderFactory( sp.GetRequiredService <GitHubPullRequestReaderFactory>(), sp.GetRequiredService <BitBucketCloudPullRequestReaderFactory>(), GetLogger <PullRequestReaderFactory>())) .AddSingleton <IRepoManagerFactory>(sp => new RepoManagerFactory( sp.GetRequiredService <IPullRequestReaderFactory>(), sp.GetRequiredService <IPullRequestCacheManager>(), dosBuffer, GetLogger <RepoManagerFactory>())); var serviceProvider = serviceCollection.BuildServiceProvider(); // FUTURE: Consider implementing an UpgradeAsync method that can do one-time data transformations/updates on cached data var repos = configuration.GetSection("WatchedRepositories").Get <List <WatchedRepository> >(); var repoManagers = repos .Select(r => serviceProvider.GetRequiredService <IRepoManagerFactory>().GetManagerAsync(r, refreshFromUpstream: false)) .ToList(); await Task.WhenAll(repoManagers); var repoWorkerInitialization = repoManagers .Select(t => t.Result) .Select(async repoManager => await RepoWorker.InitializeAsync( repoManager, serviceProvider.GetRequiredService <IPullRequestAnalyzer>(), serviceProvider.GetRequiredService <IRepositoryAnalyzer>(), serviceProvider.GetRequiredService <IHistoricalAnalysisManager>(), serviceProvider.GetRequiredService <IClock>(), GetLogger <RepoWorker>())) .ToList(); await Task.WhenAll(repoWorkerInitialization); var loopServices = repoWorkerInitialization .Select(rwi => rwi.Result) .Select(rw => new LoopService(rw, loopDelay, _cts, _logger)) .Select(l => l.LoopAsync()) .ToList(); await Task.WhenAll(loopServices); }
public AppService(ApplicationContext context) { Repo = new RepoWorker(context); }