I spent some time this afternoon working on making drush_shutdown() more intelligent, and realized that to do it properly we really needed to complete Adrian's vision of commands being tied to Drupal bootstrap levels (in the same way they are tied to Drupal core and module dependencies). Apart from making things more flexible, but allowing things to be run in more limited environments this also allows us to properly list commands that are available to you in your current environment.

A summary of the changes in this patch:
* Instead of lots of back and forth guessing game type logic to figure out what commands are available, we simply try and bootstrap Drupal to the maximum bootstrap level possible.
* Once we know what bootstrap level we are at, it is much simpler to filter the list to show what commands are available.
* We avoid the exits in the core bootstrap code by using a shutdown function - this is now in the normal bootstrap execution path, rather than coming just at the end of the request. This has the advantage that we can recover from pretty much anything (even module syntax errors) and still produce a usable tool. We use a similar output buffering technique to avoid the maintenance page (hopefully we can patch the Drupal 7 bootstrap to not die or forcefully redirect and to provide useful success/failure output, so we can simplify this in the future).
* The previous errors when you don't have a full bootstrap are now more just hints on how to enable more commands (and now appear at the top). I think generally having things degrade gracefully in this way (rather than lock you out completely until you get all 12 axis of potential errors configured just right) is the way to go to keep the user experience good for new users.

I am also attaching a patch to Drush extras on 6.x that adds bootstrap parameters to many of the commands. The $bootstrap_level global should dovetail nicely into Adrian's context API.

I have tested this a fair bunch, but I would appreciate it if someone could give it a run though, especially checking some of the more subtle error conditions. You can hack the if (is_null($bootstrap)) {... $bootstrap = DRUPAL_BOOTSTRAP_FULL; ...} line to force arbitrary bootstrap levels, as well as breaking your database uri in settings.php (forces level 1) or your Drupal site code (forces level 2).

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

moshe weitzman’s picture

I'd love Adrian's patch review on this. he is the one who thought that some commands need control the bootstrap level (presumably installer and updater)

adrian’s picture

I'll review this on the plane tomorrow, but i need to review it in context of the .. err context patch.

Which needs to go in first ?

Personally, in my commands, i need to be able to delay bootstrapping, till the configurations are created correctly.
This involves moving the DRUSH_URI definition and drush_set_environment code (which correctly sets the headers), to a later point in execution (if bootstrap is not set).

ie: when you do 'drush.php install somesite.com --root=/some/drupal/path' , it needs to not bootstrap, until after i create the drupal config and the mysql database, and then bootstrap the additional levels.

Owen Barton’s picture

Which needs to go in first ?

I don't think it matters hugely - there is not a very large intersection - this touches some of the lines the context patch changes, but it mostly just moves them around rather than actually changing them. This patch changes "what we do" (rather than "how we do it" as with the context patch) so from that point of view it might be worth putting in first, so we get the disruption out of the way.

Personally, in my commands, i need to be able to delay bootstrapping, till the configurations are created correctly.
This involves moving the DRUSH_URI definition and drush_set_environment code (which correctly sets the headers), to a later point in execution (if bootstrap is not set).

