This is related to #628262: Study fabric and build similar on top of backend.inc. In addition to command-specific options (as implemented in #671612: drush should support command-specific options), there are more things that we can do with contexts. Drush already has a certain number of pre-defined contexts, used to prioritized options. This proposal allows for additional contexts to be defined (typically in a drush configuration file), and then applied either by name or by situation.
A context is defined in a drushrc.php file like so:
$context['contextname'] = array(
'options' => array('key' => 'value', 'key2' => 'value2'),
'hooks' => 'myhook',
'includes' => 'name.context.inc',
);
'options' are command-line options that are copied to the 'process' context when the context they are defined in is selected. More on 'hooks' and 'includes' later.
The easiest way to invoke a context is by name, e.g. drush --context=contextname .... Contexts can also be invoked contextually via context selectors.
$options['context-selectors'] = array(
array( 'selectors' => array('source-type' => 'dev', 'target-type' => 'live'), 'context' => 'publish'),
);
In this example, if you call drush with drush --source-type=dev --target-type=live ..., this is the same as specifying 'context' => 'publish'. This is interesting because you can use the existing site alias feature to define 'type' => 'dev' in your dev sites and 'type' => 'live' in your live sites, and then the 'publish' context will be applied in any operation where the source is a dev site and the target is a live site.
If the context does not define a 'hooks' entry explicitly, then the context hook will default to the context name. The context hooks are added to the hooks list in drush_invoke, right after the commandfile hooks. So, for example, you could define a hook function drush_publish_post_sql_sync, and it would be called after sql-sync completes whenever the 'publish' context has been applied. Similarly, the 'includes' line can be used to specify a file that will be included (via require-once) when in context, but this is not necessary; hooks can also be defined in other files that are always included, such as a drushrc.php file.
This feature would allow the behavior of drush to be customized in flexible ways just by defining contexts, context selectors and hook functions in a drushrc.php file that is then shared among the members of a development team. While the totality of the operation of named contexts may be somewhat complex, recipes in example.drushrc.php would probably be pretty easy to understand and customize.
The code for this is not too complicated, and nearly complete. I'll post a patch shortly, once it's ready.
| Comment | File | Size | Author |
|---|---|---|---|
| #9 | load_check_with_retry.patch | 3.04 KB | greg.1.anderson |
| #8 | simple_load_check.diff | 1.79 KB | adrian |
| #1 | user-contexts.patch | 11.55 KB | greg.1.anderson |
Comments
Comment #1
greg.1.anderson commentedHere we have drush dispatching on steroids.
To illustrate what can be done with this patch, please consider the following brief walkthrough.
Step 1: Add some hook functions to your drushrc.php file:
Now if you call
drush sql-confyou won't see anything special, but if you calldrush --context=magical sql-conf, you should see both of your hooks fire. Nifty.Step 2: Add some context selectors to your drushrc.php file:
This example shows the two kinds of context selectors. A selector array contains a list of options with required values and a context name, whereas a selector string is the name of a php function somewhere that can do any test it likes to apply contexts. The return code is an array of context names. Now,
drush --xyzzy=1 sql-confordrush --magical=anything sql-confwill fire your hooks.Step 3: Add a context definition.
This perhaps should have been the first step; however, an empty or missing context definition defaults to a simple context that defines no options, and defines a single hook with the same name as the context. If you add the above definition to your drushrc.php file and re-run any of the tests above, you will see that your hooks fire, and in addition, 'verbose' and 'debug' are defined, so you will also see a list of all of your hook opportunities for sql-conf. If you change 'hooks' from 'magical' to 'mystical' and run again, then you will notice that the 'magical' hooks are no longer firing, but there are a number of hooks shown in the output of the hook opportunities code containing 'mystical' in their function names.
To make full use of this with sql-sync and rsync will require a separate patch to make those functions more hook-able. I'd like to do that as a separate step after this basic functionality is reviewed and vetted.
Comment #2
adrian commentedthis reaaaallly feels like overkill to me.
i don't like the idea of requiring people to define functions in their config files. They aren't easily version controlled (different for every person).
There are so many different layers of drushrc configuration files, that you end up in situations where contexts only exist when you are working in specific places. And there's also the possibility of conflicting 'contexts' (it's entirely possible for multiple contexts to be set at the same time).
'Context' is also already used internally to mean something different.
To add a new 'namespace' at the moment, all you need to do is create a $namespace.drush.inc file, and you get the drush invoke stuff for free. But what this seems to be about, is something that has bothered me before too ... namely being able to conditionally include modules.
The kernel of your issue here for me is the ability to go "--enable-$context" , and then have that namespace be used, and to have the ability to alias sets of options to imply others.
You can already do the following in your custom module :
<?php
function drush_mymodule_init($args) {
drush_set_option("option", 1);
drush_set_option("option2", 3);
}
My idea is to add a meta-info file for the drush command files, that are loaded and considered before actually loading the module and adding it to the stack.
Comment #3
greg.1.anderson commentedOkay, I can agree to your technique. The meta-info file needs to be considered twice, though: once before the commandlist is built, so it can include a file that defines commands, and once after the user-specified command is selected, so that additional hook functions can be defined based on the command parameters.
I suppose, though, that since that would be complicated (the later half), it might be better to just settle for:
I can go for that. I'll roll together the first half. Before drush adds a namespace and includes a file, it will look for a similarly-named file in the same folder. If found, that file will be included first, and a well-known function name inside it will be executed. Iff the result of the function is TRUE, then the commandlist processing will happen as usual. Otherwise, that file will be skipped.
If anyone has an opinion on the naming convention, let me know; otherwise I'll pick something and post a patch shortly.
Comment #4
adrian commentedhow about $namespace_drush_status() ? , and have it default to TRUE if not defined.
i was considering making it a .info file with a specific flag you specify (ie:
but that is nowhere near flexible enough. so a hook will have to do.
Comment #5
greg.1.anderson commentedYes, I think that code is better. How about $namespace_drush_load_status(), though, for better alignment with its function? It could be stored in $namespace.load.status.inc.
If you prefer brevity, then I'll stick with $namespace_drush_status() and $namespace.status.inc.
Comment #6
anarcat commentedI'm a bit confused by this issue: are we ditching the original "named contexts" idea? I agree it's very confusing to add another "context" paradigm, and we should avoid reusing that term. Therefore we should probably clarify the issue title.
Yes. This is what was requested in #418208: do not load everything and provide a way to tell drush what to load and that issue is now a duplicate of this one.
I'm not sure what this does for us...
Hum. Another meta-info file? How about just parsing the module's .info file, which should be free to have any field we want?
Comment #7
greg.1.anderson commented@anarcat: You're right, the requirements on this issue are getting confused. There are a bunch of features represented here; let's take a step back and list them:
My patch in #1 provided a mechanism to do everything except 1. and 8.
Adrian did not think it was necessary to be able to dynamically add hooks; he preferred using the existing namespace = commandfile = hook association that drush currently provides, and wants a mechanism to just turn off existing commandfiles before they are loaded (1. and 5.) In #4, I think we agreed that meta-inf files are not flexible enough; therefore code is needed (3. is required, 8. is superfluous).
To answer the question in #6, in #2, Adrian was pointing out that options can be added via code during their init functions, so he felt that it was overkill to allow them to be defined using metadata in a drush configuration file. I kind of like this feature; it is analogous to the command-specific options.
In #3, I gave up on the idea of turning off hooks via command-specific or alias-specified options (feature 4.), since you can also get this behavior just by wrapping every hook in an 'if' statement. However, I think that turning hooks on and off is what this feature is all about, so in the end I think I'd be a little dissatisfied if hooks could be switched during commandfile collection, but not during command processing (when command-specific and alias-defined options are available).
At the moment, I'm thinking of removing 2. and adding 1. to my patch. I really want to keep 4. It is unclear to me whether we need 5.; if we do not, then we don't need an extra .inc file, we can just call a well-known function that is defined in the drush.inc file.
I'm still on the fence about 6.; I might try to keep this feature if I can do it in a simpler way, or at least do it in a way that is not called "contexts". The idea from fabrique is that you can have a simple name (a "context") that defines a set of options and hooks. Doing this easily in metadata is useful, I think.
Defining hooks in the drushrc.php was really just one example usage, one that I thought was easy to understand and describe. It may very well be undesirable, but in any event, nothing in my previous patch or my next patch will prevent or require code in drushrc.php.
I don't think we need 8.
Comment #8
adrian commentedok. here's my look at it.
dead simple.
created 2 files in ~/.drush
~/.drush/mytest.drush.load.inc
and the actual commandfile
obviously this is just an example.
you can just as easily have your _load_func do something like :
This way you aren't stuck with us generating your conditionals from a non expressive array. IE: what if your conditions for enabling the module contains conditions like if this conflicting module is enabled and these flags are set in a certain way, or a certain value is over a certain threshold .. enable the module.
Comment #9
greg.1.anderson commentedOkay, I like that quite a bit. There's only one limitation with it. If there is an option that is defined in a drush configuration file that is stored in a Drupal site folder, then that option will not be defined until DRUSH_BOOTSTRAP_DRUPAL_SITE, but at this point you will have already missed all of the modules that had their load_check run during an earlier phase.
Here is a oh-so-slightly-more complicated version that retries the load test on every phase. If this satisfies the things you need, I'll open a new issue to add the code to allow this all to work in the case of
drush sql-sync mydev mylive, which is requirement 4 above. n.b. the options defined in 'mydev' and 'mylive' are currently not applied until after the sql-sync command is invoked (e.g. 'type' => 'dev' and 'type' => 'live', defined in 'mydev' and 'mylive', respectively, will define 'source-type' and 'target-type' as 'dev' and 'live', respectively, after sql-sync evaluates its parameters).Comment #10
moshe weitzman commentedadrian to review.
Comment #11
anarcat commentedMarking this for 3.0.
Comment #12
adrian commentedI've committed this patch and confirmed your new logic.
awesome. finally.
Comment #13
moshe weitzman commented#9 talked about a todo after the patch. greg/adrian should close this if no follow-up needed.
Comment #14
greg.1.anderson commentedI'm glad that #9 works well for everyone and is committed. I guess I'll leave this open for follow-on patches, although I'm not quite ready to do them yet.
Comment #15
greg.1.anderson commentedRemoving the "drush-3.0" tag, as follow-on changes are not necessary for 3.0-stable.
Comment #16
greg.1.anderson commentedI'm just going to mark this "fixed". The important part went in with #12. I don't want named contexts any more. An alias already functions in part like a named context. There are a couple of things that are, perhaps, missing: having --option1 and --option2 imply (add) an --option3 when used together, and the ability for an option to "turn on" a hook dynamically. If we can do the later, then the former can be accomplished in code per #2, and per #8, I'm not sure we need dynamic hooks. If I change my mind later, I'll open a new issue about dynamic hooks.
Also, renaming so that the title matches what was committed.
Comment #17
greg.1.anderson commented