Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow users to force Drush to recognize validity of Drupal root and site #342

Closed
bangpound opened this issue Dec 29, 2013 · 37 comments
Closed

Comments

@bangpound
Copy link
Contributor

This is a long shot, but I figured I'd ask!

Would drush accept a pull request that creates a global option that forces drush_valid_drupal_root() to return true and also forces the return value of drush_drupal_version() to be a user specified value? (This would have to be an at-your-own-risk kind of option of course.)

I'm happy to do this work myself if Drush maintainers would consider committing it when it's complete. If so, do you have any advice about it? Should I try addressing the problem some other way?

Background

I am running Drupal 7 inside the Symfony framework. I've replaced Drupal's bootstrap and front controller, but more or less everything can work the same with Drush except that Drush searches for index.php to determine the validity of a Drupal root directory.

Symfony uses app.php, and if I symlink or copy app.php to index.php, there are unwelcome side effects. (In particular, the PHP built-in web server privileges index.php even when it is told to use a router script.)

For now, I've hacked Drush and replaced index.php with app.php in various places where it's needed. Since my use is very much an edge case, I don't expect Drush to change much to accommodate my use.

@greg-1-anderson
Copy link
Member

Backdrop wanted to do something similar (c.f. backdrop/backdrop-issues#47).

I think a global option is a bad idea. I think a hook that some d7symfony.drush.inc or backdrop.drush.inc could implement to provide its own "valid root" detection would be a good idea.

It would also be nice if the "flavor" of Drupal that was detected could also in some way affect the way commandfile searching was done, so that you could make global Drush commandfiles that were specific to only certain Drupal variants. See also #88.

@bangpound
Copy link
Contributor Author

Please don't rush to reply to this. I have to get up and will have to return to it later. These thoughts are still in formation.

I read the issue you mentioned, and I think it's quite a long shot, though if someone were writing Drush from scratch today, Symfony Console would probably be the kit to use.

I haven't tried Backdrop at all, but my strategy for Symfony Drupal is simply to port Drupal 7 onto Symfony's kernel and foundation objects. This requires minimal changes, and I've tried to make as few changes as possible so that I'm not creating something that is totally unsustainable. (My intent is to continue merging Drupal 7.x and remain API compatible wherever possible.)

I'm only marginally acquainted with Drush's guts, so let me try to expand your idea.

Rather than a global option, there should be a Drush extension file that is specific to each Drupal fork. I guess this would be placed in one of the six paths (identified at the top of example.drushrc.php) where Drush searches for extensions. Its presence would mean it is automatically included, and it would be a file that only implements Drush hooks (similar to a the policy.drush.inc file that is also in examples).

For Symfony Drupal, it would be stored in sites/all/drush. (If another fork exists that doesn't have a distro-specific directory where Drush already looks, it would have to be specified with the --config option? This doesn't apply to Symfony Drupal, though.)

This fork-specific file would implement hooks that don't exist yet. These hooks would be invoked at almost every phase of bootstrap (or would replace bootstrap entirely?) to supply alternative or additional validation routines for identifying the drupal_root, site, conf_path, and database settings.

Am I getting it right so far?

I've already hacked Drupal's bootstrap so that I can override it. I wonder if it would make more sense for forks to provide their own bootstrap.inc and environment.inc?

@greg-1-anderson
Copy link
Member

Don't worry about supporting backdrop, or rewriting Drush to use the Symfony Console; those are side-notes that I include for reference, so folks who work on changing the way bootstrapping works will be aware of other issues where similar things might be happening at some point.

I don't really like the idea of forks providing entire bootstrap.inc / envrionment.inc files. It would be better to make a new bootstrap class that knows how to bootstrap a certain flavor of Drupal.

For your purposes, all you would need to do is create the bootstrap class, make some way for forks to provide their own derived class during the bootstrap process, make a method that returns 'index.php' in the base class, and override that method to return 'app.php' in your derived class. (To be clear: DRUSH_BOOTSTRAP_DRUSH and all of the site-selection code stays hardcoded in Drush; the bootstrap class is just to bootstrap Drupal, or some variant thereof.) Then, obvously, Drush would need to call your method everywhere that you replaced index.php in your prototype. Extending Drush to use this class for more things (e.g. to make it possible to bootstrap Backdrop) could be left as an exercise for folks who needed such things.

@weitzman
Copy link
Member

weitzman commented Jan 2, 2014

Right. I have long wanted to use factor out the Drupal from Drush. The bootstrap class would be a big step forward.

@bangpound
Copy link
Contributor Author

I think I can do this! I'll give it a shot soon.

@greg-1-anderson
Copy link
Member

The only wrinkle is that the methods you are modifying are needed to select the bootstrap class. :) But I think you can figure that out. Maybe every hook is allowed to create its bootstrap object, and the ones that do not validate (hopefully all but one!) are thrown out.

