Beispiel #1
0
        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);
                }
            }
        }
Beispiel #2
0
        /// <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");
        }
Beispiel #3
0
        /// <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);
        }