Yes, and you can still do this in at least 3 ways:
1: You can set your command to run at bootstrap level -1 and it will not attempt any bootstrap (probably your best bet in your case).
2: If there is no specified/detected Drupal root then it will also not attempt bootstrap (doesn't apply in your case, since you want this).
3: You can set a bootstrap level and allow it to attempt (and fail), which will leave you at a -1 bootstrap level. This could be useful in some cases if you think there may be a site there, but you need to confirm it's status (e.g. to fix it).

Either way, once you are in you command callback, you can check the $bootstrap_level and if it is -1 you can do your installation stuff, then re-call drush_drupal_bootstrap. This will attempt the bootstrap again and then deposit execution back at the start of your command callback (or another command callback if you switched the command, which should be possible with the context patch). You can then check the bootstrap level again to see if it worked and if so, complete any post installation tasks. If you use the same command you should probably implement a $first_run static or something, so it doesn't get stuck in an infinite installation loop if it all borks and returns -1 on the second bootstrap attempt too.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

Sounds like Install/Update needs are met, so feel free to proceed with commit of this and with the error context patch. I didn't get to test it but I'm sure we can iron out problems afterwards.

Owen Barton’s picture

Status: Reviewed & tested by the community » Needs work

I had a play with this and it seems to be doing something very odd to the system_module forms in the new enable/disable command (it appears that the form already reflects the changes we are about to make, before we even started applying the...very odd).

Anyway, for the time being I committed a very minimal patch to remove the dummy errors while we figure out what is going on here.

Owen Barton’s picture

Assigned: Unassigned » Owen Barton

I am going to take a shot at getting this and #385898 playing nicely together on the plane tomorrow.

Owen Barton’s picture

Status: Needs work » Fixed

Figured out the wierd issue above (we needed to do an "exit" rather than call the drush_bootstrap_continue function directly, so that the function is removed from the shutdown callback list). Committed the patch to HEAD.

A good followup patch would be to add some help to let you know if/why some commands are not listed, and provide a simple way to let you list all of them (rather then just the runnable ones) - probably just an explicit "drush help".

anarcat’s picture

Status: Fixed » Needs work

I should note that this commit breaks provision, I don't exactly understand why. From what I understand, now drush bootstraps as much as it can now, which will break provision since it sometimes needs to do some things (like creating the settings.php) *before* bootstrapping. In short it needs much better control over the bootstrap workflow than what's available here.

In fact, I think there's been a conceptual confusion about the 'bootstrap' flag in drush command declarations. This patch assumes it's "minimal bootstrap level necessary to run the command" whereas I think it should mean "do not bootstrap beyond that level". Maybe we need both?

Not sure where to go from here now. I'll test my luck at putting this back to 'needs work' since this was done for provision after all...

Owen Barton’s picture

@anarcat - this was indeed the original intention, but seemed to have got lost along the way - it should be easy enough to add though - I am happy to have a shot this weekend if you don't get to it first (just after $bootstrap_level = -1; in drush_bootstrap make a call to fetch all commands and pull the bootstrap level out of one, if provided).

An alternative approach is to let it fail to bootstrap, do your settings.php stuff and then recall drush_drupal_bootstrap() with your next bootstrap level. In some ways this is more flexible, because you can check that the site is not already existing and functional and so on. We do something like this in drush dl, with an "optional" conf_path level bootstrap to allow more magic in the download location logic.

Owen Barton’s picture

Here is a quick sketch of what I am suggesting. I am not sure $check_bootstrap is the best level of abstraction (perhaps command list altering should be a hook?) but it's a start.

Todos (not necessarily all in this issue):
* Provide a help function to list all commands (rather than just the "available" commands).
* Port the patch to add bootstrap levels to drush_extras.
* Fix up the drush_set_error(DRUSH_DRUPAL_DB_ERROR); help to display in more appropriate places.
* Perhaps provide some generic constants and help somewhere of at least the most useful bootstrap levels.

Owen Barton’s picture

Status: Needs work » Needs review
FileSize
2.76 KB

Oh...and the patch...

adrian’s picture

Status: Needs review » Needs work

This was committed, and broke all the provision commands. http://drupal.org/cvs?commit=182490

We need the bootstrap to set the maximum level to boot to, and not the minimum required level to boot to.

http://drupal.org/node/400034

I also think it's wrong that all the application logic is in the shutdown code AND the documentation is also incorrect.

I am reverting this patch , and will look at it more closely on monday. http://drupal.org/cvs?commit=182904

Macronomicus’s picture

I had to go back to 1.44 in order to fix this on my build.
1.48 killed my queue dispatcher --- 1.47 or #387214 killed my provision
Everythings happy again @ 1.44
^_^

moshe weitzman’s picture

Would be great if Owen and Adrian could get this sorted quickly. We really need to release Drush 2 ASAP. Our current situation is confusing for many potential drush users. I removed all releases from the project page except HEAD because I have no intention of supporting them.

adrian’s picture

Priority: Normal » Critical

Now that context is in, I'm busy working on a new version of this patch.

It's essentially modeled after how Drupal does it's bootstrapping, with the addition of some drush specific levels.

/**
 * Sequential Drush bootstrapping levels
 */
define('DRUSH_BOOTSTRAP_NONE', 0);
define('DRUSH_BOOTSTRAP_PLATFORM' 1);
define('DRUSH_BOOTSTRAP_SITE', 2);
define('DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION', 3);
define('DRUSH_BOOTSTRAP_DRUPAL_DATABASE', 4);
define('DRUSH_BOOTSTRAP_DRUPAL_FULL', 5);

This will allow any command to specify the level to bootstrap to, and allow the help command to sequentially boot to new levels.
This will also allow us to cleanly tie the error handling into the current bootstrap level, and reliably detect more errors (such as not having a proper drupal root, not having a proper site directory, the 'site not found' error)

I'm still trying to determine what the default bootstrap level is going to be.

adrian’s picture

So here's a weird little issue that I see cropping up.

So essentially when dispatching the command, it will start at bootstrap level 0, find the relevant commandfiles, boot to bootstrap level 1, do an additive find, etc.

However, when it finds a command with a lower bootstrap level, let's say for example 'status' (this is not an actual case, just a pertinent example), it should cease trying to find a command with a higher bootstrap level, and dispatch the command.

However, if you then add a command called 'status modules', which requires a higher bootstrap level than the 'status' command which is found and evaluated first, it will never reach that command.

I can't think of a way around this, and I think it's actually the way it's supposed to work. So it should be documented at least.

moshe weitzman’s picture

My first inclination is that we can just document this somewhere and accept the limitation.

adrian’s picture

I've been doing some more work on this patch, and i've found a pretty solid approach.

What i've been doing is taking the drush_bootstrap as it is now, and moving the code into their own functions.
This means the code that is used in drush_bootstrap to set the constants is now callable up to a specific level.

The levels are :


/**
 * @name Drush bootstrap phases
 * @{
 * Sequential Drush bootstrapping phases.
 */

/**
 * Only bootstrap Drush, without any Drupal specific code.
 *
 * Any code that operates on the Drush installation, and not specifically
 * any Drupal directory, should bootstrap to this level.
 */
define('DRUSH_BOOTSTRAP_DRUSH', 0);

/**
 * Set up and test for a valid drupal root, either through the -r/--root options,
 * or evaluated based on the current working directory.
 *
 * Any code that interacts with an entire Drupal installation, and not a specific
 * site on the Drupal installation should use this bootstrap phase.
 */
define('DRUSH_BOOTSTRAP_DRUPAL_ROOT' 1);

/**
 * Set up a Drupal site directory and the correct environment variables to
 * allow Drupal to find the configuration file.
 *
 * If no site is specified with the -u / --uri options, Drush will assume the
 * site is 'default', which mimics Drupal's behaviour.
 *
 * If you want to avoid this behaviour, it is recommended that you use the
 * DRUSH_BOOTSTRAP_DRUPAL_ROOT bootstrap phase instead.
 *
 * Any code that needs to modify or interact with a specific Drupal site's
 * settings.php file should bootstrap to this phase.
 */
define('DRUSH_BOOTSTRAP_DRUPAL_SITE', 2);

/**
 * Load the settings from the Drupal sites directory.
 *
 * This phase is analagous to the DRUPAL_BOOTSTRAP_CONFIGURATION bootstrap phase in Drupal
 * itself, and this is also the first step where Drupal specific code is included.
 *
 * This phase is commonly used for code that interacts with the Drupal install API,
 * as both install.php and update.php start at this phase.
 */
define('DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION', 3);

/**
 * Connect to the Drupal database using the database credentials loaded
 * during the previous bootstrap phase.
 *
 * This phase is analogous to the DRUPAL_BOOTSTRAP_DATABASE bootstrap phase in
 * Drupal.
 *
 * Any code that needs to interact with the Drupal database API needs to
 * be bootstrapped to at least this phase.
 */
define('DRUSH_BOOTSTRAP_DRUPAL_DATABASE', 4);

/**
 * Fully initialize Drupal.
 *
 * This is the default bootstrap phase all commands will try to reach,
 * unless otherwise specified.
 * This is analogous to the DRUPAL_BOOTSTRAP_FULL bootstrap phase in
 * Drupal.
 *
 * Any code that interacts with the general Drupal API should be
 * bootstrapped to this phase.
 */
define('DRUSH_BOOTSTRAP_DRUPAL_FULL', 5);

/**
 * @} End of Drush bootstrap phases.
 */

Any command that needs to bootstrap to a lower phase than full, will be able to further bootstrap at it's own convenience using drush_bootstrap(DRUSH_BOOTSTRAP_WHATEVER).

When drush is initialized now, it will sequentially try to bootstrap to the drupal_full level, and test whether there is a command that
should be executed at the current bootstrap level, if there is it will hand over control of bootstrapping to the command using drush_dispatch.

adrian’s picture

Status: Needs work » Needs review
FileSize
49.63 KB

Here's the patch.

I tested this with hosting/ provision (where it's mostly working .. but i think the issues that remain are on my side), and on drush_extras 5.x (which i didn't modify at all).

I added another bootstrap level to require a valid login to the site.

adrian’s picture

FileSize
51.02 KB

here's a new version of the patch, with more logging (only visible with --verbose)

moshe weitzman’s picture

I reviewed this, and am comfortable with committing this once the dl command is working properly. Nice work, Adrian.

adrian’s picture

FileSize
56.02 KB

Here's my latest version of the patch, fully tested with hosting and provision (the companion patches are here : http://drupal.org/node/411822)

I'd like to get this committed ASAP, so i can have a solid base to make smaller patches on. I'm maintaining over 100k's worth of patches across 3 projects for this stuff.

Any further issues we find we can create new, simpler issues for.

moshe weitzman’s picture

Status: Needs review » Fixed

I fixed up dl command and committed this. Thanks Adrian.

anarcat’s picture

Status: Fixed » Needs review
FileSize
435 bytes

A typo went in at some point, here's a trivial fix.

anarcat’s picture

Status: Needs review » Fixed

Sorry, wrong issue.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.