Thanks for taking this on.

@jenlampton
Copy link

@greg-1-anderson I just wanted to check and see if this was still the direction you wanted to go with this... since it's been a year since the last comment :) I may be able to drum up some people who are working on Backdrop (but really miss Drush) to take a stab at this, as long as you guys still approve of the direction.

@greg-1-anderson
Copy link
Member

There are a couple of different directions that we could potentially go here.

Please see the discussion in #624 (referenced above); I claimed late in that thread that the PR merged there implements this issue. That declaration was a bit optimistic, though; while the scaffolding is now in place, but there still isn't any way to plug in some code to allow Drush to recognize a Backdrop bootstrap class.

Here is the code that runs first thing in drush_main() in drush.php:

  $bootstrap_class = drush_get_option('bootstrap_class', 'Drush\Boot\DrupalBoot');
  $bootstrap = new $bootstrap_class;
  drush_set_context('DRUSH_BOOTSTRAP_OBJECT', $bootstrap);
  $bootstrap->preflight();

There's the global option that allows you to specify on the command line that you'd like to use some bootstrap class other than DrupalBoot. This is done for the benefit of 'early' code that might want to use the bootstrap object. So, with this code, you can today support Backdrop, but you'd have to do so by adding a --bootstrap_class option to the command line (perhaps via a convenient bash alias).

I think what we actually need, though, is to postpone finding the bootstrap class until after (or during) the DRUSH_BOOTSTRAP_DRUSH phase. Until we have bootstrapped Drush, we can't really provide plugin classes that might be consulted to find a better bootstrap class. I don't have a plan on what to do with the 'early' code in this scenario, though.

Another possibility would be to depend on #572. If we did this, and were therefore using Composer to load our Drush extensions, then we could use php reflection to find the available bootstrap classes. The only trick here is that we would need to do this after we included the Composer autoloader.

Not sure what Backdrop's opinion on Composer is. Hopefully, Composer will be used in Backdrop as well, as this is the direction we'd ultimately like to go in Drush as well.

@Kazanir
Copy link
Contributor

Kazanir commented Jan 21, 2015

I confess to some confusion with this issue. I would definitely like to see Drush support Backdrop somehow, but I have read through the code and I do not see how the Boot interface and DrupalBoot class could help us accomplish this.

Here is my chain of thought from exploring the code:

  • The offending code here seems to be in the function drush_valid_drupal_root() which is defined in includes/environment.inc. The other offender is drush_drupal_version() which is similarly in includes/drupal.inc.
  • Both of these files are included from drush_preflight_prepare() in includes/preflight.inc.
  • The bootstrap code is run first thing in drush_main(), but drush_main() is not actually the first thing that runs in drush.php.
  • Rather, includes/preflight.inc is included, and drush_preflight_prepare() is run before drush_main() is executed. (Hence the name "preflight", presumably.)
  • This means, as far as I can tell, that there is no way to override the includes that happen during preflight and no way to override the drush_valid_drupal_root() function.
  • This function is hard-coded into several other places in includes/environment.inc as well as includes/sitealias.inc, so even if the DrupalBoot class were replaced with one which doesn't invoke these offenders, this problem would still crop up elsewhere in the code.
  • Finally, I observe that overriding the DrupalBoot class is going to be kind of a pain. It has two main methods, one which includes 950 lines of code in other include files and one which is a 70-line method that primarily calls the functions from those 2 files. It seems like overriding a small piece of the Boot class functionality is going to end up requiring a lot of needless copy/paste.

Please let me know if I'm totally barking up the wrong tree here or have missed something obvious. I'm happy to provide some firepower to get this done but it is hard to know what the "right" path to take is.

@greg-1-anderson
Copy link
Member

