static void FindAllCommands( Assembly assembly, BuildTaskManager buildTaskManager) { foreach (Type type in assembly.GetTypes()) { if (type.IsClass && type.IsPublic && !type.IsAbstract && typeof(IBuildTask).IsAssignableFrom(type)) { var runBeforeListPropertyInfo = type.GetProperty("RunBeforeList", BindingFlags.Public | BindingFlags.Static); if (runBeforeListPropertyInfo is null) { throw new InvalidOperationException("Type missing RunBeforeList"); } var runBeforeList = (IReadOnlyList <string>?)runBeforeListPropertyInfo.GetValue(null, null); if (runBeforeList is null) { throw new InvalidOperationException("RunBeforeList is null"); } var runAfterListPropertyInfo = type.GetProperty("RunAfterList", BindingFlags.Public | BindingFlags.Static); if (runAfterListPropertyInfo is null) { throw new InvalidOperationException("Type missing RunAfterList"); } var runAfterList = (IReadOnlyList <string>?)runAfterListPropertyInfo.GetValue(null, null); if (runAfterList is null) { throw new InvalidOperationException("RunAfterList is null"); } buildTaskManager.RegisterTask(type.Name, type, runBeforeList, runAfterList); } } }
/// <summary> /// Execute the entire operation graph that is referenced by this build generate engine. /// </summary> public async Task GenerateAsync(Path soupTargetDirectory) { // Run all build operations in the correct order with incremental build checks Log.Diag("Build generate start"); // Load the parameters file var parametersFile = soupTargetDirectory + BuildConstants.GenerateParametersFileName; if (!ValueTableManager.TryLoadState(parametersFile, out var parametersState)) { Log.Error("Failed to load the parameter file: " + parametersFile.ToString()); throw new InvalidOperationException("Failed to load parameter file."); } // Load the read access file var readAccessFile = soupTargetDirectory + BuildConstants.GenerateReadAccessFileName; var readAccessList = new List <Path>(); if (!await PathListManager.TryLoadFileAsync(readAccessFile, readAccessList)) { Log.Error("Failed to load the read access file: " + readAccessFile.ToString()); throw new InvalidOperationException("Failed to load read access file."); } // Load the write access file var writeAccessFile = soupTargetDirectory + BuildConstants.GenerateWriteAccessFileName; var writeAccessList = new List <Path>(); if (!await PathListManager.TryLoadFileAsync(writeAccessFile, writeAccessList)) { Log.Error("Failed to load the write access file: " + writeAccessFile.ToString()); throw new InvalidOperationException("Failed to load write access file."); } // Get the required input state from the parameters var targetDirectory = new Path(parametersState["TargetDirectory"].AsString().ToString()); var packageDirectory = new Path(parametersState["PackageDirectory"].AsString().ToString()); // Load the recipe file var recipeFile = packageDirectory + BuildConstants.RecipeFileName; var(isSuccess, recipe) = await RecipeExtensions.TryLoadRecipeFromFileAsync(recipeFile); if (!isSuccess) { Log.Error("Failed to load the recipe: " + recipeFile.ToString()); throw new InvalidOperationException("Failed to load recipe."); } // Combine all the dependencies shared state var dependenciesSharedState = LoadDependenciesSharedState(parametersState); // Generate the set of build extension libraries var buildExtensionLibraries = GenerateBuildExtensionSet(recipe, dependenciesSharedState); // Start a new active state that is initialized to the recipe itself var activeState = new ValueTable(); // Initialize the Recipe Root Table var recipeState = recipe.Table; activeState.Add("Recipe", new Value(recipeState)); // Initialize the Parameters Root Table activeState.Add("Parameters", new Value(parametersState)); // Initialize the Dependencies Root Table activeState.Add("Dependencies", new Value(dependenciesSharedState)); // Keep the extension libraries open while running the build system // to ensure their memory is kept alive var evaluateGraph = new OperationGraph(); IValueTable sharedState = new ValueTable(); { // Create a new build system for the requested build var buildTaskManager = new BuildTaskManager(); // Run all build extension register callbacks foreach (var buildExtension in buildExtensionLibraries) { var library = LoadPlugin(buildExtension); FindAllCommands(library, buildTaskManager); } // Run the build var buildState = new BuildState( activeState, _fileSystemState, readAccessList, writeAccessList); buildTaskManager.Execute(buildState, soupTargetDirectory); // Grab the build results so the dependency libraries can be released asap evaluateGraph = buildState.BuildOperationGraph(); sharedState = buildState.SharedState; } // Save the operation graph so the evaluate phase can load it var evaluateGraphFile = soupTargetDirectory + BuildConstants.GenerateEvaluateOperationGraphFileName; OperationGraphManager.SaveState(evaluateGraphFile, evaluateGraph, _fileSystemState); // Save the shared state that is to be passed to the downstream builds var sharedStateFile = soupTargetDirectory + BuildConstants.GenerateSharedStateFileName; ValueTableManager.SaveState(sharedStateFile, sharedState); Log.Diag("Build generate end"); }
/// <summary> /// Add the main edity services. If you call this and can use the default IFileFinder, you don't need to /// do anything else, if you want to customize the file finder add it before calling this function. /// </summary> /// <param name="services">The services collection.</param> /// <param name="setupEditySettings">Callback to configure the edity settings.</param> /// <returns>The service collection.</returns> public static IServiceCollection AddEdity(this IServiceCollection services, Action <EditySettings> setupEditySettings) { services.AddThreaxSharedHttpClient(); var editySettings = new EditySettings(); setupEditySettings.Invoke(editySettings); //Setup the mapper var mapperConfig = SetupMappings(); services.AddScoped <IMapper>(i => mapperConfig.CreateMapper()); //Setup repos services.TryAddScoped <ICommitRepository, CommitRepository>(); services.TryAddScoped <ISyncRepository, SyncRepository>(); services.TryAddScoped <IPathBaseInjector, PathBaseInjector>(); services.TryAddScoped <IDraftRepository, DraftRepository>(); services.TryAddScoped <IPublishRepository, PublishRepository>(); services.TryAddScoped <IHistoryRepository, HistoryRepository>(); services.TryAddScoped <IMergeRepository, MergeRepository>(); services.TryAddScoped <IPageRepository, PageRepository>(); services.TryAddScoped <ITemplateRepository, TemplateRepository>(); services.TryAddScoped <IAssetRepository, AssetRepository>(); services.TryAddScoped <IBranchRepository, BranchRepository>(); services.TryAddSingleton <IOverrideValuesProvider>(s => new DefaultOverrideValuesProvider(editySettings.OverrideVars)); services.TryAddScoped <IGitCredentialsProvider, DefaultGitCredentialsProvider>(); var baseUrl = HalcyonConventionOptions.HostVariable; if (editySettings.BaseUrl != null) { baseUrl += "/" + editySettings.BaseUrl; } var halOptions = new HalcyonConventionOptions() { BaseUrl = baseUrl, HalDocEndpointInfo = new HalDocEndpointInfo(typeof(EndpointDocController)), MakeAllControllersHalcyon = false }; services.AddConventionalHalcyon(halOptions); var halClientGenOptions = new HalClientGenOptions() { SourceAssemblies = new Assembly[] { typeof(EdityMvcExtensions).GetTypeInfo().Assembly } }; if (editySettings.AdditionalMvcLibraries != null) { halClientGenOptions.SourceAssemblies = halClientGenOptions.SourceAssemblies.Concat(editySettings.AdditionalMvcLibraries); } services.AddHalClientGen(halClientGenOptions); if (editySettings.Events == null) { editySettings.Events = new EdityEvents(); } services.TryAddScoped <ITargetFileInfoProvider>(s => { var fileFinder = s.GetRequiredService <IFileFinder>(); return(new DefaultTargetFileInfoProvider(fileFinder.Project.DefaultPage)); }); services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>(); services.TryAddSingleton <WorkQueue, WorkQueue>(); services.TryAddSingleton <ICompileService>(s => new CompileService(s.GetRequiredService <WorkQueue>(), mapperConfig.CreateMapper())); services.TryAddScoped <IUserInfo, DefaultUserInfo>(); services.TryAddScoped <IPhaseDetector>(s => { var settings = new JsonSerializerSettings(); settings.SetToHalcyonDefault(); var serializer = JsonSerializer.Create(settings); return(new CookiePhaseDetector("edityBranch", serializer, s.GetRequiredService <IHttpContextAccessor>())); }); services.AddSingleton <EditySettings>(s => editySettings); switch (editySettings.ProjectMode) { case ProjectMode.OneRepo: default: services.AddTransient <ProjectFinder, OneRepo>(s => { return(new OneRepo(editySettings.ProjectPath, editySettings.EdityCorePath, editySettings.SitePath)); }); break; case ProjectMode.OneRepoPerUser: services.AddTransient <ProjectFinder, OneRepoPerUser>(s => { return(new OneRepoPerUser(editySettings, s.GetRequiredService <IPhaseDetector>(), s.GetRequiredService <ILogger <OneRepoPerUser> >())); }); break; } services.AddTransient <Repository, Repository>(s => { var userInfo = s.GetRequiredService <IUserInfo>(); var projectFinder = s.GetRequiredService <ProjectFinder>(); var projectFolder = projectFinder.GetUserProjectPath(userInfo.UniqueUserName); return(new Repository(Repository.Discover(projectFolder))); }); services.AddTransient <Signature, Signature>(s => { var userInfo = s.GetRequiredService <IUserInfo>(); return(new Signature(userInfo.PrettyUserName, userInfo.Email, DateTime.Now)); }); services.AddTransient <SiteBuilderSettings, SiteBuilderSettings>(s => { return(new SiteBuilderSettings() { OutDir = editySettings.OutputPath }); }); services.TryAddScoped <IContentCompilerFactory, ContentCompilerFactory>(); services.AddDefaultFileFinder(); services.AddSingleton <BuildTaskManager>(s => { var buildTaskManager = new BuildTaskManager(); buildTaskManager.SetBuildTaskType("PublishMenu", typeof(PublishMenu)); buildTaskManager.SetBuildTaskType("CreateIISWebConfig", typeof(CreateIISWebConfig)); buildTaskManager.SetBuildTaskType("GetPublishRepo", typeof(GetPublishRepo)); buildTaskManager.SetBuildTaskType("PublishToGitRepo", typeof(PublishToGitRepo)); buildTaskManager.SetBuildTaskType("AddGithubCname", typeof(AddGithubCname)); editySettings.Events.CustomizeBuildTasks?.Invoke(buildTaskManager); return(buildTaskManager); }); services.TryAddTransient <PullPublish>(s => { var projectFinder = s.GetRequiredService <ProjectFinder>(); return(new PullPublish(projectFinder.MasterRepoPath, projectFinder.PublishedProjectPath)); }); services.AddTransient <ISiteBuilder>(s => { var settings = s.GetRequiredService <SiteBuilderSettings>(); var compilerFactory = s.GetRequiredService <IContentCompilerFactory>(); var fileFinder = s.GetRequiredService <IFileFinder>(); String deploymentFolder = null; if (editySettings.Publisher == Publishers.RoundRobin) { deploymentFolder = Guid.NewGuid().ToString(); } var builder = new SiteBuilder(settings, compilerFactory, fileFinder, deploymentFolder); var buildTaskManager = s.GetRequiredService <BuildTaskManager>(); //Customize publisher settings depending on compiler setting switch (editySettings.Publisher) { case Publishers.RoundRobin: var outputBaseFolder = settings.OutDir; settings.OutDir = Path.GetFullPath(Path.Combine(settings.OutDir, deploymentFolder)); builder.AddPublishTask(new RoundRobinPublisher(settings.OutDir)); break; } if (editySettings.ProjectMode == ProjectMode.OneRepoPerUser) { builder.AddPreBuildTask(s.GetRequiredService <PullPublish>()); } foreach (var preBuild in fileFinder.Project.PreBuildTasks) { builder.AddPreBuildTask(buildTaskManager.CreateBuildTask(preBuild)); } foreach (var postBuild in fileFinder.Project.PostBuildTasks) { builder.AddPostBuildTask(buildTaskManager.CreateBuildTask(postBuild)); } foreach (var publish in fileFinder.Project.PublishTasks) { builder.AddPublishTask(buildTaskManager.CreateBuildTask(publish)); } foreach (var postPublish in fileFinder.Project.PostPublishTasks) { builder.AddPostPublishTask(buildTaskManager.CreateBuildTask(postPublish)); } editySettings.Events.CustomizeSiteBuilder(new SiteBuilderEventArgs() { SiteBuilder = builder, Services = s }); return(builder); }); services.AddExceptionErrorFilters(new ExceptionFilterOptions() { DetailedErrors = editySettings.DetailedErrors }); // Add framework services. var mvcBuilder = services.AddMvc(o => { o.UseExceptionErrorFilters(); o.UseConventionalHalcyon(halOptions); }) .AddNewtonsoftJson(o => { o.SerializerSettings.SetToHalcyonDefault(); o.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }) .AddEdityControllers(editySettings.AdditionalMvcLibraries); editySettings.Events.CustomizeMvcBuilder?.Invoke(mvcBuilder); services.AddScoped <ICompileRequestDetector, CompileRequestDetector>(); services.AddSingleton <IFileVerifier>(s => { var verifier = new FileVerifier() .AddHtml() .AddBitmap() .AddJpeg() .AddPng() .AddSvgXml() .AddGif() .AddPdf() .AddDocx() .AddDoc() .AddPptx() .AddPpt() .AddXlsx() .AddXls() .AddJson(); editySettings.Events.CustomizeFileVerifier?.Invoke(verifier); return(verifier); }); services.AddScoped <IToolRunner>(s => { var tools = new ToolRunner() .UseClientGenTools(); editySettings.Events.CustomizeTools?.Invoke(tools); return(tools); }); services.AddThreaxProcessHelper(o => { //o.IncludeLogOutput = false; //o.DecorateProcessRunner = r => new SpyProcessRunner(r) //{ // Events = new ProcessEvents() // { // ErrorDataReceived = (o, e) => { if (e.DataReceivedEventArgs.Data != null) Console.WriteLine(e.DataReceivedEventArgs.Data); }, // OutputDataReceived = (o, e) => { if (e.DataReceivedEventArgs.Data != null) Console.WriteLine(e.DataReceivedEventArgs.Data); }, // } //}; }); return(services); }