Thanks for looking at this Kazanir. First off, you are correct in your analysis. A lot of the early-execution code in Drush has evolved over a period of many releases, and could stand to be improved.

Some comments.

  • drush_valid_drupal_root() should probably become a method of the bootstrap class. This will require some refactoring.
  • Anything included from preflight.inc should be Drupal-agnostic. Drupal-specific code should move to the bootstrap class.
  • The primary purpose of the preflight code is to find out if something other than running a local Drush command is happening. For example, drush @remote ssh will execute without ever running drush_main(). This is perhaps odd, but in the short term, would not need to be fixed to support Backdrop.
  • I haven't surveyed the usage of drush_valid_drupal_root() in sitealais.inc, but often this is done to process long-form site aliases. This problem will have to be resolved one way or another, but I can probably help out here if we get to the point where a basic bootstrap is working.
  • If you need to change something buried deep inside some overly-large method of DrupalBoot, then by all means, factor out a separate method that returns just the information you need to modify, and insure that your new method is called whenever needed. Once DrupalBoot is fixed up, you should be able to override it and change just a few specific methods.

Looks like there's still quite a bit of work needed here--sorry to say that I don't see any silver bullet, it's just going to be a simple ("simple") matter of refactoring and fixing the shortcomings.

@Kazanir
Copy link
Contributor

Kazanir commented Jan 22, 2015

Your post is a big relief actually -- I'm more scared of doing something completely wrong than of a bunch of refactoring. I'll try to knock out the outlines of a PR "soon" based on your comments, probably this weekend.

@jenlampton
Copy link

To summarize, what is needed for Drush is as follows:

  • Refactor `drush_valid_drupal_root' as part of bootstrap class
  • Move Drupal-specific functions from preflight into bootstrap (if needed / carefully)
  • Refactor DrupalBoot to be less of a pain when overridden
  • Create a way to plug in some code to recognize a different bootstrap class ** doozy?

And what is needed for Backdrop:

  • Specify a different bootstrap class
  • Provide the bootstrap class specified

@greg-1-anderson
Copy link
Member

@jenlampton That's mostly right. I think the stuff in preflight.inc is probably fine (or mostly fine), but preflight.inc includes some files that will have code that needs to move to the bootstrap class. This will primarily be in environment.inc. Caution should be used here, especially when changing the execution order of code. I'm happy to review and help adjust PRs here.

Also, the other issue is that right now, Backdrop does not have any way to provide the bootstrap class. Even if you provide a new classname via the --bootstrap_class option, only classes that are part of Drush core are considered at this time. The choices to fix this deficiency are:

  • Delay the lookup of the bootstrap class until after DRUSH_BOOTSTRAP_DRUSH (might be hard wrt "early" code).
  • Depend on composer, and make sure that the bootstrap class is not created until after the autoloader is included -- which it already is.

The second option looks easier, but it still has the problem that there is no good way to add code at this phase except to modify (hack) Drush's composer.json file.

@greg-1-anderson
Copy link
Member

Perhaps a better short-term solution would be to have Drush look for very specific files, perhaps *.bootstrap.drush.inc in ~/.drush folder, and include those just before the bootstrap class is instantiated. It would be a little disappointing to have what is essentially a separate loader just for the bootstrap class, but in the short term, this would be easiest.

@Kazanir
Copy link
Contributor

Kazanir commented Jan 22, 2015

I don't think the Composer idea with reflection will actually work -- autoloaders can load classes without knowing about their existence beforehand, but PHP's reflection classes are dumb -- I don't think they can give us a list of all possible classes in the autoload path(s) if they haven't actually been loaded yet.

@greg-1-anderson
Copy link
Member

Good point -- php would have to find and load all possible classes for that to work. Drat. Strike that from consideration, then. Maybe the short-term solution is best.

greg-1-anderson added a commit that referenced this issue Jan 23, 2015
…r which operations are happening in the first couple of preflight functions.
@greg-1-anderson
Copy link
Member

Okay, I reviewed the drush bootstrap again this morning, and it turns out that I wasn't entirely correct above. The first preflight function, drush_preflight_prepare(), loads the core Drush code, including the composer autoloader, and parses the command line arguments. The second preflight function, drush_preflight(), is where all of the configuration files are parsed, and the command files (Drush extensions) are loaded. It isn't until drush_preflight_command_dispatch(), futher down, where Drush checks for remote commands and such, and bails out if the command is handled here.

I just pushed a commit, above, that comments this function a bit better. I also moved a couple of functions to make things clearer.

So, the next issue we have is that the bootstrap class is instantiated before drush_preflight(), and I would like to instantiate it after drush_preflight(). If we do this, then we can use the existing drush hook system to load as many bootstrap classes as are available. The only thing standing in our way for doing this today is that one command, global-options, is created and registered in drush_preflight().

I'll make a separate PR that delays the creation of the global-options command, and moves the instantiation of the bootstrap class after drush_preflight(). Note that this will NOT impact the "early" code; I was wrong about that too -- the early code is already run after the command files are loaded, so this change will not slow down command completion.

Once that PR goes in, then the way will be clear to (a) add a boostrap class loader, and (b) refactor the environment stuff like drush_valid_root() mentioned above such that anything that needs the bootstrap class is delayed until $bootstrap->preflight().

n.b. there are some chicken-and-egg problems here too; I will comment on those later.

greg-1-anderson added a commit that referenced this issue Jan 23, 2015
…s insures that all of the command files are loaded first, which will open the way to allow us to instantiate the bootstrap class via a hook.
@greg-1-anderson
Copy link
Member

Okay, moving drush_preflight() up a bit in drush.php was an innocuous change, so I just committed it without making a PR.

The next thing I would suggest would be adding a hook that gives every Drush commandfile the opportunity to return a bootstrap class. Once we have a list of bootstrap classes, we should give all of them the change to determine where the root folder is. Ideally, at most one will return a path to a root folder, and all of the others will say, "nope, this is not my kind of installation."

Once that is available, then the real fun starts. Any code in environment.inc, etc., that depends on drush_valid_drupal_root() and runs during drush_preflight() will need to be postponed to run after the bootstrap class is created. This also includes any code that depends on DRUSH_SELECTED_DRUPAL_ROOT, etc., which means that some of the config files will have to be loaded later than they currently are. The above-mentioned refactoring of the DrupalBoot class will also need to be done.

Maybe I'll have time to make the hook, and then leave the refactoring fun to @Kazanir and other contributors. Sound good?

@Kazanir
Copy link
Contributor

Kazanir commented Jan 26, 2015

I started work on some of this refactoring already but then didn't get to do as much as I wanted over the weekend. I like this plan, but doesn't some of the commandfile searching depend on the resolution of the root followed by the locating of modules where commandfiles might live? Can this be done in phases somehow? I haven't studied this portion of the code -- i.e. the chicken and egg problem -- yet, so I guess that is next.

@greg-1-anderson
Copy link
Member

Yes, as I started to do this refactoring I found that it was a little bit more involved than I imagined. I have not published my branch yet, because I broke bootstrapping :P, but hopefully I can fix and share it today, depending on how busy I get with other things.

This will have to happen in phases, because you can't load all aliases until you've found the root of the site, but if an alias is in use, then it will change the root of the site. I think that the way to resolve this is to make the bootstrap class selection lazy-evaluate when you're finding the root of the site. Once Drush has determined what sort of site is being used, then only that particular bootstrap class will be usable going forward.

@jenlampton
Copy link

Thanks so much @Kazanir and @greg-1-anderson for working on this over the weekend.

@greg-1-anderson
Copy link
Member

Haven't had a chance to do more in the past couple of days, but will pick it up again soon.

@greg-1-anderson
Copy link
Member

I submitted a related issue to Drush, #1138. This could be helpful iff it becomes necessary to patch Drush; with this PR, you can associate a site-local copy of Drush, which the global Drush will call seamlessly (just as it does for remote commands).

Didn't have time to work on re-working my bootstrap changes this week after all, but will continue with it next week.

@greg-1-anderson greg-1-anderson added this to the Drush7 milestone Jan 31, 2015
@greg-1-anderson
Copy link
Member

It's not guaranteed that we will be able to implement the entirety of what we envision here in time for Drush 7, but I'm tagging this issue with that milestone for visibility. I'd like to at least commit the parts of the bootstrap refactor that we can easily get in, but it might be necessary to use Bootstrap with Drush master instead of 7 Stable. We'll see.

@greg-1-anderson
Copy link
Member

The PR referenced above (#1156) is a big step forward for this issue.

@greg-1-anderson
Copy link
Member

See proposed bootstrap improvements relevant to this issue at #1165.

@pirog
Copy link

pirog commented Feb 23, 2015

So i can get drush running on Backdrop using the new Boot class preflight magix over in #1165. Very cool!

That said and unsurprisingly there are a few additional issues that pop up almost right away when you change preflight to bootstrap something that is "drupal-like".

I think the immediate question has to do with the scoping of drush. Namely, should drush require that other "drupal-like" things implement a compatibility layer... so that you can translate that other CMSs things into known things in D8/D7/Dwhatever. I think the answer to that is probably yes? If that is the case then there are other things that will need to be handled as well. The biggest one is getting drush to recognize that some other CMS has loaded a compatibility layer.

For Backdrop this can happen by loading in Backdrops D7 compat layer in preflight and getting drush_drupal_version to recognize that aka with something like this that isn't horrible code
pirog@5c3099a

This gets a good deal of drush to "just work" with backdrop but does introduce some other issues that will need to be specially handled either with alternate engines or perhaps some drush hook foo. A good example is DRUPAL_CORE_COMPATIBILITY being set to 7.x but Backdrop module versions being set to 1.x which result in drush en module epically failing.

I honestly don't know how i feel about this approach but i wanted to put it out there for people who know way more about drush and backdrop than i do so people can riff on it a bit.

An alternative to this approach would be to define a whole backdrop engine type to handle a lot of this stuff but i feel like that would be a lot of mostly duplicated code and you might as well just fork drush at that point... which might provide some nice philosophical harmony ;)

The current working example of the Backdrop Bootclass and preflight stuffs is over here in the Kalabox2 app example repo
https://github.com/kalabox/kalabox-app-examples/tree/master/backdrop/config/drush

@pirog
Copy link

pirog commented Feb 23, 2015

Some other notes if you run drush on Backdrop with Backdrop loading and using its D7 compat layer.

  1. update_main_prepare will always fail since require_once $core . '/includes/entity.inc'; doesn't exist in Backdrop

@jenlampton
Copy link

For Backdrop, instead of checking DRUPAL_CORE_COMPATIBILITY we should use BACKDROP_CORE_COMPATIBILITY. We'll need an easy place/way to swap those out.

Backdrop also has entity module instead of the D7 core include, so can we require_once on /core/modules/entity/entity.class.inc? It's the same (or nearly the same) in D8 so we should see how drush handles it there.

@pirog
Copy link

pirog commented Feb 23, 2015

Problem is right now drush does its version checking only with DRUPAL_CORE_COMPATIBILITY but i feel like it wouldn't be hard to get the drush_get_drupal_core_compatibility to use other things.

Let me try loading up entity.class.inc as part of the Backdrop drush Boot classes "load drupal7 compat layer" thing and see what drush does.

@greg-1-anderson
Copy link
Member

With Drupal using versions like 7, 8, and Backdrop using versions like 1,2, any function such as drush_get_drupal_core_compatibility() should be viewed as toxic, and not useful to cross-compatibility.

There are three distinct areas that need to be considered:

  1. Drush core
  2. Individual groups of commands (sql-, pm-, user-*, etc.)
  3. Engines

Drush core:

In the case of Drush core, any code that is using functions such as drush_get_drupal_core_compatibility() needs to be moved into the Boot class. It's not useful to use Drush as a guide for how to do things yet, because there's a lot of "if it's 7, do this, if it's 8, do that". This code all needs to change to use proper dispatching. #1165 makes this possible, but more work will be needed to refactor existing code, once we are creating the boot class. I'd like to make DrushBootDrupal7 and DrushBootDrupal8 classes to guide the way here, but until this is available, you'll just have to imagine what it should be like, and make your DrushBootBackdrop class to match.

Command groups:

These need to be considered individually. For example, the sql-* commands are already more-or-less independent of Drupal. As long as they know how to get the database credentials, they are going to work. I have used Drush sql commands to manage Bugzilla instances, just by setting up a Drush alias containing the appropriate credentials. Works great.

Other command groups may be more problematic. If the target platform is completely compatible with the command group (e.g. user-* might be usable with Backdrop, if all of the same Drupal APIs are available), then it can be used as-is. We'll need some mechanism whereby the command subsystem and the Boot class can work together so that Drupal can figure out if the commands should be made available to the user. If a command group does not work, then there needs to be another mechanism where a replacement command group can be dropped in to place.

Engines

Some Drush command groups use "engines" to provide functionality that varies by Drupal version. The pm-* commands are one example. The "generic" code is in pm.drush.inc, but the Drupal-version-specific code appears in an engine file. For these commands, we need to insure that the Engine selection process will return the right engine for the target platform. Code may need to be refactored out of pm-* to make this possible.

If this all becomes too complicated, then forking Drush / making your own tool might be the way to go. This doesn't mean that we can't share code if we go this way; it just means that we'd need to identify code that can be used in multiple contexts (command dispatching? backend invoke / aliases? output engines?), and factor them into separate libraries, published on Packagist and included as needed via Composer.

At this point in time, I'm not sure which strategy is better. There are multiple tools that we should consider here; backdrop, Drupal Console, wp-cli, etc. There are Drush features that I'd like to be able to use regardless of what the target platform is; I think collaboration is possible and beneficial, if we keep working on the different angles.

@pirog
Copy link

pirog commented Feb 23, 2015

This is a very good write up that describes all the issues well. Thanks @greg-1-anderson!

So drush_coer_compat and drush_drupal_version are two things that would def need to be in the Boot class and then abstracted in the rest of drush for Backdrop and other Drupal-like things to work good and proper. I talked to @Kazanir and it looks like he's done some prelim work on this on top of #1165.

Using backdrops drupal7 compatibility layer a lot of command groups will just work straight up but there also are some that need to be made backdrop specific so we will need a way to tell drush when to just "inherit" the D7 (or maybe D8?) command/engine or to use something else.

I think the data that we need at this point are to just determine which core command groups and/or specific commands Backdrop can straight up inherit and what things need to be backdrop specific.

I hacked up Drush over here (https://github.com/pirog/drush/wiki/Using-gross-hacked-stuff-to-get-drush-to-mostly-work-with-backdrop) and added in a quick Boot class for Backdrop. More or less just to test what things would work if we told drush that Backdrop was Drupal 7 and added in a few other basic compat things and a lot of stuff does seem to work but some things like variables and drush dl are going to need their own backdrop things.

Would be great to get an exhaustive list of the differences since that seems key to what approach we should use ie a bigger more abstract Boot class or forking the project

Whatever the differences a lot of them can probably also be closed on the Backdrop side with a more robust D7 compat layer. That said.. backdrop eventually probably doesnt want to ALWAYS have the D7 compat layer on? and even if they did there are some things that seem like they can ONLY be changed on the Drush side.

Anyway, i'm pretty swamped from here till SandCamp but would be down to sprint on this post-Thursday.

@pirog
Copy link

pirog commented Feb 23, 2015

Another thing that could be useful for us to look at and/or analyze down the road:

If you find a diff between D7 and backdrop submit a PR with the hack over here
https://github.com/pirog/drush/tree/backdrush

That way, at the end of the day, we have a nice diff of all the things that are different and HOW they are different... and also a starting place for a fork if we need to go that way.

@weitzman
Copy link
Member

My vision for Drupal related commands and engines (e.g. updatestatus, projects, user commands, ...) is that they move to a separate Composer package. So we'll have two Composer projects - Drush core and Drush Drupal (or something). Drush Drupal is what most people will install, and Drush Drupal will depend on Drush core.

BackDrop will have its own Drush package that probably depends on Drush Drupal and overrides whatever it needs to in order to function.

We'll need a lot of help to accomplish this - a peyote influenced vision statement is a good first step :)

@pirog
Copy link

pirog commented Feb 26, 2015

+1 to that vision!

I think that would be perfect.

I think you may need to bump up to ayahuasca for an appropriate spirit quest for this sort of thing :)

@greg-1-anderson
Copy link
Member

Now that #1242 has been merged, I'd say that this issue is "fixed". There's still a lot to do in terms of making individual commands work, and there are other spots where Drush is still Drupal-specific, but it is now possible to extend the BaseBoot class to bootstrap a non-Drupal CMS.

Continuation in another issue.

@jenlampton
Copy link

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants